/** * MCP Tool Definitions * * Defines the tools exposed by the CodeGraph MCP server. */ import CodeGraph from '../index'; import type { PendingFile } from '../sync'; import type { Node, Edge, TaskContext } from '../types'; export declare class NotIndexedError extends Error { } export declare class PathRefusalError extends Error { } type SerializedTaskContext = { query: string; summary: string; retrieval?: TaskContext['retrieval']; entryPoints: Node[]; nodes?: Node[]; edges: Edge[]; roots?: string[]; relatedFiles: string[]; codeBlocks: TaskContext['codeBlocks']; stats: TaskContext['stats']; }; type TaskMapInsights = { displayEntryPoints: Node[]; keyLinks: string[]; conceptClusters: Array<{ label: string; symbols: string[]; files: string[]; reasons: string[]; }>; convergencePoints: string[]; traceHints: string[]; confirmedPath: string[]; confirmedPathFiles: string[]; nextHops: string[]; whyIncluded: string[]; }; /** * Calculate the recommended number of codegraph_explore calls based on project size. * Larger codebases need more exploration calls to cover their surface area, * but smaller ones should use fewer to avoid unnecessary overhead. */ export declare function getExploreBudget(fileCount: number): number; /** * Adaptive output budget for `codegraph_explore`, scaled to project size. * * Smaller codebases get a tighter total cap, fewer default files, smaller * per-file cap, and tighter clustering — so a focused query on a 100-file * project doesn't dump a whole file's worth of source into the agent's * context. Larger codebases keep the generous defaults because the * agent's native discovery cost (grep + find + many Reads) genuinely * dwarfs a fat explore call at that scale. * * Meta-text (relationships map, "additional relevant files" list, * completeness signal, budget note) is gated off for tiny projects * where one rich call is the whole story and the extra prose is just * overhead. * * Tier breakpoints mirror `getExploreBudget` so a project sits in the * same tier across both knobs. */ export interface ExploreOutputBudget { /** Hard cap on total output characters. */ maxOutputChars: number; /** Default `maxFiles` when the caller didn't specify one. */ defaultMaxFiles: number; /** Cap on contiguous source returned per file (across all its clusters). */ maxCharsPerFile: number; /** Cluster gap threshold in lines — tighter clustering on small projects. */ gapThreshold: number; /** Max symbols listed in the per-file header (`#### path — sym(kind), ...`). */ maxSymbolsInFileHeader: number; /** Max edges shown per relationship kind in the Relationships section. */ maxEdgesPerRelationshipKind: number; /** Include the "Relationships" section. */ includeRelationships: boolean; /** Include the "Additional relevant files (not shown)" trailing list. */ includeAdditionalFiles: boolean; /** Include the "Complete source code is included above…" reminder. */ includeCompletenessSignal: boolean; /** Include the explore-budget reminder at the end. */ includeBudgetNote: boolean; /** * Hard-drop test/spec/icon/i18n files from the relevant-file set unless * the query itself mentions tests. Today they're only deprioritized in * the sort, which on tiny repos can still let one displace a primary * implementation file and dominate the response budget with low-signal * content. Off by default; on for the very-tiny tier where one slip * dominates the budget. */ excludeLowValueFiles: boolean; } export declare function getExploreOutputBudget(fileCount: number): ExploreOutputBudget; /** * Per-file staleness banner emitted at the top of a tool response when the * file watcher has pending events for files referenced by the response. */ export declare function formatStaleBanner(stale: PendingFile[]): string; /** * Compact footer listing pending files that are NOT referenced in this * response. */ export declare function formatStaleFooter(stale: PendingFile[]): string; /** * MCP Tool definition */ export interface ToolDefinition { name: string; description: string; inputSchema: { type: 'object'; properties: Record; required?: string[]; }; /** Behavioral hints for clients (see {@link ToolAnnotations}). */ annotations?: ToolAnnotations; } /** * MCP ToolAnnotations — behavioral hints a client MAY use to decide how, or * whether, to run a tool (introduced in the 2025-03-26 spec, carried in * 2025-06-18). They are advisory and never to be trusted for security, but * clients gate on them: Cursor's Ask mode, for one, refuses any MCP tool that * doesn't advertise `readOnlyHint: true` (issue #1018). * * The field is purely additive — a client that predates annotations ignores it * — so codegraph advertises these even though `initialize` still negotiates the * 2024-11-05 protocol version. * * https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations */ export interface ToolAnnotations { /** Human-readable title for the tool. */ title?: string; /** If true, the tool does not modify its environment. Default (unset): false. */ readOnlyHint?: boolean; /** Meaningful only when NOT read-only: may the tool perform destructive updates? */ destructiveHint?: boolean; /** If true, repeat calls with the same arguments have no additional effect. */ idempotentHint?: boolean; /** If true, the tool interacts with an open world of external entities. */ openWorldHint?: boolean; } interface PropertySchema { type: string; description: string; enum?: string[]; default?: unknown; } /** * Tool execution result */ export interface ToolResult { content: Array<{ type: 'text'; text: string; }>; isError?: boolean; } /** * All CodeGraph MCP tools * * Designed for minimal context usage - use codegraph_context as the primary tool, * and only use other tools for targeted follow-up queries. * * All tools support cross-project queries via the optional `projectPath` parameter. */ export declare const tools: ToolDefinition[]; export declare function getStaticTools(): ToolDefinition[]; /** * Tool handler that executes tools against a CodeGraph instance * * Supports cross-project queries via the projectPath parameter. * Other projects are opened on-demand and cached for performance. */ export declare class ToolHandler { private cg; private projectCache; private defaultProjectHint; private worktreeMismatchCache; private catchUpGate; constructor(cg: CodeGraph | null); /** * Update the default CodeGraph instance (e.g. after lazy initialization) */ setDefaultCodeGraph(cg: CodeGraph): void; /** * Engine-only: register the catch-up sync promise so the next `execute()` * call awaits it before serving. The handler swallows rejections (the * engine logs them) so a sync failure never propagates as a tool error; * we still want to serve a best-effort result over the same potentially- * stale data, which is what would have happened without the gate. */ setCatchUpGate(p: Promise | null): void; private awaitCatchUpGate; /** * Record the directory the server tried to resolve the default project from. * Used only to make the "no default project" error actionable. */ setDefaultProjectHint(searchedPath: string): void; /** * Whether a default CodeGraph instance is available */ hasDefaultCodeGraph(): boolean; /** * Optional allowlist of exposed tools, parsed from the CODEGRAPH_MCP_TOOLS * env var (comma-separated short names, e.g. "trace,search,node,context"). * Unset/empty → every tool is exposed. Lets an operator (or an A/B harness) * trim the tool surface without rebuilding the client config; the ablated * tool is then truly absent from ListTools rather than merely denied on call. * Matching is on the short form, so "trace" and "codegraph_trace" both work. */ private toolAllowlist; /** Whether a tool name passes the CODEGRAPH_MCP_TOOLS allowlist (if any). */ private isToolAllowed; /** * Get tool definitions with dynamic descriptions based on project size. * The codegraph_explore tool description includes a budget recommendation * scaled to the number of indexed files. Honors the CODEGRAPH_MCP_TOOLS * allowlist so a trimmed surface is reflected in ListTools. */ getTools(): ToolDefinition[]; private degradedWatcherNotice; /** * Get CodeGraph instance for a project * * If projectPath is provided, opens that project's CodeGraph (cached). * Otherwise returns the default CodeGraph instance. * * Walks up parent directories to find the nearest .codegraph/ folder, * similar to how git finds .git/ directories. */ private getCodeGraph; /** * Heal a long-lived connection whose `.codegraph/` was removed and recreated * at the same path (a worktree recreated, or `rm -rf .codegraph` + re-init) * before handing it to a tool. Otherwise the daemon keeps serving the * pre-removal snapshot from its now-unlinked file handle until restart — and * because the daemon registry is keyed by path, a same-path recreate routes * new clients straight back to this same stale daemon (#925). The check is one * stat() and a no-op unless the inode actually changed; it never throws into a * tool call. */ private freshen; /** * Close all cached project connections */ closeAll(): void; /** * Validate that a value is a non-empty string within length bounds. * * The `maxLength` cap protects against MCP clients that ship huge * payloads (10MB+ query strings either by accident or maliciously). * Without this, a single oversized input can pin the FTS5 index or * exhaust memory before any real work runs. */ private validateString; /** * Validate an optional path-like string input. Returns the value if * valid (or undefined), or a ToolResult with the error. */ private validateOptionalPath; /** * Validate an optional enum-like string. Returns undefined when omitted, * or a ToolResult when the provided value is not one of the allowed values. */ private validateOptionalEnum; /** * Annotate a successful read-tool result with per-file staleness — the * non-blocking answer to issue #403. */ private withStalenessNotice; /** * Cached git worktree/index mismatch for a tool call's effective project. * * The "effective project" is what the request targets: an explicit * `projectPath` arg, else the directory the server resolved its default * project from (`defaultProjectHint`), else cwd. Memoized per start path — * see `worktreeMismatchCache`. Best-effort: if the project can't be resolved * (e.g. nothing initialized yet), it reports "no mismatch" so a tool is never * broken by this check. */ private worktreeMismatchFor; /** * Prefix a successful read-tool result with a compact worktree-mismatch * notice when the resolved index belongs to a different git working tree than * the caller's (issue #155). Without this, an agent in a nested worktree * silently trusts main-branch results. No-op on error results and when there * is no mismatch. `codegraph_status` is excluded — it embeds its own verbose * warning — so it stays out of this path. */ private withWorktreeNotice; /** * Execute a tool by name */ execute(toolName: string, args: Record): Promise; /** * Handle codegraph_search */ private handleSearch; /** * Handle codegraph_context */ private handleContext; /** * Detect a flow-style task ("how does X reach Y", "trace the path from A to B") * and pre-run trace between the most likely endpoints, returning the trace * body to splice into the context response. Returns '' for non-flow queries * or when no plausible endpoint pair can be extracted. * * Conservative by design: only fires when the task has both a clear flow * keyword AND at least two distinct PascalCase / camelCase identifiers. * False positives waste a graph query; false negatives just fall back to * the agent calling trace itself (existing path-proximity wiring handles * disambiguation either way). */ private maybeInlineFlowTrace; /** * Heuristic to detect if a query looks like a feature request */ private looksLikeFeatureRequest; /** * Handle codegraph_callers */ private handleCallers; /** * Handle codegraph_callees */ private handleCallees; /** * Handle codegraph_impact */ private handleImpact; /** * Handle codegraph_trace — shortest CALL PATH between two symbols. * * Exposes GraphTraverser.findPath: the chain of functions from `from` to `to`, * each hop annotated with file:line and the call-site line. This is the * capability grep/Read structurally cannot provide. When no static path * exists, the chain has almost certainly broken at dynamic dispatch * (callbacks, descriptors, metaclasses) — we say so and surface the start * symbol's outgoing calls so the agent bridges the one missing hop with * codegraph_node rather than blindly reading. */ private handleTrace; /** * Describe a synthesized (dynamic-dispatch) edge for human output: how the * callback was wired up — the bridge static parsing can't see. Returns null * for ordinary static edges. Used by trace + the node trail so a synthesized * hop reads as "registered via onUpdate at App.tsx:3148", not a bare arrow. */ private synthEdgeNote; /** * Read one trimmed source line at "relpath:line" (relative to the project * root). `cache` holds split file contents so a multi-hop trace reads each * file at most once. Returns null if the file/line can't be resolved. */ private sourceLineAt; /** * Read a hop's body — filePath lines [startLine..endLine] — for inlining into * a trace, capped (lines + chars) so the whole path stays path-scoped even on * a 7-hop chain. Dedents to the body's own indentation and marks truncation. * Shares `cache` with sourceLineAt so each file is read at most once per trace. */ private sourceRangeAt; /** * Flow-from-named-symbols: an agent's codegraph_explore query is a bag of * symbol names that usually spans the flow it's investigating (e.g. * "PmsProductController getList PmsProductService list PmsProductServiceImpl"). * Surface the longest call chain AMONG those named symbols — scoped to what the * agent explicitly named, so (unlike a fuzzy relevance set) there's no * wrong-feature wandering. Rides synthesized edges, so controller→service- * interface→impl shows up. Returns '' if no chain of >=3 nodes exists. * * Ambiguous tokens (Java `list` → dozens of nodes) are disambiguated by * CO-NAMING: the agent names the class too, so we keep only `list` candidates * whose qualifiedName contains another named token (`PmsProductServiceImpl::list`), * dropping unrelated `OmsOrderService::list`. */ private buildFlowFromNamedSymbols; private buildDynamicBoundaries; private boundaryCandidates; /** * Compact "blast radius" for the entry symbols of an explore result: who * depends on each (callers) and which test files cover it — locations only. */ private buildBlastRadiusSection; /** * Graph-connectivity relevance via Random-Walk-with-Restart (personalized * PageRank) from the query's matched seed nodes over the call/reference graph. * * This keeps structural relevance ahead of raw term frequency: files that are * actually on or near the matched flow accrue walk mass, while a lone text hit * with no structural connection stays near zero. */ private computeGraphRelevance; /** * Handle codegraph_explore — deep exploration in a single call * * Strategy: find relevant symbols via graph traversal, group by file, * then read contiguous file sections covering all symbols per file. * This replaces multiple codegraph_node + Read calls. * * Output size is adaptive to project file count via * `getExploreOutputBudget` — see #185 for why a fixed 35k cap was a * tax on small projects while earning its keep on large ones. */ private handleExplore; /** * Handle codegraph_node */ private handleNode; /** * FILE READ MODE: resolve `fileArg` (path or basename) to an indexed file and * read it like the Read tool — its current on-disk source with line numbers, * narrowable with `offset`/`limit` exactly as Read's are — preceded by a * one-line blast-radius header (which files depend on it). `symbolsOnly` * returns just the structural map (symbols + dependents) instead of source. * * Parity goal: the numbered source block is byte-for-byte the shape Read * returns (`\t`, no padding), so the agent treats it as a Read — only * faster (served from the index) and with the blast radius attached. Security: * yaml/properties files are summarized by key, never dumped (#383); reads go * through validatePathWithinRoot (#527). */ private handleFileView; private formatTrail; private handleStatus; private handleFiles; private globToRegex; private formatFilesFlat; private formatFilesGrouped; private formatFilesTree; private matchesSymbol; private findSymbolMatches; /** * Find ALL symbols matching a name. Used by callers/callees/impact to aggregate * results across all matching symbols (e.g., multiple classes with an `execute` method). */ private findAllSymbols; private groupDefinitions; private definitionHeading; /** Render one symbol: details + (optional) body/outline + its caller/callee trail. */ private renderNodeSection; /** * Truncate output if it exceeds the maximum length */ private truncateOutput; private formatSearchResults; private formatNodeList; private formatImpact; private buildArkTsImpactProjection; /** * Build a compact structural outline of a container symbol from its * indexed children (methods, fields, properties, …) — name, kind, * line number, and signature — so the agent gets the shape of a class * without the full source of every method. Returns '' when the container * has no indexed children, so the caller can fall back to full source. */ private buildContainerOutline; private formatNodeDetails; private formatTaskContext; private recoverLifecycleNextHopFromConfirmedPath; private getTaskContextSectionProfile; private extractTaskContextRouteOwnerLabel; private getTaskContextDisplayConfidence; private formatTaskContextPrototype; private collectDiagnoseInsights; private collectReviewerInsights; private collectKeyLinks; private collectNextHops; private collectWhyIncluded; private collectTaskMapInsights; private shouldUseTaskMapSemanticFastPath; private collectTaskMapSemanticFastPathInsights; private collectTaskMapSemanticFastPathCandidates; private recoverTaskMapMixedGenerationOwnerCandidate; private recoverTaskMapPermissionReviewOwnerCandidate; private recoverTaskMapResourceContractOwnerCandidate; private recoverTaskMapPermissionStateUpdateCandidate; private recoverTaskMapProvideConsumeOwnerCandidate; private recoverTaskMapBuildValidationContractCandidate; private recoverTaskMapForwardImpactCandidate; private selectTaskMapDirectForwardImpactCandidate; private trimTaskMapLeadingInteractionForImpactCandidate; private getTaskMapDirectForwardImpactCandidateScore; private recoverTaskMapInteractionWriteClosureCandidate; private expandTaskMapMixedGenerationTriggerCandidate; private selectTaskMapRenderBoundaryCandidate; private selectTaskMapDirectIdentityBoundaryCandidate; private selectTaskMapDirectRenderClosureCandidate; private selectTaskMapDirectReactiveDiagnoseCandidate; private selectTaskMapDirectCanonicalCoordinatorCandidate; private hasTaskMapCanonicalInteractionLeadIn; private selectTaskMapDirectLifecycleRestoreCandidate; private buildTaskMapCanonicalRestoreClosureCandidate; private recoverTaskMapCanonicalRestoreFromSelectedPath; private recoverTaskMapCanonicalRestoreFromSubgraph; private collectTaskMapFastPathStartNodes; private collectTaskMapFastPathEndNodes; private isTaskMapSemanticFastPathEdge; private alignTaskMapCandidateToQuery; private selectTaskMapSharedStateTerminalCandidate; private selectTaskMapSourceOfTruthCandidate; private recoverTaskMapSourceOfTruthFactClosure; private getTaskMapSourceOfTruthReadNodeScore; private selectTaskMapIdentityTerminalCandidate; private getTaskMapSharedStateCandidateScore; private getTaskMapIdentityCandidateScore; private getTaskMapRenderBoundaryCandidateScore; private selectTaskMapPlatformContractCandidate; private selectTaskMapOwnerTerminalCandidate; private selectTaskMapNavigationTerminalCandidate; private selectTaskMapWantBoundaryCandidate; private getTaskMapNavigationCandidateScore; private selectTaskMapResourceBindingCandidate; private selectTaskMapResourcePageCandidate; private selectTaskMapPermissionReviewCandidate; private getTaskMapResourceBindingCandidateScore; private getTaskMapOwnerCandidateScore; private inferConfirmedPathFromContext; private getTaskMapSubstrateScoring; private getTaskContextSemanticAdapter; private findBoundedTaskMapPaths; private findTaskMapPathWithinContext; private buildTaskMapPathCandidateFromSteps; private collectTaskMapConvergencePoints; private extendTaskMapPathCandidate; private collectTaskMapTraceHints; private isTaskMapLifecycleShellRestoreCandidate; private isLifecycleShellRestoreTraceHint; private prependLifecyclePageTraceHint; private collectTaskMapDisplayEntryPoints; private collectTaskMapRecoveredOwnerEntries; private buildTaskMapSummary; private collectTaskMapKeyLinks; private collectTaskMapNextHops; private collectTaskMapWhyIncluded; private selectTaskContextPlatformFacts; private selectTaskContextPlatformConstraints; private selectTaskContextStateEffects; private selectTaskContextConvergencePoints; private compactTaskContextStateEffects; private selectTaskContextCausalitySlices; private selectTaskContextRouteContracts; private selectTaskContextBridgeContracts; private selectTaskContextStateFacts; private selectTaskContextTraceHints; private formatTaskMapPath; private formatTaskMapNode; private compactTaskMapSteps; private getTaskMapNodeScore; private collectTaskMapPathSeedNodes; private collectTaskMapRetrievalAnchorNodes; private getTaskMapSearchProfile; private getTaskMapEdgeScore; private getTaskMapSemanticEdgeText; private getTaskMapSemanticEdgeScore; private appendTaskMapSemanticEvidence; private getTaskMapBacktraceScore; private isTaskMapBridgeNode; private isTaskMapExplicitBridgeBoundaryNode; private isTaskMapArkTsOwnerFlowNode; private isTaskMapNativeNode; private isTaskMapLegacyNode; private isTaskMapMixedGenerationSignalNode; private isTaskMapMixedGenerationTopLevelOwnerNode; private isTaskMapGenericHubNode; private isTaskMapBacktraceRoot; private isTaskMapArkTsLikeNode; private isTaskMapArkTsOwnerNode; private isTaskMapTerminalCandidate; private isTaskMapLowValueTerminalNode; private isTaskMapCompilerGeneratedNode; private getTaskMapQueryAlignmentScore; private getTaskMapQueryNodeBoost; private isReliableTaskMapCandidate; private isTaskMapReactiveClosureCandidate; private isTaskMapOrderedReactiveClosureCandidate; private isTaskMapOrderedLifecycleRestoreCandidate; private isTaskMapOrderedRenderClosureCandidate; private isTaskMapDirectRenderHandoffCandidate; private isOwnerIntentQuery; private isSharedStateIntentQuery; private isLifecycleIntentQuery; private isCanonicalRestoreClosureQuery; private isNavigationIntentQuery; private isPlatformConstraintIntentQuery; private isWantBoundaryQuery; private isRenderBoundaryQuery; private isRenderBoundaryDiagnoseQuery; private isResourceBindingQuery; private isResourcePageQuery; private isPermissionReviewQuery; private isPermissionStateClosureQuery; private isResourceOwnerContractQuery; private isProvideConsumeBoundaryQuery; private isBuildValidationContractQuery; private isForwardImpactReviewerQuery; private isTaskMapMixedGenerationOwnerQuery; private shouldPreferRenderBoundaryPath; private isReactiveSharedStateDiagnoseQuery; private normalizeTaskMapConfirmedCandidate; private extractTaskMapPermissionReviewCoreSteps; private extractTaskMapIdentityCoreSteps; private extractTaskMapResourceBindingCoreSteps; private extractTaskMapPlatformContractCoreSteps; private extractTaskMapRenderOwnershipCoreSteps; private extractTaskMapRenderClosureCoreSteps; private extractTaskMapReactiveDiagnoseCoreSteps; private extractTaskMapCanonicalOwnerCoreSteps; private extractTaskMapLifecycleRestoreCoreSteps; private isTaskMapLifecycleResolutionCandidate; private isTaskMapSharedStateResolutionCandidate; private isTaskMapNavigationResolutionCandidate; private isTaskMapOwnerResolutionCandidate; private isIdentityIntentQuery; private isTaskMapIdentityNode; private isTaskMapStrongIdentityTerminal; private isTaskMapIdentityResolutionCandidate; private collectTaskMapIdentityBoundaryCandidates; private collectTaskMapStateFactCandidates; private collectTaskMapCausalityClosureCandidates; private collectTaskMapPlatformContractCandidates; private collectTaskMapRouteContractCandidates; private collectTaskMapRenderBoundaryClosureCandidates; private collectTaskMapResourceBindingCandidates; private findTaskMapNamedNode; private findTaskMapBestFactNode; private findTaskMapFactNodes; private extractTaskMapFactSymbol; private createTaskMapSyntheticSemanticEdge; private getTaskMapSourceText; private extractTaskMapResourceCallsFromSource; private extractTaskMapMethodBody; private findTaskMapChildNode; private findTaskMapEdgeBetween; private isCompatibilityBoundaryQuery; private isCompatibilityBoundaryCandidate; private isTaskMapOwnerSignalNode; private isTaskMapInteractionEntryNode; private isTaskMapPlatformContractNode; private isTaskMapDirectInvocation; private collectTaskMapDirectCallIdentifiers; private isTaskMapBuildRenderInvocation; private isTaskMapPermissionBoundaryNode; private isTaskMapIdentityKeyOwnerNode; private isTaskMapIdentityRenderHandoffNode; private isTaskMapReactiveConsumerNode; private isTaskMapDownstreamEffectNode; private isTaskMapActionImpactNode; private isTaskMapResourceBindingStep; private isTaskMapResourceOwnerNode; private isTaskMapPersistenceNode; private isTaskMapStateMutationNode; private isTaskMapRenderMutationNode; private isTaskMapReadRestoreNode; private hasTaskMapCanonicalStateStemOverlap; private getTaskMapCanonicalStateStemTokens; private isTaskMapWriteOwnerNode; private isTaskMapAbilityBoundaryNode; private isTaskMapWantBootstrapNode; private isTaskMapPageConsumerNode; private isTaskMapAppShellCandidate; private isTaskMapMirrorRefreshConsumerPath; private shouldSuppressSharedStateMirrorNoise; private isTaskMapMirrorHelperNode; private isTaskMapCanonicalSharedStateCandidate; private isTaskMapCausalityFactMatch; private isTaskMapLifecycleMethod; private isTaskMapReactiveEntryNode; private isTaskMapStructuralAnchorNode; private isTaskMapUtilityNode; private isTaskMapUIOnlyNode; private getTaskMapIntentTerms; private getTaskMapScoringTerms; private getTaskMapPathQueryTermHits; private compareTaskMapDisplayNodes; private compareTaskMapCandidates; deserializeTaskContext(context: SerializedTaskContext): TaskContext; collectTaskMapInsightsFromSerializedPayload(context: TaskContext): TaskMapInsights | null; private textResult; private errorResult; } export {}; //# sourceMappingURL=tools.d.ts.map