| 1 | import assert from "node:assert/strict"; |
| 2 | |
| 3 | /** Read-only MCP lifecycle and tool/schema check; credentials never enter reports. */ |
| 4 | export async function verifyMcp(token, fetcher = fetch) { |
| 5 | const endpoint = "https://mcp.g1t.sh"; |
| 6 | const headers = { authorization: `Bearer ${token}`, "content-type": "application/json", accept: "application/json, text/event-stream" }; |
| 7 | async function post(method, params, id) { |
| 8 | const response = await fetcher(endpoint, { method: "POST", headers: { ...headers }, body: JSON.stringify({ jsonrpc: "2.0", ...(id === undefined ? {} : { id }), method, params }), signal: AbortSignal.timeout(20_000) }); |
| 9 | if (id === undefined) { assert.equal(response.status, 202); return; } |
| 10 | assert.equal(response.status, 200); |
| 11 | const value = await response.json(); |
| 12 | assert.equal(value.id, id); |
| 13 | assert.ok(!value.error, "MCP returned an error"); |
| 14 | if (response.headers.get("mcp-session-id")) headers["Mcp-Session-Id"] = response.headers.get("mcp-session-id"); |
| 15 | return value.result; |
| 16 | } |
| 17 | const initialized = await post("initialize", { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "g1t-readiness-check", version: "1" } }, 1); |
| 18 | assert.equal(initialized.protocolVersion, "2025-03-26"); |
| 19 | headers["MCP-Protocol-Version"] = initialized.protocolVersion; |
| 20 | await post("notifications/initialized", {}); |
| 21 | const listed = await post("tools/list", {}, 2); |
| 22 | assert.ok(listed.tools.length > 0); |
| 23 | const response = await fetcher(endpoint, { signal: AbortSignal.timeout(20_000) }); |
| 24 | assert.equal(response.status, 200); |
| 25 | const card = await response.json(); |
| 26 | let actions = 0; |
| 27 | for (const tool of listed.tools) { |
| 28 | const published = card.tools.find((entry) => entry.name === tool.name); |
| 29 | assert.ok(published, `Undocumented tool ${tool.name}`); |
| 30 | assert.equal(tool.inputSchema.type, "object"); |
| 31 | for (const name of tool.inputSchema.properties.action.enum) { |
| 32 | const action = published.actions.find((entry) => entry.name === name); |
| 33 | assert.ok(action, `Undocumented action on ${tool.name}`); |
| 34 | assert.ok(action.scope === null || card.scopes_supported.includes(action.scope), "Unknown action scope"); |
| 35 | actions++; |
| 36 | } |
| 37 | } |
| 38 | return { tools: listed.tools.length, actions }; |
| 39 | } |