Skip to content

Latest commit

 

History

History
872 lines (723 loc) · 23.6 KB

File metadata and controls

872 lines (723 loc) · 23.6 KB

MCP Tools Reference

CocoSearch provides 10 Model Context Protocol (MCP) tools for semantic code search, dependency analysis, and index management. These tools enable AI agents and LLMs to search indexed codebases, trace dependencies, manage indexes, analyze search pipelines, and retrieve statistics programmatically.

Available transports: stdio, SSE, streamable HTTP


search_code

Search indexed code using natural language queries. Returns code chunks ranked by semantic similarity, with optional context expansion to enclosing function/class boundaries. Performs automatic project detection using MCP Roots when available, falling back to the index_name parameter or the working directory.

Parameters

Parameter Type Required Default Description
query string Yes - Natural language search query
index_name string | null No null Name of the index to search. If not provided, auto-detects from current working directory.
limit integer No 10 Maximum results to return
language string | null No null Filter by language (e.g., python, typescript, hcl, dockerfile, bash). Aliases: terraform=hcl, shell/sh=bash. Comma-separated for multiple.
use_hybrid_search boolean | null No null Enable hybrid search (vector + keyword matching). None=auto (enabled for identifier patterns like camelCase/snake_case), True=always use hybrid, False=vector-only
symbol_type string | array<string> | null No null Filter by symbol type. Single: 'function', 'class', 'method', 'interface'. Array: ['function', 'method'] for OR filtering.
symbol_name string | null No null Filter by symbol name pattern (glob). Examples: 'get*', 'User*Service', '*Handler'. Case-insensitive matching.
context_before integer | null No null Number of lines to show before each match. Overrides smart context expansion when specified.
context_after integer | null No null Number of lines to show after each match. Overrides smart context expansion when specified.
smart_context boolean No true Expand context to enclosing function/class boundaries. Enabled by default. Set to False for exact line counts only.
include_deps boolean No true Include dependency info (imports/dependents) for each result file
index_names list<string> | null No null Search across multiple indexes. Results merged by relevance score. Mutually exclusive with index_name.

Natural Language Example

Search for authentication logic in a Python project:

"Find JWT token validation functions"

This will search the auto-detected index for code chunks related to JWT token validation, automatically using hybrid search since the query contains the identifier pattern "JWT".

JSON Request

{
  "query": "JWT token validation",
  "index_name": "my-api-server",
  "limit": 5,
  "language": "python",
  "use_hybrid_search": true,
  "symbol_type": "function",
  "context_before": 3,
  "context_after": 3,
  "smart_context": false
}

JSON Response

[
  {
    "file_path": "/Users/dev/my-api/auth/jwt.py",
    "start_line": 45,
    "end_line": 62,
    "score": 0.89,
    "content": "def validate_jwt_token(token: str) -> dict:\n    \"\"\"Validate JWT and return claims.\"\"\"\n    try:\n        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])\n        return payload\n    except jwt.ExpiredSignatureError:\n        raise AuthError('Token expired')\n    except jwt.InvalidTokenError:\n        raise AuthError('Invalid token')",
    "block_type": "function",
    "hierarchy": "validate_jwt_token",
    "language_id": "python",
    "symbol_type": "function",
    "symbol_name": "validate_jwt_token",
    "symbol_signature": "def validate_jwt_token(token: str) -> dict",
    "match_type": "both",
    "vector_score": 0.87,
    "keyword_score": 0.91,
    "context_before": "import jwt\nfrom .exceptions import AuthError\n\n",
    "context_after": "\n\ndef refresh_token(user_id: int) -> str:\n    return generate_jwt(user_id)"
  }
]

Note: Response may include a search_context header (when auto-detecting index) and a staleness_warning footer (when index is older than 7 days). When using index_names for cross-index search, each result includes an index_name field identifying which index it came from.

Cross-Index Search

Search across multiple indexes in a single call using the index_names parameter. This is useful for searching related projects together — monorepos, shared libraries, or microservice codebases.

How it works:

  1. The query embedding is computed once and reused across all indexes
  2. Each index is searched in parallel via ThreadPoolExecutor
  3. Results from all indexes are merged by score and truncated to the requested limit
  4. Each result is tagged with its source index_name
  5. Partial failures are handled gracefully — if one index fails, results from other indexes are still returned

