// Opt-in: MCP_TEST_PYTHON= MCP_TEST_SERVER_ROOT= bun tests/integration/mcp-query-smoke.ts import { resolve } from "node:path"; import { McpQueryClient, type McpHttpFetch } from "../../src/product/mcp-query-client.js"; import { bindSessionCredential, releaseSessionCredential } from "../../src/product/credential-refresh.js"; import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client"; import { startQueryMcpServer } from "../../src/mcp/query-server.js"; import { setRuntimeSessionContext, removeRuntimeSessionContext } from "../../src/runtime/sessionContext.js"; const python = process.env.MCP_TEST_PYTHON; const root = process.env.MCP_TEST_SERVER_ROOT; if (!python || !root) throw new Error("MCP smoke requires an explicit isolated Server environment"); // The fixture owns its OAuth/resource settings. Local Agent MCP configuration // (notably a private development issuer) must not override its loopback setup. const fixtureEnv = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("TJWATER_MCP_"))); const processHandle = Bun.spawn([python, resolve(root, "tests/fixtures/mcp_query_app.py")], { cwd: root, env: { ...fixtureEnv, PYTHONPATH: root }, stdout: "pipe", stderr: "pipe" }); const session = "fixture-host-session"; let gateway: ReturnType | undefined; let nativeClient: Client | undefined; try { const reader = processHandle.stdout.getReader(); const output = await Promise.race([reader.read(), Bun.sleep(10_000).then(() => { throw new Error("MCP fixture did not start"); })]); reader.releaseLock(); const resource = new TextDecoder().decode(output.value).trim().split("\n").at(-1)!; if (!/^http:\/\/127\.0\.0\.1:\d+\/mcp$/.test(resource)) throw new Error("MCP fixture endpoint invalid"); const requests: string[] = []; const grants: string[] = []; const issuer = "http://localhost/realms/test"; const tokenEndpoint = "http://localhost/fixture/oauth/token"; const fixtureJwt = (kind: string) => `e30.${Buffer.from(JSON.stringify({ sub: "be3b27c8-0e58-4e3a-9df5-e62bc43f4e51", iss: "http://localhost/realms/test", aud: "tjwater-mcp", azp: "tjwater-agent-mcp", fixture: kind })).toString("base64url")}.fixture`; const http: McpHttpFetch = async (input, init) => { const url = String(input); if (url === "http://localhost/.well-known/oauth-authorization-server/realms/test") return Response.json({ issuer, authorization_endpoint: "http://localhost/fixture/oauth/authorize", token_endpoint: tokenEndpoint, response_types_supported: ["code"], token_endpoint_auth_methods_supported: ["client_secret_post"], }); if (url === tokenEndpoint) { const body = new URLSearchParams(String(init?.body)); if (body.get("resource") !== resource) throw new Error("OAuth grant omitted its resource indicator"); const grant = body.get("grant_type"); grants.push(grant ?? ""); return Response.json({ access_token: fixtureJwt(grant === "client_credentials" ? "catalog" : "user"), token_type: "Bearer", expires_in: 300 }); } if (url === resource) requests.push(new Headers(init?.headers).get("MCP-Method") ?? init?.method ?? "GET"); return fetch(input, init); }; const client = new McpQueryClient({ issuer: "http://localhost/realms/test", resource, audience: "tjwater-mcp", clientId: "tjwater-agent-mcp", clientSecret: "fixture" }, http); const catalog = await client.prepare(); if (catalog.length !== 35) throw new Error("Query catalog discovery failed"); if (!catalog.some((tool) => tool.name === "run_simulation" && tool.inputSchema.properties?.start_time && tool.inputSchema.properties?.output_node_ids)) throw new Error("Standard hydraulic simulation schema missing"); bindSessionCredential(session, { authorization: `Bearer ${fixtureJwt("source")}`, projectId: "d44a9241-de48-4e60-9348-867dc466a031" }); setRuntimeSessionContext({ sessionId: session, clientSessionId: session, actorKey: "fixture", traceId: "fixture", projectId: "d44a9241-de48-4e60-9348-867dc466a031", projectKey: "fixture-project", purpose: "ordinary_chat" }); gateway = startQueryMcpServer({ internalToken: "MCP_TEST_ONLY", productClient: client, productTools: catalog, resolveReader: async () => null, executeLocal: async () => { throw new Error("unused fixture query"); } }); nativeClient = new Client({ name: "fixture-native", version: "1" }, { versionNegotiation: { mode: { pin: "2026-07-28" } } }); await nativeClient.connect(new StreamableHTTPClientTransport(new URL(gateway.url), { requestInit: { headers: { "x-agent-internal-token": "MCP_TEST_ONLY" } } })); if ((await nativeClient.listTools()).tools.length !== 41) throw new Error("Merged MCP query catalog discovery failed"); const result = await nativeClient.callTool({ name: "list_elements", arguments: { element_type: "pipe", limit: 1 }, _meta: { "ai.opencode/sessionID": session } }); if (result.isError || JSON.stringify((result.structuredContent as { data?: unknown } | undefined)?.data) !== '[{"id":"P-1"}]') throw new Error("Typed query execution failed"); const nearbyTool = catalog.find((tool) => tool.name === "query_nearby_elements"); if (!nearbyTool?.inputSchema.properties?.center) throw new Error("Nearby model tool schema missing"); const nearby = await nativeClient.callTool({ name: "query_nearby_elements", arguments: { center: [121.5, 30.9], radius_m: 500, limit: 5 }, _meta: { "ai.opencode/sessionID": session } }); if (nearby.isError) throw new Error("Nearby model query failed through merged MCP"); if (!requests.includes("server/discover") || !requests.includes("tools/call") || requests.includes("initialize") || requests.includes("GET")) throw new Error("Transport used unexpected protocol requests"); if (grants.join(",") !== "client_credentials,urn:ietf:params:oauth:grant-type:token-exchange") throw new Error("OAuth grants were not validated"); console.log("PASS: local MCP 41-tool discovery, real Server schemas, OAuth discovery, resource-bound grants, host binding, query and revocation over MCP HTTP"); } finally { await nativeClient?.close(); await gateway?.close(); removeRuntimeSessionContext(session); releaseSessionCredential(session); processHandle.kill("SIGTERM"); await processHandle.exited; }