mcphandlers

package
v0.0.0-...-3ada0ad Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package mcphandlers exposes ckg's MCP tool handlers as a public, reusable surface so sister repos (code-knowledge-system / cks, code-knowledge-vector / ckv) can wire their own mcp-go servers to the same eight-tool set without re-implementing the bodies.

The handlers live here rather than under internal/mcp/ because the Go internal/ rule blocks external import of anything inside it. Moving them to pkg/ is the T-14 public-surface promotion described in eval/stablenet/HANDOFF.md.

Tool catalogue

find_symbol          resolve a qname / name to nodes
find_callers         reverse call graph over calls + invokes edges
find_callees         forward call graph over calls + invokes edges
get_subgraph         bidirectional BFS rooted at qname
search_text          FTS5 search with AND/OR mode + language pushdown
impact_of_change     reverse-dependency closure (broader than callers)
get_context_for_task smart 1-shot: BM25 -> 1-hop -> score-fuse -> pack
evidence_for_intent  H3 EvidencePack assembler over commit/hunk corpus

H3 retrieval boundary

Every handler operates on a wrapped Reader (see NewLLMSafeReader) that filters AMBIGUOUS Hunk + Commit nodes — the §11.3 unreachable-history track — out of every result set before it reaches an LLM. RegisterAll applies the wrapper for the entire tool set; individual Register* callers must wrap their reader themselves if they want the same guarantee.

Minimal cks-side wiring

import (
    server "github.com/mark3labs/mcp-go/server"
    "github.com/0xmhha/code-knowledge-graph/pkg/mcphandlers"
    "github.com/0xmhha/code-knowledge-graph/pkg/store"
)

r, err := store.OpenReadOnly("/tmp/ckg-graph")
if err != nil { /* ... */ }
defer r.Close()

s := server.NewMCPServer("cks-mcp", "0.1.0")
mcphandlers.RegisterAll(s, r)
_ = server.ServeStdio(s)

Stability

The Register* function signatures and the tool schema definitions follow semantic versioning once the cks/ckv extractions land. Adding a new optional argument to a tool is a non-breaking change; removing or renaming an existing argument is breaking.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewLLMSafeReader

func NewLLMSafeReader(r store.Reader) store.Reader

NewLLMSafeReader wraps r so AMBIGUOUS Hunk/Commit nodes are stripped from every read result. Every Register* handler applies this boundary itself via [safeReader], so the §11.3 H3 boundary holds no matter how tools are mounted; this constructor stays exported for callers that want the wrapped reader for their own use.

func RegisterAll

func RegisterAll(s *server.MCPServer, reader store.Reader)

RegisterAll wires every ckg tool to s in one call. Equivalent to invoking each Register* function individually plus RegisterEvidenceForIntent with a fresh evidence.NewCache.

The §11.3 H3 retrieval boundary is enforced inside every Register* (each wraps its reader via safeReader, idempotently), so it holds for the whole tool set here and for any individual handler mounted on its own — callers can no longer forget to wrap. Callers that need a different lifecycle (one cache across multiple servers, a partial tool subset) can still compose the individual Register* calls directly.

Existing callers within this module use this to keep server.go down to ~10 lines; sister-repo wiring (cks / ckv) is recommended to use this too unless there's a specific reason not to.

func RegisterChangeHistory

func RegisterChangeHistory(s *server.MCPServer, reader store.Reader)

RegisterChangeHistory wires the change_history tool: the merged pull requests that touched a symbol (number, title, summary, merged-at), newest first. It exposes the graph's existing per-node PR breadcrumbs (Reader.GetNodePRs) over MCP so an agent can answer "when/why did this code change" from a single HEAD graph — no pre-fix re-index or git fallback.

func RegisterConcurrencyImpact

func RegisterConcurrencyImpact(s *server.MCPServer, reader store.Reader)