linkedIndexes auto-expansion: When cocosearch.yaml includes a linkedIndexes list, single-index searches automatically expand to include the linked indexes — no explicit index_names parameter needed. Explicitly providing index_names overrides the config. Missing linked indexes are silently skipped.

Example config:

indexName: my-api
linkedIndexes:
  - shared-libs
  - common-types

With this config, searching my-api automatically includes shared-libs and common-types.

JSON Request (explicit cross-index):

{
  "query": "authentication middleware",
  "index_names": ["api-server", "shared-libs", "auth-service"],
  "limit": 10,
  "use_hybrid_search": true,
  "smart_context": true
}

Note: All indexes must use the same embedding provider and model. Mismatched embedding models across indexes will produce unreliable relevance scores.


analyze_query

Analyze the search pipeline for a query with stage-by-stage diagnostics. Runs the same pipeline as search_code but captures diagnostics at each stage: query analysis, mode selection, cache status, vector search, keyword search, RRF fusion, definition boost, filtering, and per-stage timing breakdown.

Use this to understand WHY a query returns specific results.

Parameters

Parameter Type Required Default Description
query string Yes - Search query to analyze
index_name string | null No null Name of the index. Auto-detects if not provided.
limit integer No 10 Maximum results to return
language string | null No null Filter by language. Comma-separated for multiple.
use_hybrid_search boolean | null No null None=auto, True=always hybrid, False=vector-only
symbol_type string | array<string> | null No null Filter by symbol type
symbol_name string | null No null Filter by symbol name pattern (glob)

Natural Language Example

"Why does searching for getUserById not return the right function?"

JSON Request

{
  "query": "getUserById",
  "index_name": "my-api-server"
}

JSON Response

{
  "query_analysis": {
    "original_query": "getUserById",
    "has_identifier": true,
    "normalized_keyword_query": "getUserById get User By Id"
  },
  "search_mode": {
    "mode": "hybrid",
    "reason": "Auto-detected identifier pattern in query",
    "use_hybrid_flag": null,
    "has_content_text_column": true,
    "has_identifier_pattern": true
  },
  "cache": {
    "checked": false,
    "hit": false,
    "hit_type": "miss",
    "cache_key_prefix": "a1b2c3d4e5f67890"
  },
  "vector_search": {
    "result_count": 12,
    "top_score": 0.872,
    "bottom_score": 0.534
  },
  "keyword_search": {
    "executed": true,
    "normalized_query": "getUserById get User By Id",
    "result_count": 8,
    "top_ts_rank": 0.098
  },
  "fusion": {
    "executed": true,
    "k_constant": 60,
    "vector_only_count": 8,
    "keyword_only_count": 4,
    "both_count": 4,
    "total_fused": 16
  },
  "definition_boost": {
    "executed": true,
    "boost_multiplier": 2.0,
    "boosted_count": 3,
    "rank_changes": 1
  },
  "filtering": {
    "language_filter": null,
    "symbol_type_filter": null,
    "symbol_name_filter": null,
    "min_score": 0.0,
    "pre_filter_count": 10,
    "post_filter_count": 10
  },
  "timings": {
    "query_analysis_ms": 0.1,
    "cache_check_ms": 0.0,
    "embedding_ms": 0.0,
    "vector_search_ms": 12.3,
    "keyword_search_ms": 2.1,
    "rrf_fusion_ms": 0.3,
    "definition_boost_ms": 0.1,
    "total_ms": 15.2
  },
  "results": []
}

Note: The results array contains full SearchResult objects (same format as search_code). Cache is always bypassed for analysis.


list_indexes

List all available code indexes. Returns index names and their corresponding table names.

Parameters

None

Natural Language Example

Get all indexed codebases to see what's available for searching.

JSON Request

{}

JSON Response

[
  {
    "name": "my-api-server",
    "table_name": "cocosearch_my_api_server"
  },
  {
    "name": "frontend-app",
    "table_name": "cocosearch_frontend_app"
  },
  {
    "name": "shared-utils",
    "table_name": "cocosearch_shared_utils"
  }
]

index_stats

Get statistics for code indexes. Returns file count, chunk count, storage size, language distribution, symbol counts, parse health, and staleness information.

Parameters

