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
+2 -2
View File
@@ -1,5 +1,5 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { SceneBridge } from '../bridge/scene-bridge'
import type { SceneOperations } from '../operations'
export const AGENT_GUIDE = [
'# Pascal MCP agent guide',
@@ -46,7 +46,7 @@ export const AGENT_GUIDE = [
'- Use `apply_patch` for bulk exact edits after semantic tools have established the main structure.',
].join('\n')
export function registerAgentGuide(server: McpServer, _bridge: SceneBridge): void {
export function registerAgentGuide(server: McpServer, _bridge: SceneOperations): void {
server.registerResource(
'agent-guide',
'pascal://agent/guide',
+2 -2
View File
@@ -1,5 +1,5 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { SceneBridge } from '../bridge/scene-bridge'
import type { SceneOperations } from '../operations'
import { MCP_CATALOG_ITEMS } from '../tools/asset-catalog'
/**
@@ -8,7 +8,7 @@ import { MCP_CATALOG_ITEMS } from '../tools/asset-catalog'
* The editor UI owns the full catalog. MCP intentionally keeps a dependency-free
* subset so headless agents can still place realistic furniture and fixtures.
*/
export function registerCatalogItems(server: McpServer, _bridge: SceneBridge): void {
export function registerCatalogItems(server: McpServer, _bridge: SceneOperations): void {
server.registerResource(
'catalog-items',
'pascal://catalog/items',
+6 -3
View File
@@ -2,7 +2,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { AnyNode, SlabNode, WallNode } from '@pascal-app/core/schema'
import { getWallPlanFootprint } from '@pascal-app/core/wall'
import type { SceneBridge } from '../bridge/scene-bridge'
import type { SceneOperations } from '../operations'
type WallFootprint = {
wallId: string
@@ -36,7 +36,10 @@ const EMPTY_MITER_DATA: Parameters<typeof getWallPlanFootprint>[1] = {
junctions: new Map(),
}
function buildPayload(bridge: SceneBridge, levelId: string): ConstraintsPayload | ConstraintsError {
function buildPayload(
bridge: SceneOperations,
levelId: string,
): ConstraintsPayload | ConstraintsError {
const level = bridge.getNode(levelId as never)
if (!level || level.type !== 'level') {
return {
@@ -72,7 +75,7 @@ function buildPayload(bridge: SceneBridge, levelId: string): ConstraintsPayload
* input hints for agents: slab nodes (with polygons/holes/elevation) + each
* wall's plan-view footprint polygon.
*/
export function registerConstraints(server: McpServer, bridge: SceneBridge): void {
export function registerConstraints(server: McpServer, bridge: SceneOperations): void {
server.registerResource(
'constraints',
new ResourceTemplate('pascal://constraints/{levelId}', { list: undefined }),
+7 -7
View File
@@ -1,5 +1,5 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { SceneBridge } from '../bridge/scene-bridge'
import type { SceneOperations } from '../operations'
import { registerAgentGuide } from './agent-guide'
import { registerCatalogItems } from './catalog-items'
import { registerConstraints } from './constraints'
@@ -16,10 +16,10 @@ import { registerSceneSummary } from './scene-summary'
* - `pascal://constraints/{levelId}` — application/json, per-level constraints
* - `pascal://agent/guide` — text/markdown, MCP-first construction guide
*/
export function registerResources(server: McpServer, bridge: SceneBridge): void {
registerAgentGuide(server, bridge)
registerSceneCurrent(server, bridge)
registerSceneSummary(server, bridge)
registerCatalogItems(server, bridge)
registerConstraints(server, bridge)
export function registerResources(server: McpServer, operations: SceneOperations): void {
registerAgentGuide(server, operations)
registerSceneCurrent(server, operations)
registerSceneSummary(server, operations)
registerCatalogItems(server, operations)
registerConstraints(server, operations)
}
+2 -2
View File
@@ -1,12 +1,12 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { SceneBridge } from '../bridge/scene-bridge'
import type { SceneOperations } from '../operations'
/**
* `pascal://scene/current` — full `{ nodes, rootNodeIds, collections }` snapshot.
*
* Static URI (not a template). MIME `application/json`.
*/
export function registerSceneCurrent(server: McpServer, bridge: SceneBridge): void {
export function registerSceneCurrent(server: McpServer, bridge: SceneOperations): void {
server.registerResource(
'scene-current',
'pascal://scene/current',
+5 -3
View File
@@ -1,6 +1,6 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { AnyNode, AnyNodeType } from '@pascal-app/core/schema'
import type { SceneBridge } from '../bridge/scene-bridge'
import type { SceneOperations } from '../operations'
type Poly2D = ReadonlyArray<readonly [number, number]>
@@ -77,7 +77,9 @@ function countByType(nodes: AnyNode[]): Record<string, number> {
}
/** Build the markdown summary. Pure over the SceneGraph snapshot. */
export function buildSceneSummaryMarkdown(snapshot: ReturnType<SceneBridge['exportJSON']>): string {
export function buildSceneSummaryMarkdown(
snapshot: ReturnType<SceneOperations['exportJSON']>,
): string {
const { nodes, rootNodeIds } = snapshot
const allNodes = Object.values(nodes) as AnyNode[]
@@ -193,7 +195,7 @@ function walkToLevel(node: AnyNode, nodes: Record<string, AnyNode>): string | nu
* `pascal://scene/current/summary` — human-readable scene overview.
* MIME `text/markdown`.
*/
export function registerSceneSummary(server: McpServer, bridge: SceneBridge): void {
export function registerSceneSummary(server: McpServer, bridge: SceneOperations): void {
server.registerResource(
'scene-summary',
'pascal://scene/current/summary',