Pascal Editor is a local-first 3D CAD tool built with React Three Fiber and WebGPU. It runs in the browser or as a CLI-managed service and exposes parametric geometry operations to agents through the Model Context Protocol (MCP). The project (23K+ stars, trending #2 on GitHub for TypeScript) demonstrates how to bridge visual domain tools and agent workflows without corrupting scene state.
The architecture reveals practical patterns for exposing complex stateful operations to agents: authenticated background services, collision-free port selection, read-only safety boundaries, and persistent SQLite storage for project data.
Why This Matters
Most MCP implementations wrap simple CRUD operations or file systems. Pascal Editor exposes parametric 3D modeling primitives (extrusions, boolean operations, constraints) where naive agent access could corrupt the scene graph or violate geometric invariants. The CLI manages an authenticated MCP server in the background, handles agent connection handshakes, and enforces read-only boundaries for operations like furniture candidate checks.
The project shows how to handle the impedance mismatch between agent text commands and stateful visual operations. Agents can query geometry, propose changes, and check constraints without directly mutating the 3D scene.
Installation and Process Management
The CLI creates a persistent local installation without requiring a repository clone:
npx @pascal-app/cli editor
This command:
- Installs the editor and MCP service in
~/.pascal/ - Creates a SQLite database at
~/.pascal/data/pascal.dbfor project storage - Starts the editor UI and an authenticated MCP server in the background
- Selects collision-free loopback ports for both services
- Writes connection credentials to a local config file
Agents connect using:
pascal mcp connect
The CLI handles process lifecycle, port conflicts, and credential rotation. The MCP server runs as a long-lived background process, not a per-request spawn. This avoids startup latency and keeps the 3D scene graph in memory.
MCP Service Architecture
Pascal’s MCP server exposes geometry operations through a structured tool interface. The server runs authenticated to prevent unauthorized scene mutations. Connection flow:
- CLI generates a session token and writes it to
~/.pascal/mcp-credentials.json - Agent reads the token and connects to the loopback port
- MCP server validates the token before accepting tool calls
- Server maintains a read/write boundary: most agent operations are read-only queries
The authenticated model prevents agents from accidentally corrupting the scene graph. Write operations require explicit user confirmation through the UI.
Read-Only Safety Boundaries
The verified preview (commit 5dabbc3b) adds read-only furniture candidate checks. This pattern shows how to expose domain expertise to agents without granting mutation access:
- Agent queries the scene for furniture placement candidates
- MCP server runs collision detection and constraint checks
- Server returns valid placement zones as structured data
- Agent proposes placements, but cannot directly insert geometry
- User confirms or rejects proposals in the UI
This read-only pattern works for any domain where agents need to reason about complex state but should not mutate it directly. The MCP server acts as a query engine, not a command executor.
Hosted Agent Claim and Status Commands
The preview also adds hosted agent claim and status commands. These tools let agents:
- Claim ownership of a design task (prevents concurrent agent conflicts)
- Report progress and status updates
- Release claims when tasks complete or fail
The claim mechanism uses optimistic locking in SQLite. If two agents try to claim the same task, one gets a conflict error and must retry or abort. This prevents race conditions without requiring distributed locks.
State Management and Persistence
Pascal stores project data in SQLite at ~/.pascal/data/pascal.db. The schema includes:
- 3D geometry (meshes, curves, constraints)
- Parametric operation history (extrusions, booleans, transforms)
- Agent task claims and status
- User preferences and session state
The MCP server queries this database directly. Agents see a consistent view of the scene graph without needing to parse 3D file formats. The database also acts as an event log: the operation history can be replayed to reconstruct the scene or generate diffs.
Collision Detection and Constraint Checking
Furniture candidate checks run collision detection against the existing scene. The MCP server uses the same geometry engine as the UI, so agents get accurate spatial queries. The flow:
- Agent requests furniture candidates for a room
- MCP server loads the room geometry from SQLite
- Server runs a spatial query to find open floor areas
- Server checks each candidate against constraints (clearances, accessibility, structural elements)
- Server returns a ranked list of valid placements
This pattern generalizes to any domain with spatial or logical constraints. The MCP server encapsulates domain logic (collision detection, constraint solving) and exposes it as structured queries.
Port Selection and Conflict Avoidance
The CLI selects collision-free loopback ports for the editor UI and MCP server. The algorithm:
- Try default ports (3000 for UI, 3001 for MCP)
- If a port is in use, increment and retry
- Write selected ports to
~/.pascal/ports.json - Agents read the port file to discover the MCP endpoint
This avoids hardcoded ports and handles multiple Pascal instances on the same machine. The port file also includes the session token, so agents get both the endpoint and credentials in one read.
Failure Modes and Recovery
Common failure modes and how Pascal handles them:
| Failure | Detection | Recovery |
|---|---|---|
| MCP server crash | CLI polls process status | Restart server, rotate token, notify connected agents |
| SQLite lock timeout | Query returns error after 5s | Agent retries with exponential backoff |
| Corrupted scene graph | Geometry validation on load | Rollback to last valid operation in history |
| Agent connection loss | Heartbeat timeout (30s) | Release task claims, log partial progress |
| Port conflict on startup | Bind fails with EADDRINUSE | Increment port, retry up to 10 times |
The operation history in SQLite acts as a write-ahead log. If the scene graph becomes corrupted, Pascal can replay operations from the last known-good state.
Deployment Shape
Pascal runs entirely on the local machine. No cloud dependencies. The deployment shape:
- Editor UI: Next.js app running on a loopback port
- MCP server: Node.js process running on a separate loopback port
- SQLite database: Single file at
~/.pascal/data/pascal.db - Agent connection: Local IPC through MCP over HTTP
This local-first architecture avoids network latency and keeps sensitive design data off external servers. Agents run on the same machine and connect through localhost.
For teams, Pascal can run on a shared workstation with remote desktop access. The MCP server accepts connections only from localhost, so agents must run on the same machine or tunnel through SSH.
Observability and Debugging
The CLI writes logs to ~/.pascal/logs/. Key log streams:
- mcp-server.log: Tool calls, authentication events, query performance
- editor.log: UI events, scene graph mutations, user actions
- agent-tasks.log: Task claims, status updates, completion events
Agents can query the MCP server for task status and progress. The server exposes a get_task_status tool that returns structured data from the SQLite database.
For debugging, the CLI includes a pascal mcp logs command that tails the MCP server log in real time. This shows tool calls and responses as they happen.
Code Example: MCP Tool Definition
Here’s how Pascal defines a read-only furniture candidate tool:
// packages/mcp-server/src/tools/furniture-candidates.ts
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { db } from '../db.js';
import { checkCollisions } from '../geometry/collision.js';
export const furnitureCandidatesTool: Tool = {
name: 'get_furniture_candidates',
description: 'Find valid furniture placement zones in a room',
inputSchema: {
type: 'object',
properties: {
roomId: { type: 'string' },
furnitureType: { type: 'string', enum: ['desk', 'chair', 'shelf'] },
minClearance: { type: 'number', default: 0.6 }
},
required: ['roomId', 'furnitureType']
}
};
export async function handleFurnitureCandidates(args: any) {
const room = await db.getRoom(args.roomId);
const candidates = await db.getFloorAreas(room);
const valid = candidates.filter(candidate => {
const collisions = checkCollisions(candidate, room.geometry);
const clearance = candidate.minDistanceToWalls();
return collisions.length === 0 && clearance >= args.minClearance;
});
return {
content: [{
type: 'text',
text: JSON.stringify(valid.map(c => ({
position: c.center,
rotation: c.suggestedRotation,
score: c.accessibilityScore
})))
}]
};
}
The tool queries SQLite for room geometry, runs collision detection, and returns structured placement data. The agent cannot mutate the scene, only query it.
Technical Verdict
Use Pascal’s MCP architecture when:
- You need to expose complex domain operations (CAD, simulation, constraint solving) to agents
- You want read-only safety boundaries to prevent agent-induced corruption
- You need persistent local state with operation history for rollback
- You can run agents on the same machine as the domain tool (local-first)
Avoid this pattern when:
- Agents need low-latency access from remote machines (localhost-only MCP server)
- Your domain operations are stateless or idempotent (simpler to expose as HTTP APIs)
- You need multi-user concurrent access (SQLite has limited write concurrency)
- You cannot run a long-lived background process (serverless environments)
Pascal’s authenticated MCP server with read-only boundaries and SQLite persistence is a strong pattern for exposing stateful domain tools to agents. The collision-free port selection and process management make it practical for local development. The operation history provides rollback and audit trails. The main limitation is the localhost-only deployment shape, which requires agents to run on the same machine or tunnel through SSH.