Parameter Type Required Default Description
index_name string | null No null Name of the index (omit for all indexes)
include_failures boolean No false Include per-language parse failure details in response

Natural Language Example

Check how many files and chunks are indexed in "my-api-server" and when it was last updated.

JSON Request (Single Index)

{
  "index_name": "my-api-server"
}

JSON Response (Single Index)

{
  "name": "my-api-server",
  "file_count": 342,
  "chunk_count": 1523,
  "storage_size": 8421376,
  "storage_size_pretty": "8.0 MB",
  "created_at": "2026-01-15T10:30:00Z",
  "updated_at": "2026-02-05T14:22:00Z",
  "is_stale": false,
  "staleness_days": 1,
  "languages": [
    {
      "language": "python",
      "file_count": 180,
      "chunk_count": 890,
      "line_count": 12500
    },
    {
      "language": "typescript",
      "file_count": 120,
      "chunk_count": 520,
      "line_count": 8200
    },
    {
      "language": "hcl",
      "file_count": 42,
      "chunk_count": 113,
      "line_count": 1800
    }
  ],
  "symbols": {
    "function": 450,
    "class": 85,
    "method": 320,
    "interface": 25
  },
  "parse_stats": {
    "parse_health_pct": 95.2,
    "total_files": 342,
    "total_ok": 325,
    "by_language": {
      "python": {
        "files": 180,
        "ok": 175,
        "partial": 3,
        "error": 2,
        "no_grammar": 0
      },
      "typescript": {
        "files": 120,
        "ok": 115,
        "partial": 3,
        "error": 2,
        "no_grammar": 0
      }
    }
  },
  "warnings": []
}

When include_failures is true, the response includes a parse_failures array with file paths and error details for each failed parse:

{
  "parse_failures": [
    {
      "file_path": "src/legacy/parser.py",
      "language": "python",
      "parse_status": "error",
      "error_message": "tree-sitter parse failed"
    }
  ]
}

JSON Request (All Indexes)

{}

JSON Response (All Indexes)

[
  {
    "name": "my-api-server",
    "file_count": 342,
    "chunk_count": 1523,
    "storage_size": 8421376,
    "storage_size_pretty": "8.0 MB",
    "created_at": "2026-01-15T10:30:00Z",
    "updated_at": "2026-02-05T14:22:00Z",
    "is_stale": false,
    "staleness_days": 1,
    "languages": [
      {
        "language": "python",
        "file_count": 180,
        "chunk_count": 890,
        "line_count": 12500
      }
    ],
    "symbols": {
      "function": 450,
      "class": 85
    },
    "parse_stats": {
      "parse_health_pct": 97.8,
      "total_files": 180,
      "total_ok": 176,
      "by_language": {}
    },
    "warnings": []
  },
  {
    "name": "frontend-app",
    "file_count": 215,
    "chunk_count": 980,
    "storage_size": 5242880,
    "storage_size_pretty": "5.0 MB",
    "created_at": "2026-01-20T08:15:00Z",
    "updated_at": "2026-01-28T16:45:00Z",
    "is_stale": true,
    "staleness_days": 9,
    "languages": [
      {
        "language": "typescript",
        "file_count": 200,
        "chunk_count": 920,
        "line_count": 15200
      }
    ],
    "symbols": {
      "function": 320,
      "interface": 45
    },
    "parse_stats": {
      "parse_health_pct": 99.0,
      "total_files": 200,
      "total_ok": 198,
      "by_language": {}
    },
    "warnings": [
      "Index is stale (9 days since last update)"
    ]
  }
]

Note: The line_count field is null for indexes created before v1.7 (lacking the content_text column). The symbols field is an empty object {} for pre-v1.7 indexes.


clear_index

Clear (delete) a code index. Permanently deletes all indexed data for a codebase, including the associated parse results tracking table. This operation cannot be undone.

WARNING: This is a destructive operation.

Parameters

Parameter Type Required Default Description
index_name string Yes - Name of the index to delete

Natural Language Example

Delete the "old-prototype" index that's no longer needed.

JSON Request

{
  "index_name": "old-prototype"
}

JSON Response (Success)

{
  "success": true,
  "message": "Index 'old-prototype' cleared successfully"
}

JSON Response (Error)

