feat(mcp): unindexed sessions go quiet — empty tools/list + inactive instructions, no-error policy (#769) (#817)
An MCP session in a workspace with no .codegraph/ previously got the full "lean on codegraph for everything" playbook plus all 8 tools, then every call returned isError — and one or two early errors teach an agent to abandon codegraph for the whole session (maintainer-observed). Now the initialize response picks an instructions variant by index state (cheap sync walk-up, #172 respond-fast contract holds) and tools/list serves an EMPTY list when unindexed: absence is the one signal an agent can't misread. Indexing is deliberately the user's call — the inactive note tells the agent not to run init itself. No-error policy in the tool handler: expected/recoverable conditions (NotIndexedError — cross-project query to an unindexed path, default- project detection miss) return SUCCESS-shaped guidance instead of isError; security refusals (PathRefusalError) stay hard errors without retry encouragement; genuine internal failures keep isError but add a retry-once note so a transient blip doesn't convert to permanent abandonment. Principle recorded in CLAUDE.md. Also: codegraph_search kind:"type" (advertised by its own schema enum) silently matched nothing — now maps to type_alias; codegraph_explore's query param no longer tells agents to run codegraph_search first (contradicted explore's call-FIRST design); server-instructions §Limitations rewords the unindexed case to stay-out-for-the-session. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0682681175
commit
f9fcc2cd6a
+23
-3
@@ -16,8 +16,9 @@ import * as path from 'path';
|
||||
import { JsonRpcRequest, JsonRpcNotification, JsonRpcTransport, ErrorCodes } from './transport';
|
||||
import { MCPEngine } from './engine';
|
||||
import { tools } from './tools';
|
||||
import { SERVER_INSTRUCTIONS } from './server-instructions';
|
||||
import { SERVER_INSTRUCTIONS, SERVER_INSTRUCTIONS_UNINDEXED } from './server-instructions';
|
||||
import { CodeGraphPackageVersion } from './version';
|
||||
import { findNearestCodeGraphRoot } from '../directory';
|
||||
|
||||
/**
|
||||
* MCP Server Info — kept on the session because some clients log it. The
|
||||
@@ -178,12 +179,24 @@ export class MCPSession {
|
||||
explicitPath = this.explicitProjectPath;
|
||||
}
|
||||
|
||||
// Pick the instructions variant by the workspace's index state — a cheap
|
||||
// synchronous walk-up (existsSync loop only, no DB open, so the #172
|
||||
// respond-fast contract holds). An unindexed workspace gets the short
|
||||
// "inactive this session" note instead of the full playbook: the playbook
|
||||
// tells the agent to lean on tools that would all fail, and early failures
|
||||
// teach the agent to abandon codegraph entirely. `tools/list` is gated the
|
||||
// same way (empty list when unindexed). When no explicit path is known yet
|
||||
// (roots/list dance pending), cwd is the best predictor of where the
|
||||
// default project will resolve — and on a mismatch the worst case is the
|
||||
// optimistic full playbook backstopped by the empty tool list.
|
||||
const indexed = findNearestCodeGraphRoot(explicitPath ?? process.cwd()) !== null;
|
||||
|
||||
// Respond to the handshake BEFORE doing any heavy init — see issue #172.
|
||||
this.transport.sendResult(request.id, {
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
capabilities: { tools: {} },
|
||||
serverInfo: SERVER_INFO,
|
||||
instructions: SERVER_INSTRUCTIONS,
|
||||
instructions: indexed ? SERVER_INSTRUCTIONS : SERVER_INSTRUCTIONS_UNINDEXED,
|
||||
});
|
||||
|
||||
if (explicitPath) {
|
||||
@@ -196,8 +209,15 @@ export class MCPSession {
|
||||
|
||||
private async handleToolsList(request: JsonRpcRequest): Promise<void> {
|
||||
await this.retryInitIfNeeded();
|
||||
// An unindexed workspace serves an EMPTY tool list: absence is the one
|
||||
// signal an agent can't misread. Listing 8 tools that all fail wastes the
|
||||
// agent's calls and teaches it codegraph is broken (observed: one or two
|
||||
// early isError responses and the agent stops calling codegraph for the
|
||||
// whole session). A `codegraph init` run after the server started is
|
||||
// picked up on the next tools/list — retryInitIfNeeded re-walks — though
|
||||
// most hosts only request the list once per connection.
|
||||
this.transport.sendResult(request.id, {
|
||||
tools: this.engine.getToolHandler().getTools(),
|
||||
tools: this.engine.hasDefaultCodeGraph() ? this.engine.getToolHandler().getTools() : [],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user