docs(mcp): add README, examples, and changelog

- README.md: install/quick start; configs for Claude Desktop, Claude
  Code, and Cursor; programmatic usage; tables covering all 21 tools,
  4 resources, and 3 prompts; limitations; development commands.
- CHANGELOG.md: 0.1.0 entry in Keep a Changelog format.
- examples/generate-apartment.md: prose transcript using from_brief
  to build an 80 m² 2-bed apartment, showing apply_patch, set_zone,
  cut_opening, validate_scene.
- examples/renovate-from-photos.md: prose transcript using the vision
  tools + renovation_from_photos prompt.
- examples/embed-in-agent.ts: compilable TypeScript showing
  programmatic usage via InMemoryTransport.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adrian Perez
2026-04-18 17:51:37 +02:00
co-authored by Claude Opus 4.7
parent 441e97b2b6
commit 3406dad8f5
5 changed files with 760 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
/**
* Programmatic `@pascal-app/mcp` usage.
*
* Runs a full MCP client/server pair over the in-memory transport inside a
* single Node process. Useful for agent frameworks and tests that want to
* drive Pascal without spawning a subprocess.
*
* Compile with the package's `tsc --build`, or run directly with Bun:
*
* bun run packages/mcp/examples/embed-in-agent.ts
*/
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { createPascalMcpServer, SceneBridge } from '@pascal-app/mcp'
async function main(): Promise<void> {
// 1. Spin up the headless bridge. `loadDefault()` seeds a Site → Building →
// Level stack so the client has something to query immediately.
const bridge = new SceneBridge()
bridge.loadDefault()
const server = createPascalMcpServer({ bridge })
// 2. Link the server to an in-memory client. Exactly the same API surface
// as the stdio / HTTP transports, but without any process boundary.
const [srvT, cliT] = InMemoryTransport.createLinkedPair()
const client = new Client({ name: 'my-agent', version: '0.1.0' })
await Promise.all([server.connect(srvT), client.connect(cliT)])
// 3. Discover available capabilities.
const tools = await client.listTools()
console.log(
'available tools:',
tools.tools.map((t) => t.name),
)
// 4. Inspect the current scene.
const scene = await client.callTool({ name: 'get_scene', arguments: {} })
console.log('scene snapshot:', JSON.stringify(scene, null, 2))
// 5. Find the default level, create a 5 m wall, and undo it.
const levels = await client.callTool({
name: 'find_nodes',
arguments: { type: 'level' },
})
const levelId = (levels.structuredContent as { nodes: Array<{ id: string }> }).nodes[0]?.id
if (levelId) {
const created = await client.callTool({
name: 'create_wall',
arguments: {
levelId,
start: [0, 0],
end: [5, 0],
thickness: 0.2,
height: 2.5,
},
})
console.log('created wall:', created.structuredContent)
const undone = await client.callTool({ name: 'undo', arguments: { steps: 1 } })
console.log('undone:', undone.structuredContent)
}
// 6. Validate and export.
const validation = await client.callTool({ name: 'validate_scene', arguments: {} })
console.log('validation:', validation.structuredContent)
const exported = await client.callTool({
name: 'export_json',
arguments: { pretty: true },
})
console.log('export size:', (exported.structuredContent as { json: string }).json.length)
await client.close()
await server.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})