{
  "success": false,
  "error": "Index 'nonexistent' not found"
}

index_codebase

Index a codebase directory for semantic search. Creates embeddings for all code files and stores them in the database. If the index already exists, it will be updated with any changes.

Parameters

Parameter Type Required Default Description
path string Yes - Path to the codebase directory to index
index_name string | null No null Name for the index (auto-derived from path if not provided)

Natural Language Example

Index the codebase at "/Users/dev/my-new-project" so it can be searched.

JSON Request

{
  "path": "/Users/dev/my-new-project",
  "index_name": "my-new-project"
}

JSON Response (Success)

{
  "success": true,
  "index_name": "my-new-project",
  "path": "/Users/dev/my-new-project",
  "stats": {
    "files_added": 150,
    "files_removed": 0,
    "files_updated": 0
  }
}

JSON Response (Error)

{
  "success": false,
  "error": "Failed to index codebase: Path does not exist: /invalid/path"
}

Note: If index_name is not provided, it will be auto-derived from the path (e.g., "/Users/dev/my-api" becomes "my-api").


get_file_dependencies

Get dependencies for a file (what it depends on). Returns structured dependency data with transitive traversal. With depth=1, returns direct dependencies as a flat list. With depth>1, returns a transitive dependency tree showing the full chain of dependencies.

Parameters

Parameter Type Required Default Description
file string Yes - File path relative to project root
index_name string | null No null Index name. Auto-detects from project if not provided.
depth integer No 1 Traversal depth. 1=direct only, >1=transitive
dep_type string | null No null Filter by type: import, call, reference

JSON Request

{
  "file": "src/auth/jwt.py",
  "depth": 2,
  "dep_type": "import"
}

JSON Response (depth=1)

{
  "file": "src/auth/jwt.py",
  "dependencies": [
    {
      "target_file": "src/config/settings.py",
      "target_symbol": "SECRET_KEY",
      "dep_type": "import",
      "module": "config.settings"
    },
    {
      "target_file": null,
      "target_symbol": "jwt",
      "dep_type": "import",
      "module": "jwt"
    }
  ],
  "total": 2
}

JSON Response (depth>1)

{
  "file": "src/auth/jwt.py",
  "tree": {
    "file": "src/auth/jwt.py",
    "symbol": null,
    "dep_type": "root",
    "children": [
      {
        "file": "src/config/settings.py",
        "symbol": "SECRET_KEY",
        "dep_type": "import",
        "children": []
      }
    ]
  }
}

get_file_impact

Get impact analysis for a file (what would be affected if it changes). Returns a transitive reverse-dependency tree showing files that depend on the given file, useful for understanding blast radius of changes.

Parameters

Parameter Type Required Default Description
file string Yes - File path relative to project root
index_name string | null No null Index name. Auto-detects from project if not provided.
depth integer No 3 Traversal depth for transitive impact analysis (max 20)
dep_type string | null No null Filter by type: import, call, reference

JSON Request

{
  "file": "src/models/user.py",
  "depth": 2
}

JSON Response

{
  "file": "src/models/user.py",
  "impact_tree": {
    "file": "src/models/user.py",
    "symbol": null,
    "dep_type": "root",
    "children": [
      {
        "file": "src/api/users.py",
        "symbol": null,
        "dep_type": "import",
        "children": [
          {
            "file": "src/api/admin.py",
            "symbol": null,
            "dep_type": "import",
            "children": []
          }
        ]
      },
      {
        "file": "tests/test_user.py",
        "symbol": null,
        "dep_type": "import",
        "children": []
      }
    ]
  }
}

get_batch_dependencies

Get dependencies for multiple files in a single batch call. More efficient than calling get_file_dependencies per file when analyzing multiple changed files (e.g., from a git diff). With depth>1, uses a shared visited set across all files to eliminate redundant traversal of overlapping dependency subgraphs.

Parameters

Parameter Type Required Default Description
files array<string> Yes - File paths relative to project root
index_name string | null No null Index name. Auto-detects from project if not provided.
depth integer No 1 Traversal depth. 1=direct only, >1=transitive with shared visited set
dep_type string | null No null Filter by type: import, call, reference

JSON Request

{
  "files": ["src/auth/jwt.py", "src/auth/oauth.py"],
  "depth": 2
}

