Skip to content

Latest commit

 

History

History
227 lines (184 loc) · 9.25 KB

File metadata and controls

227 lines (184 loc) · 9.25 KB

Tool Contracts

The context-mcp crate exposes agent-facing tool contracts as Rust functions and through a minimal MCP stdio server.

Start the server:

cargo run -p context-cli -- mcp-stdio

Core Tools

  • index_repo: index Rust, Java, TypeScript/TSX, and Python files, chunks, symbols, graph edges, and the Tantivy sidecar. It can also run configured external reference providers for compiler/type-checker-grade references and calls.
  • search_code: search indexed chunks and return metadata. Set vector to true to fuse the deterministic local hash-vector signal with BM25 results.
  • vector_search: run only the deterministic local hash-vector search signal.
  • get_chunk: return exact source text for a chunk.
  • find_references: return graph edges that reference a symbol, including provider, confidence, and evidence. The heuristic provider resolves common local TypeScript/Python import aliases and receiver-style method calls when the referenced file is indexed. Syntactic calls are also persisted as calls edges for expansion and impact, while this tool continues to return reference/test-reference edges only.
  • expand_context: expand graph context around a chunk.
  • impact_analysis: report affected symbols, files, tests, and risk from caller-supplied changes. This lower-level call requires explicit changed files, changed symbols, and depth. Impact reasons include the graph edge provider label, for example calls via scip-java changed symbol.
  • pre_change_context: search before an edit, suggest chunk IDs, and return a budgeted metadata context pack. When repo is provided, search/expansion are scoped to that repository and matching project memories are included.
  • post_change_impact: analyze changed files and symbols after an edit, optionally deriving them from diff text.
  • memory_add: persist a repo/file/symbol/span anchored project memory.
  • memory_list: list memories, optionally filtered by repo and status.
  • memory_delete: delete one memory by ID.
  • memory_reconcile: refresh memory anchor statuses for a repo.

index_repo.force is optional. When omitted or false, indexing is incremental and removes stale files. When true, only the selected repository is rebuilt.

All public tool calls require a file-backed SQLite database path. :memory: is reserved for direct storage-layer tests because each public tool opens its own connection.

Contract Rule

search_code, pre_change_context, and expand_context return metadata. get_chunk returns source text.

The context budget is metadata-only. total_estimated_tokens sums each selected chunk's estimated_metadata_tokens; it is not an estimate of full source text size.

Search Result Shape

{
  "chunk_id": "chunk:simple-rust-0123:src/lib.rs:run",
  "path": "src/lib.rs",
  "language": "rust",
  "start_line": 1,
  "end_line": 3,
  "symbol_path": "run",
  "kind": "function",
  "score": 1.23,
  "signals": ["tantivy_bm25", "bm25", "local_hash_vector"]
}

local_hash_vector is a deterministic local feature-hashing signal over metadata and source text tokens. It does not download a model and is not represented as semantic ML retrieval.

Graph Edge Shape

{
  "from_id": "symbol:file-repo:src/lib.rs:wrapper",
  "to_id": "symbol:file-repo:src/lib.rs:run",
  "kind": "references",
  "provider": "heuristic",
  "confidence": 0.7,
  "evidence": "wrapper references run from src/lib.rs"
}

The built-in reference provider is heuristic. It preserves evidence and confidence, but does not claim compiler-grade precision. External rust-analyzer/SCIP/TypeScript/Python providers must use their own provider labels after invoking and validating the real compiler or type checker.

The graph crate exposes registered reference-provider descriptors. The built-in descriptor advertises the heuristic provider and its possible edge kinds: defines, references, calls, imports, implements, and contains_test_for.

For Java, TypeScript/TSX, and Python, heuristic evidence can include local imports, class relationships such as Service implements Runner or Worker extends WorkerBase, and receiver calls such as new Worker().run() or Worker().run(). These edges are intended to improve context selection and impact hints without claiming full type-checker accuracy.

The persisted edge identity includes provider, so multiple providers can report the same from_id/to_id/kind relationship without overwriting each other's confidence or evidence.

External Reference Providers

index_repo.reference_providers is optional. Each entry runs one external command with repo as the working directory. The command is invoked by argv, not through a shell string.

{
  "name": "scip-rust",
  "command": "scip-rust-provider",
  "args": ["--workspace", "."],
  "languages": ["rust"],
  "compiler_grade": true,
  "strict": true,
  "timeout_ms": 30000
}

