{
  "description": "MCP v2 local fixture demo",
  "checked_on": "2026-10-20",
  "node": "22.23.3",
  "protocol": "2026-07-28",
  "command": "node client.mjs",
  "result": {
    "checks": 9,
    "result": "PASS",
    "demo": {
      "project": "demo",
      "open": 3,
      "closed": 7,
      "source": "fixture-v1"
    }
  },
  "files": {
    "server.mjs": "import { McpServer } from '@modelcontextprotocol/server';\nimport { serveStdio } from '@modelcontextprotocol/server/stdio';\nimport { z } from 'zod/v4';\n\nconst projects = { demo: { open: 3, closed: 7, source: 'fixture-v1' } };\n\nserveStdio(() => {\n  const server = new McpServer({ name: 'project-summary', version: '2.0.0' });\n  server.registerTool('get_project_summary', {\n    description: 'Read issue counts from the local demo fixture. No live data.',\n    inputSchema: z.object({ project: z.string().min(1).max(40) }),\n    outputSchema: z.object({\n      project: z.string(), open: z.number(), closed: z.number(), source: z.string()\n    }),\n    annotations: { readOnlyHint: true, openWorldHint: false }\n  }, async ({ project }) => {\n    if (!Object.hasOwn(projects, project)) {\n      return { isError: true, content: [{ type: 'text', text: 'Unknown project. Available: demo.' }] };\n    }\n    const result = { project, ...projects[project] };\n    return { structuredContent: result, content: [{ type: 'text', text: JSON.stringify(result) }] };\n  });\n  return server;\n});\n",
    "client.mjs": "import assert from 'node:assert/strict';\nimport { fileURLToPath } from 'node:url';\nimport { Client } from '@modelcontextprotocol/client';\nimport { StdioClientTransport } from '@modelcontextprotocol/client/stdio';\n\nconst client = new Client({ name: 'article-check', version: '2.0.0' }, {\n  versionNegotiation: { mode: { pin: '2026-07-28' } }\n});\nconst transport = new StdioClientTransport({\n  command: process.execPath,\n  args: [fileURLToPath(new URL('./server.mjs', import.meta.url))]\n});\nlet checks = 0;\ntry {\n  await client.connect(transport);\n  const { tools } = await client.listTools();\n  assert.equal(tools.length, 1); checks++;\n  assert.equal(tools[0].name, 'get_project_summary'); checks++;\n  const demo = await client.callTool({ name: 'get_project_summary', arguments: { project: 'demo' } });\n  assert.deepEqual(demo.structuredContent, { project: 'demo', open: 3, closed: 7, source: 'fixture-v1' }); checks++;\n  const missing = await client.callTool({ name: 'get_project_summary', arguments: { project: 'missing' } });\n  assert.equal(missing.isError, true); checks++;\n  for (const project of ['', 'x'.repeat(41), 12, '__proto__']) {\n    const result = await client.callTool({ name: 'get_project_summary', arguments: { project } });\n    assert.equal(result.isError, true); checks++;\n  }\n  const again = await client.callTool({ name: 'get_project_summary', arguments: { project: 'demo' } });\n  assert.equal(again.structuredContent.open, 3); checks++;\n  console.log(JSON.stringify({ checks, result: 'PASS', demo: demo.structuredContent }));\n} finally {\n  await client.close();\n}\n",
    "package.json": "{\n  \"dependencies\": {\n    \"@modelcontextprotocol/client\": \"2.3.1\",\n    \"@modelcontextprotocol/server\": \"2.3.1\",\n    \"zod\": \"4.6.7\"\n  }\n}\n",
    "package-lock.json": "{\n  \"name\": \"demo-v2\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": {\n      \"dependencies\": {\n        \"@modelcontextprotocol/client\": \"2.3.1\",\n        \"@modelcontextprotocol/server\": \"2.3.1\",\n        \"zod\": \"4.6.7\"\n      }\n    },\n    \"node_modules/@modelcontextprotocol/client\": {\n      \"version\": \"2.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/@modelcontextprotocol/client/-/client-2.3.1.tgz\",\n      \"integrity\": \"sha512-mIGZXpHsjnZ6lD+gD/WCMpR5k8yVQQ8nNFH1N0Srf7AvnwTUMYD6pvTY8ng2+He5FfV7YMZLmrjL7cLa/cc3dQ==\",\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"@modelcontextprotocol/core\": \"2.3.1\",\n        \"cross-spawn\": \"^7.0.5\",\n        \"eventsource\": \"^3.0.2\",\n        \"eventsource-parser\": \"^3.0.8\",\n        \"jose\": \"^6.1.3\",\n        \"pkce-challenge\": \"^5.0.0\",\n        \"zod\": \"^4.2.0\"\n      },\n      \"engines\": {\n        \"node\": \">=20\"\n      }\n    },\n    \"node_modules/@modelcontextprotocol/core\": {\n      \"version\": \"2.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/@modelcontextprotocol/core/-/core-2.3.1.tgz\",\n      \"integrity\": \"sha512-laVmIhPGpWi7rG9q+0UigblRN0Hws/Yjlb6qLFtVqryXVLkc4h+yN8z731POD2LnAGQlFe7/tXhUm7Bi0YB91g==\",\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"zod\": \"^4.2.0\"\n      },\n      \"engines\": {\n        \"node\": \">=20\"\n      }\n    },\n    \"node_modules/@modelcontextprotocol/server\": {\n      \"version\": \"2.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/@modelcontextprotocol/server/-/server-2.3.1.tgz\",\n      \"integrity\": \"sha512-e59MfWuSssj6DoQ96JCTqdW1AXT5/XtnTl0WWd6MU1Ka434fFUGGosCLJbGNySDhHGvxQWoRWK137FXYFkYNWw==\",\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"@modelcontextprotocol/core\": \"2.3.1\",\n        \"zod\": \"^4.2.0\"\n      },\n      \"engines\": {\n        \"node\": \">=20\"\n      }\n    },\n    \"node_modules/cross-spawn\": {\n      \"version\": \"7.0.6\",\n      \"resolved\": \"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz\",\n      \"integrity\": \"sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"path-key\": \"^3.1.0\",\n        \"shebang-command\": \"^2.0.0\",\n        \"which\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/eventsource\": {\n      \"version\": \"3.0.7\",\n      \"resolved\": \"https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz\",\n      \"integrity\": \"sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"eventsource-parser\": \"^3.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=18.0.0\"\n      }\n    },\n    \"node_modules/eventsource-parser\": {\n      \"version\": \"3.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz\",\n      \"integrity\": \"sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=18.0.0\"\n      }\n    },\n    \"node_modules/isexe\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz\",\n      \"integrity\": \"sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==\",\n      \"license\": \"ISC\"\n    },\n    \"node_modules/jose\": {\n      \"version\": \"6.2.13\",\n      \"resolved\": \"https://registry.npmjs.org/jose/-/jose-6.2.13.tgz\",\n      \"integrity\": \"sha512-7TIg10ZE1x82YediN+lcrPNvGMKge9yFHQD+Qho/b8y2adfugU3AsvvHfxV0fzyFllT6l9xCjf4RWY9g/Z9PJQ==\",\n      \"license\": \"MIT\",\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/panva\"\n      }\n    },\n    \"node_modules/path-key\": {\n      \"version\": \"3.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz\",\n      \"integrity\": \"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/pkce-challenge\": {\n      \"version\": \"5.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz\",\n      \"integrity\": \"sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=16.20.0\"\n      }\n    },\n    \"node_modules/shebang-command\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz\",\n      \"integrity\": \"sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"shebang-regex\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/shebang-regex\": {\n      \"version\": \"3.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz\",\n      \"integrity\": \"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/which\": {\n      \"version\": \"2.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/which/-/which-2.0.2.tgz\",\n      \"integrity\": \"sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==\",\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"isexe\": \"^2.0.0\"\n      },\n      \"bin\": {\n        \"node-which\": \"bin/node-which\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/zod\": {\n      \"version\": \"4.6.7\",\n      \"resolved\": \"https://registry.npmjs.org/zod/-/zod-4.6.7.tgz\",\n      \"integrity\": \"sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==\",\n      \"license\": \"MIT\",\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/colinhacks\"\n      }\n    }\n  }\n}\n"
  }
}