JSON Response (depth=1)

{
  "files_requested": 2,
  "depth": 1,
  "results": [
    {
      "file": "src/auth/jwt.py",
      "dependencies": [
        {
          "target_file": "src/config/settings.py",
          "target_symbol": "SECRET_KEY",
          "dep_type": "import",
          "module": "config.settings"
        }
      ],
      "total": 1
    },
    {
      "file": "src/auth/oauth.py",
      "dependencies": [
        {
          "target_file": "src/config/settings.py",
          "target_symbol": "OAUTH_CLIENT_ID",
          "dep_type": "import",
          "module": "config.settings"
        }
      ],
      "total": 1
    }
  ]
}

JSON Response (depth>1)

{
  "files_requested": 2,
  "depth": 2,
  "results": [
    {
      "file": "src/auth/jwt.py",
      "tree": {
        "file": "src/auth/jwt.py",
        "symbol": null,
        "dep_type": "root",
        "children": [
          {
            "file": "src/config/settings.py",
            "symbol": "SECRET_KEY",
            "dep_type": "import",
            "children": []
          }
        ]
      }
    },
    {
      "file": "src/auth/oauth.py",
      "tree": {
        "file": "src/auth/oauth.py",
        "symbol": null,
        "dep_type": "root",
        "children": []
      }
    }
  ]
}

Note: With depth>1, src/config/settings.py appears only in the first tree's children — the shared visited set prevents redundant traversal.


get_batch_impact

Get impact analysis for multiple files in a single batch call. More efficient than calling get_file_impact per file when analyzing multiple changed files (e.g., from a git diff). Uses a shared visited set across all files to eliminate redundant traversal of overlapping reverse-dependency subgraphs.

Parameters

Parameter Type Required Default Description
files array<string> Yes - File paths relative to project root
index_name string | null No null Index name. Auto-detects from project if not provided.
depth integer No 3 Traversal depth for transitive impact analysis (max 20)
dep_type string | null No null Filter by type: import, call, reference

JSON Request

{
  "files": ["src/models/user.py", "src/models/order.py"],
  "depth": 2
}

JSON Response

{
  "files_requested": 2,
  "depth": 2,
  "results": [
    {
      "file": "src/models/user.py",
      "impact_tree": {
        "file": "src/models/user.py",
        "symbol": null,
        "dep_type": "root",
        "children": [
          {
            "file": "src/api/users.py",
            "symbol": null,
            "dep_type": "import",
            "children": []
          }
        ]
      }
    },
    {
      "file": "src/models/order.py",
      "impact_tree": {
        "file": "src/models/order.py",
        "symbol": null,
        "dep_type": "root",
        "children": [
          {
            "file": "src/api/orders.py",
            "symbol": null,
            "dep_type": "import",
            "children": []
          }
        ]
      }
    }
  ]
}

Note: The shared visited set means if src/api/users.py depends on both user.py and order.py, it will only appear in the first tree where it's encountered.

Staleness Warnings (all dependency tools)

All four dependency tools (get_file_dependencies, get_file_impact, get_batch_dependencies, get_batch_impact) perform best-effort staleness checks and may include a "warnings" key in the response when dependency data is outdated. Warning types:

Type Meaning
deps_not_extracted No dependency data exists, or extraction timestamp is missing. Run cocosearch deps extract .
deps_outdated The index was re-indexed after the last dependency extraction. Run cocosearch deps extract .
deps_branch_drift Git branch or commit has changed since the index was last built. Run cocosearch index . --deps

Example response with warnings:

{
  "file": "src/auth/jwt.py",
  "dependencies": [],
  "total": 0,
  "warnings": [
    {
      "type": "deps_not_extracted",
      "warning": "Dependencies not extracted",
      "message": "No dependency data found for this index. Run `cocosearch deps extract .` to extract dependencies."
    }
  ]
}

The "warnings" key is omitted when dependency data is fresh.


Implementation

All tools are implemented in src/cocosearch/mcp/server.py using the FastMCP framework.

Core search engine: src/cocosearch/search/query.py Dependency queries: src/cocosearch/deps/query.py Index management: src/cocosearch/management/__init__.py Statistics: src/cocosearch/management/stats.py Parse tracking: src/cocosearch/indexer/parse_tracking.py