languages filters providers by indexed chunk language. strict: true fails the whole index if an emitted edge cannot be resolved; strict: false skips unresolved edges. timeout_ms is optional. Provider stdout/stderr are drained concurrently while the process runs, each stream is capped at 16 MiB, and the timeout covers stdin writing plus process completion. On Windows, provider commands are attached to an unnamed Job Object when available; timeout cleanup terminates the Job Object so child processes from .cmd shims, Node, or language servers do not survive the index. Provider stdout diagnostics are surfaced on the index_repo response as provider_diagnostics entries with { "provider": "...", "message": "..." }.

The provider receives JSON on stdin:

{
  "protocol_version": 1,
  "repo": "/workspace/repo",
  "repo_id": "repo-0123",
  "provider": "scip-rust",
  "compiler_grade": true,
  "symbols": [],
  "chunks": []
}

The provider writes JSON on stdout:

{
  "edges": [
    {
      "from_symbol": "wrapper",
      "from_path": "src/lib.rs",
      "to_symbol": "run",
      "to_path": "src/lib.rs",
      "kind": "calls",
      "confidence": 1.0,
      "evidence": "SCIP call graph wrapper -> run"
    }
  ],
  "diagnostics": [
    "rust-analyzer command rust-analyzer"
  ]
}

Endpoints can use from_id/to_id for exact symbol IDs, or *_symbol plus optional *_path. Exact IDs are preferred for compiler-grade providers because they avoid same-name ambiguity. Path-qualified endpoints are recommended when IDs are not available. Confidence must be finite and within 0..=1. Unknown or ambiguous endpoints fail in strict mode.

The context-cli binary includes first-party adapters:

  • context provider rust-analyzer-scip: runs rust-analyzer scip, parses the SCIP index, and emits references/calls edges by from_id/to_id.
  • context provider java-scip: runs scip-java index, parses the generated index.scip, and emits references/calls edges by from_id/to_id.
  • context provider typescript: runs tsc --noEmit --strict, then resolves references and call edges through the stable TypeScript compiler/language-service API (typescript/lib/typescript.js).
  • context provider python: runs Pyright diagnostics, then resolves references through pyright-langserver LSP.

All adapters support --timeout-ms for direct provider subprocess execution. The Python adapter also supports --lsp-startup-delay-ms to avoid a hard-coded Pyright LSP settle wait. These adapters are loaded like any other external provider by setting the provider command to the context binary and args to the matching provider ... subcommand.

Expanded Chunk Shape

{
  "chunk_id": "chunk:simple-rust-0123:src/lib.rs:run",
  "path": "src/lib.rs",
  "start_line": 1,
  "end_line": 3,
  "symbol_path": "run",
  "kind": "function",
  "distance": 0,
  "reason": "seed",
  "estimated_metadata_tokens": 30
}

Pre-Change Shape

{
  "results": [],
  "suggested_chunk_ids": [],
  "context_pack": [],
  "symbols": [],
  "edges": [],
  "memories": [],
  "total_estimated_tokens": 0,
  "token_budget": 4000,
  "budget_exhausted": false
}

Project Memory Shape

{
  "id": "memory:repo-0123:abcd",
  "repo_id": "repo-0123",
  "anchor_kind": "symbol",
  "anchor_id": "run",
  "anchor_path": "src/lib.rs",
  "start_line": null,
  "end_line": null,
  "text": "run preserves the existing public behavior",
  "source": "human",
  "confidence": 1.0,
  "status": "current",
  "created_at_ms": 1785600000000,
  "updated_at_ms": 1785600000000
}

Memory statuses:

  • current: the exact anchor still resolves.
  • relocated: the exact ID moved, but a path/span anchor still resolves nearby.
  • stale: the repo/file remains, but the symbol/span no longer resolves.
  • gone: the anchored file or path is no longer indexed.

Post-Change Shape

{
  "changed_files": ["src/lib.rs"],
  "changed_symbols": ["run"],
  "changed_new_line_ranges": {
    "src/lib.rs": [{ "start": 1, "end": 1 }]
  },
  "report": {
    "risk_level": "low",
    "affected_symbols": [],
    "affected_files": [],
    "recommended_tests": [],
    "evidence": []
  }
}

Diff Input

post_change_impact.diff accepts unified git diff text. The parser is intentionally pure and does not run git itself. It supports normal file edits, additions, deletions, renames, quoted paths, CRLF input, zero-length new hunks, and malformed-hunk tolerance.