fix(mcp): add shared operations and secure scene APIs

This commit is contained in:
Aymeric Rabot
2026-04-27 17:10:25 -04:00
parent 058ee747c9
commit 8dfd1a9429
81 changed files with 1332 additions and 1786 deletions
@@ -5,6 +5,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { SceneGraph } from '@pascal-app/core/clone-scene-graph'
import { type AnyNodeId, AnyNode as AnyNodeSchema } from '@pascal-app/core/schema'
import { SceneBridge } from '../../bridge/scene-bridge'
import { createSceneOperations } from '../../operations'
import {
InMemorySceneStore,
parseToolText,
@@ -56,8 +57,9 @@ async function setup(): Promise<{
bridge.setScene({}, [])
bridge.loadDefault()
const store = new InMemorySceneStore()
const operations = createSceneOperations({ bridge, store })
const server = new McpServer({ name: 'test', version: '0.0.0' })
registerGenerateVariants(server, bridge, store)
registerGenerateVariants(server, operations)
const [srvT, cliT] = InMemoryTransport.createLinkedPair()
const client = new Client({ name: 'test-client', version: '0.0.0' })
await Promise.all([server.connect(srvT), client.connect(cliT)])
@@ -2,8 +2,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { forkSceneGraph, type SceneGraph } from '@pascal-app/core/clone-scene-graph'
import { type AnyNode, AnyNode as AnyNodeSchema } from '@pascal-app/core/schema'
import { z } from 'zod'
import type { SceneBridge } from '../../bridge/scene-bridge'
import type { SceneStore } from '../../storage/types'
import type { SceneOperations } from '../../operations'
import { ErrorCode, throwMcpError } from '../errors'
import { applyMutation, describeVariant, type MutationKind, mulberry32 } from './mutations'
@@ -93,17 +92,13 @@ function countInvalidNodes(graph: SceneGraph): number {
return invalid
}
export function registerGenerateVariants(
server: McpServer,
bridge: SceneBridge,
store: SceneStore,
): void {
export function registerGenerateVariants(server: McpServer, bridge: SceneOperations): void {
server.registerTool(
'generate_variants',
{
title: 'Generate variants',
description:
'Generate N variations of a base scene by forking and applying seeded mutations. Example: "give me 5 variations of this kitchen". If `save=true`, each variant is persisted via the SceneStore and returned with an id + URL; otherwise the graph is returned inline.',
'Generate N variations of a base scene by forking and applying seeded mutations. Example: "give me 5 variations of this kitchen". If `save=true`, each variant is persisted via scene operations and returned with an id + URL; otherwise the graph is returned inline.',
inputSchema: generateVariantsInput,
outputSchema: generateVariantsOutput,
},
@@ -112,19 +107,14 @@ export function registerGenerateVariants(
let base: SceneGraph
let baseName = 'scene'
if (baseSceneId) {
const loaded = await store.load(baseSceneId)
const loaded = await bridge.loadStoredScene(baseSceneId)
if (!loaded) {
throwMcpError(ErrorCode.InvalidParams, 'scene_not_found', { id: baseSceneId })
}
base = loaded.graph
baseName = loaded.name
} else {
const exported = bridge.exportJSON()
base = {
nodes: exported.nodes,
rootNodeIds: exported.rootNodeIds,
collections: exported.collections as SceneGraph['collections'],
}
base = bridge.exportSceneGraph()
}
// 2. Seed the RNG. Default seed is a time-ish number so runs vary, but
@@ -167,7 +157,7 @@ export function registerGenerateVariants(
if (save) {
try {
const meta = await store.save({
const meta = await bridge.saveScene({
name: `${baseName}-variant-${i + 1}`,
graph: forked,
})
+4 -11
View File
@@ -1,19 +1,12 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { SceneBridge } from '../../bridge/scene-bridge'
import type { SceneStore } from '../../storage/types'
import type { SceneOperations } from '../../operations'
import { registerGenerateVariants } from './generate-variants'
/**
* Register the variant-generation MCP tools against the given server. Uses the
* supplied `SceneStore` both to load a `baseSceneId` (when provided) and to
* persist variants when `save=true`.
* Register the variant-generation MCP tools against shared scene operations.
*/
export function registerVariantTools(
server: McpServer,
bridge: SceneBridge,
store: SceneStore,
): void {
registerGenerateVariants(server, bridge, store)
export function registerVariantTools(server: McpServer, bridge: SceneOperations): void {
registerGenerateVariants(server, bridge)
}
export {