v1.14.1-mcp-poc: single-server MCP client against Context7
Validates the MCP-client loop end-to-end against one real MCP server before the full v1.15 port. New services/mcp-client.ts wraps @modelcontextprotocol/sdk v1.29.0 with Streamable HTTP transport. On startup (when MCP_CONTEXT7_URL is set), connects to Context7, discovers tools via tools/list, wraps each as a ToolDef prefixed context7_<name>, and appends to ALL_TOOLS via appendMcpTools. Read-only invariant guard rejects any tool with readOnlyHint: false. Tool dispatch is transparent — executeToolCall routes MCP calls through the ToolDef execute wrapper, which strips the prefix before calling the MCP server. Result size capped at 5MB with truncation. Graceful degradation: server down at startup → zero tools; server down mid-session → error result, model self-corrects. Adversarial review caught that a Zod .default() on the URL config made MCP always-on instead of opt-in — fixed by removing the default. MCP_CONTEXT7_URL must be explicitly set to enable. ALL_TOOLS changed from ReadonlyArray to mutable to support late-registration. appendMcpTools re-sorts and rebuilds TOOLS_BY_NAME after append. 348/348 server tests passing (16 new mcp-client tests). No schema changes, no frontend changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -651,7 +651,9 @@ export const askUserInput: ToolDef<AskUserInputInputT> = {
|
||||
// of the system prompt, so any order drift would invalidate every cached
|
||||
// turn. Single source of truth for ordering lives here — toolJsonSchemas()
|
||||
// and TOOLS_BY_NAME inherit it.
|
||||
export const ALL_TOOLS: ReadonlyArray<ToolDef<unknown>> = [
|
||||
// v1.14.1-mcp-poc: changed from ReadonlyArray to let-bound mutable array
|
||||
// so appendMcpTools() can push MCP-discovered tools at startup.
|
||||
export let ALL_TOOLS: ToolDef<unknown>[] = [
|
||||
viewFile as ToolDef<unknown>,
|
||||
viewTruncatedOutput as ToolDef<unknown>,
|
||||
listDir as ToolDef<unknown>,
|
||||
@@ -725,10 +727,23 @@ export const READ_ONLY_TOOL_NAMES = [
|
||||
'request_read_access',
|
||||
] as const;
|
||||
|
||||
export const TOOLS_BY_NAME: Record<string, ToolDef<unknown>> = Object.fromEntries(
|
||||
export let TOOLS_BY_NAME: Record<string, ToolDef<unknown>> = Object.fromEntries(
|
||||
ALL_TOOLS.map((t) => [t.name, t])
|
||||
);
|
||||
|
||||
// v1.14.1-mcp-poc: append MCP-discovered tools at startup. Called once
|
||||
// from index.ts after mcpClient.initialize(). Re-sorts ALL_TOOLS and
|
||||
// rebuilds TOOLS_BY_NAME. READ_ONLY_TOOL_NAMES is not rebuilt because
|
||||
// it's a const tuple used only for budget-tier checks; MCP tools are
|
||||
// individually checked via their category at budget resolution time —
|
||||
// they are all read_only by construction (the read-only guard in
|
||||
// mcp-client.ts rejects any tool with readOnlyHint: false).
|
||||
export function appendMcpTools(mcpTools: ToolDef<unknown>[]): void {
|
||||
if (mcpTools.length === 0) return;
|
||||
ALL_TOOLS = [...ALL_TOOLS, ...mcpTools].sort((a, b) => a.name.localeCompare(b.name));
|
||||
TOOLS_BY_NAME = Object.fromEntries(ALL_TOOLS.map((t) => [t.name, t]));
|
||||
}
|
||||
|
||||
// v1.13.15-tools: tiered tool loading. BOOCODE_TOOLS env var (`core` |
|
||||
// `standard` | `all`) filters the agent's tool whitelist before LLM dispatch.
|
||||
// Daily-driver token win on qwen3.6-35b-a3b — the 35B-A3B MoE benefits from
|
||||
|
||||
Reference in New Issue
Block a user