RegisterConcurrencyImpact wires the concurrency_impact tool — the concurrency blast radius of a symbol over the five contract edge types (spawns, sends_to, recvs_from, acquires_lock, accessed_under_lock; R1' contract C1, S1). The algorithm body lives in pkg/concurrency so cks consumes it in-process; this file is the MCP request envelope only (the dev-only ckg server, 00 §7), mirroring RegisterImpactOfChange.

func RegisterEvidenceForIntent

func RegisterEvidenceForIntent(s *server.MCPServer, reader store.Reader, cache *evidence.Cache)

RegisterEvidenceForIntent wires the H3 EvidencePack assembler (docs/design/hunk-graph.md §5). Returns an EvidencePack JSON for an intent string + optional seed_qname / issue_id, ranked by BM25 over the (commit subject || patch || modifies-qnames) virtual document and grouped by parent commit.

The cache parameter is shared across the lifetime of one Run invocation so the BM25 corpus is built once and reused across queries — sub-second latency on graphs that take ~4s for a cold rebuild. The cache invalidates itself when the underlying graph.db's manifest drifts (a `ckg build` while the server is running). RegisterAll passes a fresh evidence.NewCache so callers get the standard lifetime without having to thread it themselves; if you need to share one cache across multiple servers, call this function directly.

§11.3 retrieval boundary is enforced two ways:

  • When wired via RegisterAll, reader is already wrapped by NewLLMSafeReader, so AllNodes/AllEdges/GetBlob filter AMBIGUOUS Hunks/Commits at the read layer.
  • pkg/evidence.indexCorpus also filters confidence='EXTRACTED' as defence in depth — even if a future caller bypasses the wrapper, the EvidencePack assembler still hides the unreachable-history track from LLM consumers.

func RegisterFindCallees

func RegisterFindCallees(s *server.MCPServer, reader store.Reader)

RegisterFindCallees walks the forward call graph from the seed. Same edge-type filter as RegisterFindCallers for symmetry.

func RegisterFindCallers

func RegisterFindCallers(s *server.MCPServer, reader store.Reader)

RegisterFindCallers walks the reverse call graph from the seed. Filters to calls/invokes edges so the BFS only follows real invocation links (see callEdgeTypes for the rationale). Default depth=2 — see docs/ckg5-depth-sweep-report-2026-05-20.md for the latency justification.

func RegisterFindSymbol

func RegisterFindSymbol(s *server.MCPServer, reader store.Reader)

RegisterFindSymbol resolves an exact-or-suffix qname / name to nodes.

Description text was rewritten 2026-05-11 (go-stablenet VERIFICATION_REPORT §3.1 B2): the prior phrasing implied a bare name worked with exact=true, but FindSymbol always matches qualified_name — bare names work only with exact=false (suffix match). The schema spells that contract out so LLM agents don't false-empty on `{"name":"NewBlockChain","exact":true}`.

func RegisterGetContextForTask

func RegisterGetContextForTask(s *server.MCPServer, reader store.Reader)

RegisterGetContextForTask wires the smart 1-shot retrieval tool: BM25 retrieve -> 1-hop expand -> score-fuse -> diversify -> pack within token budget. The body lives in pkg/smartctx so the eval δ baseline measures exactly the same algorithm the LLM consumer runs at request time.

1-shot enrichment (P0 #2, docs/PROJECT-BLUEPRINT-ALIGNMENT.md §4.2): opt-in flags include_recent_prs and include_impact fold the PR breadcrumb and reverse-deps closures into the same response, so a coding-agent can answer "what is this, why did it change, what depends on it?" in a single tool call.

func RegisterGetSubgraph

func RegisterGetSubgraph(s *server.MCPServer, reader store.Reader)

RegisterGetSubgraph returns the BFS bidirectional subgraph rooted at qname. Unlike find_callers / find_callees this DOES follow every edge type — the caller asked for a neighbourhood, not a call graph.

func RegisterImpactOfChange

func RegisterImpactOfChange(s *server.MCPServer, reader store.Reader)

RegisterImpactOfChange wires the impact_of_change tool. Either seed_qname or seed_file must be set; if both are set, seed_qname wins (less ambiguous, and the qname path returns a single seed node for the response envelope).

The algorithm body lives in pkg/impact so the same code path is shared with internal/server's HTTP /api/impact handler — mirrors the smartctx pattern (pkg/smartctx is shared by get_context_for_task and the eval δ baseline). This file is the MCP request envelope only.

func RegisterSearchText

func RegisterSearchText(s *server.MCPServer, reader store.Reader)

RegisterSearchText runs the smart Search router (FTS5 with auto-prefix for ASCII, LIKE substring fallback for CJK). Routes through attachBlobs so the response shape matches find_symbol / find_callers / get_subgraph — LLM clients can parse one schema across the toolbox.

mode = "or" (default) ORs multi-token queries via rewriteFTSQuery (any token match surfaces the candidate, BM25 + PageRank + usage rerank). mode = "and" requires every token to appear in the hit's FTS-indexed columns; useful for precise multi-keyword retrieval. language pushes a `WHERE language = ?` filter into the SQL when non-empty (CKG-2).

node_kinds is the optional whitelist of node types to return. The default (omitted / empty) applies a symbol-only filter that strips statement-level rows (IfStmt/LoopStmt/CallSite/ReturnStmt/SwitchStmt/ AwaitPoint), meta rows (Commit/Hunk), and path-only rows (Import/Export). Pass an explicit array — typically the full set produced by types.AllNodeTypes — to disable the default narrowing and surface every FTS match.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL