Adrian Perez 441e97b2b6 feat(mcp): add stdio + streamable HTTP transports, CLI, and smoke test
- connectStdio(server): wires the MCP server to StdioServerTransport.
- connectHttp(server, port): wires the MCP server to
  StreamableHTTPServerTransport over node:http. Returns a handle with
  port and close().
- bin/pascal-mcp.ts: CLI entrypoint with --stdio (default), --http,
  --port, --scene, --help, --version. Loads node-shims before any
  core import. Logs to stderr (stdio transport uses stdout for
  JSON-RPC).
- scripts/smoke.ts: end-to-end smoke — spawns the stdio bin, connects
  as an MCP Client, exercises get_scene, create_level, validate_scene,
  undo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:51:27 +02:00

47 lines
1.8 KiB
TypeScript

import { afterEach, beforeEach, expect, test } from 'bun:test'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { SceneBridge } from '../bridge/scene-bridge'
import { createPascalMcpServer } from '../server'
import { connectStdio } from './stdio'
let bridge: SceneBridge
beforeEach(() => {
bridge = new SceneBridge()
bridge.loadDefault()
})
afterEach(() => {
// Fresh store per test — no global teardown needed.
})
test('connectStdio is an async function', () => {
expect(typeof connectStdio).toBe('function')
// Async functions report their constructor as AsyncFunction.
expect(connectStdio.constructor.name).toBe('AsyncFunction')
})
test('server+client over linked in-memory pair can list tools', async () => {
// Functional equivalence check: we can't attach the real stdio transport in
// a test (it hijacks process stdin/stdout), but we can still verify that
// `createPascalMcpServer` produces a server that exposes tools over any
// MCP transport. This catches regressions where tool registration fails
// silently during server construction.
const server = createPascalMcpServer({ bridge })
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair()
const client = new Client({ name: 'stdio-test-client', version: '0.0.0' })
await Promise.all([server.connect(serverTransport), client.connect(clientTransport)])
try {
const tools = await client.listTools()
expect(Array.isArray(tools.tools)).toBe(true)
// Don't require a specific count — other agents own tool registration.
// Just assert the wiring carries protocol traffic end-to-end.
} finally {
await client.close()
await server.close()
}
})