@zindex-ai/mcp 0.40.12 → 0.40.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +12 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,7 +21,9 @@ ${x}`);let t=s;if(typeof t.id=="string"&&typeof t.units=="string"&&typeof t.canv
|
|
|
21
21
|
|
|
22
22
|
${x}`);return s}function G(s,e){s.tool("dsp_validate_scene",`Validate a scene document for structural and semantic correctness. Use this to check your scene BEFORE creating it with dsp_create_scene. Does not persist anything.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
INPUT: the 'scene' argument must be a COMPLETE inline scene document, NOT a sceneId string. If you only have a sceneId (e.g. from dsp_create_scene), fetch the document first with dsp_get_scene and pass the returned envelope here. This tool does not look up scenes by id.
|
|
25
|
+
|
|
26
|
+
The 'scene' argument shape:
|
|
25
27
|
|
|
26
28
|
{
|
|
27
29
|
"schemaVersion": "0.1",
|
|
@@ -31,14 +33,17 @@ The 'scene' argument must be a COMPLETE scene document with this shape:
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
Common mistakes:
|
|
36
|
+
- DO NOT pass a sceneId string (e.g. "my-scene-123"); fetch with dsp_get_scene first, then pass the returned envelope.
|
|
34
37
|
- DO NOT pass just the inner scene object ({"id":...,"title":...}); pass the full envelope above.
|
|
35
38
|
- DO NOT wrap in another envelope ({scene: {...}}); the 'scene' field at this tool's input level IS the envelope.
|
|
36
39
|
- DO NOT pass a string of JSON; pass a parsed object.
|
|
37
40
|
|
|
38
41
|
Returns { ok, diagnostics: [{ code, severity, path, message }] }.
|
|
39
|
-
On 'ok: false' the diagnostics array contains structured per-rule details - programmatically iterate them rather than parsing the message string.`,{scene:te.any()},async({scene:t})=>{let n=E(t,"dsp_validate_scene"),o=await e.validateScene(n);return{content:[{type:"text",text:JSON.stringify(o)}]}})}import{z as v}from"zod";function B(s,e,t){let n=e===null,o=n?"Render a scene to SVG or PNG (anonymous mode). Pass the inline scene as `scene` - the platform validates, normalizes, lays out, and renders in one call. Stateless: no scene IDs, no revisions, no diff, no watermark. For persistence (sceneIds, revisions, diff via dsp_diff_scene, watermark on every render), set ZINDEX_API_KEY and use the sceneId form (free key at https://zindex.ai/signup). Check `diagnostics` for: TEXT_OVERFLOW (resize element), CANVAS_AUTO_EXTENDED (declared canvas was auto-extended to fit content; minimum, not a hard cap), NODE_OVERLAP_DETECTED (two node rects overlap; reposition or switch to layoutStrategy), MISSING_DIAGRAM_FAMILY (declare diagramFamily on every scene), MISSING_GLYPH (label contains a codepoint Inter doesn't have; rendered as visible tofu placeholder \u25A1 instead of dropped silently), AUTOGROUP_FAMILY_MISMATCH / AUTOGROUP_SUPPRESSED_TRIVIAL / AUTOGROUP_SKIPPED_ABSOLUTE (scene.extensions.autoGroup was set but skipped - see code for the reason).":"Render a scene to SVG or PNG. Two input forms: pass `sceneId` to render a persisted scene at its current revision (with watermark and diff support, the recommended workflow after dsp_create_scene + dsp_apply_ops); or pass `scene` to render an inline scene without persisting (stateless, no watermark, no revision tracking). Persisted renders include the `Rev N - date` watermark and accept `diffFromRevision` for a coloured visual diff. Check the `diagnostics` array for TEXT_OVERFLOW (resize element), CANVAS_AUTO_EXTENDED (info; canvas was auto-extended), EDGE_LABEL_SUPPRESSED_REDUNDANT (info; an edge label matching a column in either endpoint's extensions.columns was auto-promoted to a column-row anchor, matching the dbdiagram.io / DBeaver convention - the FK edge now terminates at the named column row; set edge.style.forceLabel=true to keep the label, or set endpoint.column directly to anchor without auto-inference), EDGE_LABEL_SUPPRESSED_FANIN (info; 2+ edges share an exact label string AND a target endpoint AND come from the same conceptual source unit (same source, same parent frame with full coverage, OR a common single-step predecessor), so the platform renders the label once on the lowest-id edge per group and drops it from the others; set edge.style.forceLabel=true to keep a label on a specific edge, or rename one of the duplicates), EDGE_LABEL_FANIN_BUNDLED (info; 2+ edges share an exact label string AND a target endpoint but the source nodes are structurally distinct - geometry merges into one trunk so the convergence is visible, but every label and arrowhead is preserved because each edge represents a distinct hop with its own meaning; typical case is parallel CRUD handlers writing the same SQL operation to a shared database), EDGE_OBSTACLE_GRAZED (info; data has grazedEdgeIds and clearance - one or more edges were routed through the degraded-clearance fallback because the router could not find a route that cleared every non-source/non-target node by 16 px; recovery: widen scene.canvas, move the obstacle node, distinguish/merge the affected edges, or set style.forceLabel=true if intentional), EDGE_COLUMN_NOT_FOUND (warning; endpoint.column references a column not declared in the entity - falls back to face-centre anchoring), LABEL_DUPLICATION_DETECTED (rename one of the duplicated labels), LAYOUT_ABSOLUTE_AT_ORIGIN (info; 1+ nodes declare layout.mode='absolute' with default coordinates {x:0, y:0} - data has affectedElementIds, allAtOrigin, and layoutStrategyPresent; remove the layout block on those nodes and rely on layoutStrategy at scene level), LAYOUT_STRATEGY_OVERRIDDEN (info; data has strategyAlgorithm - scene declares layoutStrategy AND every node has explicit layout.mode='absolute' coords, so the strategy is silently ignored; pick one: REMOVE per-element layout blocks to use auto-layout, OR REMOVE layoutStrategy if pinning every node by hand), NODE_OVERLAP_DETECTED (warning; data has nodeA, nodeB, and overlap{x,y,width,height} - two leaf-node bounding boxes overlap by more than 2 px after layout completes; the renderer paints both rects so the lower one is obscured by the upper, surfacing as labels reading through other shapes or edges terminating inside a covered node; reposition one of the named nodes via updateNode, or remove the per-node absolute layout blocks and set scene.layoutStrategy for auto-placement), MISSING_DIAGRAM_FAMILY (info; the scene omits diagramFamily, which gates family-specific behaviour the engine and downstream tooling rely on; data has allowedValues; declare diagramFamily on every scene via dsp_create_scene or updateScene), MISSING_GLYPH (info; data has codepoint, weight, hex - a label contained a codepoint Inter doesn't include, typically math/logic symbols like \u2227 \u2228 \u2261 \u2200 \u2203 \u2208; rendered as visible tofu placeholder \u25A1 U+25A1 instead of being silently dropped; rewrite the label or accept the marker), AUTOGROUP_FAMILY_MISMATCH (info; scene.extensions.autoGroup was set but diagramFamily isn't 'architecture' - extension is architecture-only in v1, ignored), AUTOGROUP_SUPPRESSED_TRIVIAL (info; scene.extensions.autoGroup was set but fewer than 2 nodeType groups have 2+ members - single band is pointless visual chrome), and AUTOGROUP_SKIPPED_ABSOLUTE (info; scene.extensions.autoGroup was set but every node uses absolute layout - auto-grouping requires the layered planner to pack same-type nodes contiguously; remove per-node layout blocks and rely on layoutStrategy).";s.tool("dsp_render_scene",o,{sceneId:v.string().optional().describe(n?"Persisted-scene ID. NOT AVAILABLE in anonymous mode - this server has no ZINDEX_API_KEY set. Either set ZINDEX_API_KEY (free key at https://zindex.ai/signup) and restart the host, or pass `scene` instead for an inline render.":"Persisted-scene ID returned by dsp_create_scene. Renders the scene at its current revision with watermark + diff support. Use this for the recommended persist-first workflow."),scene:v.any().optional().describe("Inline scene document (the same shape dsp_validate_scene accepts). Use for stateless one-off renders without persisting. Available in both anonymous and authenticated modes; in authenticated mode prefer `sceneId` so you get the watermark and diff."),format:v.string().default("svg"),theme:v.string().optional().describe("Render theme: clean (default), dark, blueprint, sketch"),showRevision:v.boolean().optional().describe('Persisted-scene only. Show the full watermark: "scene-id | Rev N - date" in the bottom-right and "zindex.ai" in the bottom-left. Default true. Set to false for completely clean output - the watermark is binary (full metadata or nothing).'),diffFromRevision:v.number().optional().describe("Persisted-scene only. Render visual diff from this revision to current. Added=green, removed=red, modified=amber.")},async({sceneId:r,scene:d,format:i,theme:a,showRevision:f,diffFromRevision:m})=>{if(!r&&!d)return{content:[{type:"text",text:JSON.stringify({error:"Either `sceneId` (persisted scene) or `scene` (inline scene) must be provided."})}],isError:!0};if(r&&d)return{content:[{type:"text",text:JSON.stringify({error:"Provide `sceneId` OR `scene`, not both. Use `sceneId` for a persisted scene, `scene` for an inline render."})}],isError:!0};if(r&&n)return{content:[{type:"text",text:JSON.stringify({error:"Rendering a persisted scene by sceneId requires an API key. This MCP server is in anonymous mode (no ZINDEX_API_KEY set). Either set ZINDEX_API_KEY (free key at https://zindex.ai/signup) and restart the MCP host, or pass an inline `scene` to use the public render endpoint without authentication.",mode:"anonymous",hint:"dsp_validate_scene and dsp_normalize_scene also work in anonymous mode and accept the same inline scene shape."})}],isError:!0};if(r){let p=await e.getScene(r);if(!p)return{content:[{type:"text",text:JSON.stringify({error:`Scene '${r}' not found`})}],isError:!0};let I;try{I=await t.render({scene:p.scene,target:i,context:{sceneId:r,revision:p.revision,theme:a,showRevision:f!==!1,diffFromRevision:m}})}catch(M){let U=g(M);if(U)return U;throw M}let h=I.output,N=(await e.listRevisions(r).catch(()=>null))?.revisions?.length??p.revision;return{content:[{type:"text",text:JSON.stringify({mimeType:h.mimeType,...h.content?{content:h.content}:{},...h.contentBase64?{contentBase64:h.contentBase64}:{},...h.width?{width:h.width}:{},...h.height?{height:h.height}:{},...h.fileName?{fileName:h.fileName}:{},diagnostics:I.diagnostics??[],sceneId:r,revision:p.revision,revisionCount:N,hint:N>1?`Rendered scene '${r}' at revision ${p.revision} (${N} revisions). Use dsp_diff_scene to compare revisions.`:`Rendered scene '${r}' at revision ${p.revision}. Use dsp_apply_ops to make incremental edits (each edit creates a new revision).`})}]}}let k=await t.render({scene:d,target:i,context:{theme:a}}),l=k.output;return{content:[{type:"text",text:JSON.stringify({mimeType:l.mimeType,...l.content?{content:l.content}:{},...l.contentBase64?{contentBase64:l.contentBase64}:{},...l.width?{width:l.width}:{},...l.height?{height:l.height}:{},...l.fileName?{fileName:l.fileName}:{},diagnostics:k.diagnostics??[],mode:"stateless",hint:n?"Stateless render in anonymous mode (no API key). For persistence (sceneIds, revisions, diff, watermark), set ZINDEX_API_KEY (free at https://zindex.ai/signup) and use dsp_create_scene + dsp_apply_ops + dsp_render_scene with sceneId.":"Stateless render: no scene ID, no revision tracking, no watermark. For persistence, use dsp_create_scene + dsp_apply_ops + dsp_render_scene with sceneId."})}]}})}import{z as R}from"zod";function V(s,e){s.tool("dsp_get_scene","Retrieve a persisted scene by ID. Pass revision to fetch a specific historical version. By default returns the scene WITHOUT the computed layout section (bounds, edge paths) to save tokens - pass includeComputed: true only when you need layout data. Always read the latest scene before editing with dsp_apply_ops.",{sceneId:R.string(),revision:R.number().optional(),includeComputed:R.boolean().optional().default(!1)},async({sceneId:t,revision:n,includeComputed:o})=>{let r=await e.getScene(t,n);if(!r){let i=n?`Scene '${t}' revision ${n} not found`:`Scene '${t}' not found`;return{content:[{type:"text",text:JSON.stringify({error:i})}],isError:!0}}let d=o?r.scene:{...r.scene,computed:{layout:{},edgePaths:{},diagnostics:[]}};return{content:[{type:"text",text:JSON.stringify({sceneId:r.sceneId,revision:r.revision,scene:d})}]}})}import{z as ne}from"zod";function j(s,e){s.tool("dsp_normalize_scene",`Normalize a scene document - applies defaults, resolves layout, computes positions, and auto-extends scene.canvas to fit the computed bbox if necessary (emits a CANVAS_AUTO_EXTENDED info diagnostic when this happens; declared canvas dimensions are treated as a minimum, not a hard cap). Returns the result without persisting it. For production diagrams, create a persisted scene with dsp_create_scene instead (persistence gives you revision tracking and incremental edits).
|
|
42
|
+
On 'ok: false' the diagnostics array contains structured per-rule details - programmatically iterate them rather than parsing the message string.`,{scene:te.any()},async({scene:t})=>{let n=E(t,"dsp_validate_scene"),o=await e.validateScene(n);return{content:[{type:"text",text:JSON.stringify(o)}]}})}import{z as v}from"zod";function B(s,e,t){let n=e===null,o=n?"Render a scene to SVG or PNG (anonymous mode). Pass the inline scene as `scene` - the platform validates, normalizes, lays out, and renders in one call. Stateless: no scene IDs, no revisions, no diff, no watermark. For persistence (sceneIds, revisions, diff via dsp_diff_scene, watermark on every render), set ZINDEX_API_KEY and use the sceneId form (free key at https://zindex.ai/signup). Check `diagnostics` for: TEXT_OVERFLOW (resize element), CANVAS_AUTO_EXTENDED (declared canvas was auto-extended to fit content; minimum, not a hard cap), NODE_OVERLAP_DETECTED (two node rects overlap; reposition or switch to layoutStrategy), MISSING_DIAGRAM_FAMILY (declare diagramFamily on every scene), MISSING_GLYPH (label contains a codepoint Inter doesn't have; rendered as visible tofu placeholder \u25A1 instead of dropped silently), AUTOGROUP_FAMILY_MISMATCH / AUTOGROUP_SUPPRESSED_TRIVIAL / AUTOGROUP_SKIPPED_ABSOLUTE (scene.extensions.autoGroup was set but skipped - see code for the reason).":"Render a scene to SVG or PNG. Two input forms: pass `sceneId` to render a persisted scene at its current revision (with watermark and diff support, the recommended workflow after dsp_create_scene + dsp_apply_ops); or pass `scene` to render an inline scene without persisting (stateless, no watermark, no revision tracking). Persisted renders include the `Rev N - date` watermark and accept `diffFromRevision` for a coloured visual diff. Check the `diagnostics` array for TEXT_OVERFLOW (resize element), CANVAS_AUTO_EXTENDED (info; canvas was auto-extended), EDGE_LABEL_SUPPRESSED_REDUNDANT (info; an edge label matching a column in either endpoint's extensions.columns was auto-promoted to a column-row anchor, matching the dbdiagram.io / DBeaver convention - the FK edge now terminates at the named column row; set edge.style.forceLabel=true to keep the label, or set endpoint.column directly to anchor without auto-inference), EDGE_LABEL_SUPPRESSED_FANIN (info; 2+ edges share an exact label string AND a target endpoint AND come from the same conceptual source unit (same source, same parent frame with full coverage, OR a common single-step predecessor), so the platform renders the label once on the lowest-id edge per group and drops it from the others; set edge.style.forceLabel=true to keep a label on a specific edge, or rename one of the duplicates), EDGE_LABEL_FANIN_BUNDLED (info; 2+ edges share an exact label string AND a target endpoint but the source nodes are structurally distinct - geometry merges into one trunk so the convergence is visible, but every label and arrowhead is preserved because each edge represents a distinct hop with its own meaning; typical case is parallel CRUD handlers writing the same SQL operation to a shared database), EDGE_OBSTACLE_GRAZED (info; data has grazedEdgeIds and clearance - one or more edges were routed through the degraded-clearance fallback because the router could not find a route that cleared every non-source/non-target node by 16 px; recovery: widen scene.canvas, move the obstacle node, distinguish/merge the affected edges, or set style.forceLabel=true if intentional), EDGE_COLUMN_NOT_FOUND (warning; endpoint.column references a column not declared in the entity - falls back to face-centre anchoring), LABEL_DUPLICATION_DETECTED (rename one of the duplicated labels), LAYOUT_ABSOLUTE_AT_ORIGIN (info; 1+ nodes declare layout.mode='absolute' with default coordinates {x:0, y:0} - data has affectedElementIds, allAtOrigin, and layoutStrategyPresent; remove the layout block on those nodes and rely on layoutStrategy at scene level), LAYOUT_STRATEGY_OVERRIDDEN (info; data has strategyAlgorithm - scene declares layoutStrategy AND every node has explicit layout.mode='absolute' coords, so the strategy is silently ignored; pick one: REMOVE per-element layout blocks to use auto-layout, OR REMOVE layoutStrategy if pinning every node by hand), NODE_OVERLAP_DETECTED (warning; data has nodeA, nodeB, and overlap{x,y,width,height} - two leaf-node bounding boxes overlap by more than 2 px after layout completes; the renderer paints both rects so the lower one is obscured by the upper, surfacing as labels reading through other shapes or edges terminating inside a covered node; reposition one of the named nodes via updateNode, or remove the per-node absolute layout blocks and set scene.layoutStrategy for auto-placement), MISSING_DIAGRAM_FAMILY (info; the scene omits diagramFamily, which gates family-specific behaviour the engine and downstream tooling rely on; data has allowedValues; declare diagramFamily on every scene via dsp_create_scene or updateScene), MISSING_GLYPH (info; data has codepoint, weight, hex - a label contained a codepoint Inter doesn't include, typically math/logic symbols like \u2227 \u2228 \u2261 \u2200 \u2203 \u2208; rendered as visible tofu placeholder \u25A1 U+25A1 instead of being silently dropped; rewrite the label or accept the marker), AUTOGROUP_FAMILY_MISMATCH (info; scene.extensions.autoGroup was set but diagramFamily isn't 'architecture' - extension is architecture-only in v1, ignored), AUTOGROUP_SUPPRESSED_TRIVIAL (info; scene.extensions.autoGroup was set but fewer than 2 nodeType groups have 2+ members - single band is pointless visual chrome), and AUTOGROUP_SKIPPED_ABSOLUTE (info; scene.extensions.autoGroup was set but every node uses absolute layout - auto-grouping requires the layered planner to pack same-type nodes contiguously; remove per-node layout blocks and rely on layoutStrategy).";s.tool("dsp_render_scene",o,{sceneId:v.string().optional().describe(n?"Persisted-scene ID. NOT AVAILABLE in anonymous mode - this server has no ZINDEX_API_KEY set. Either set ZINDEX_API_KEY (free key at https://zindex.ai/signup) and restart the host, or pass `scene` instead for an inline render.":"Persisted-scene ID returned by dsp_create_scene. Renders the scene at its current revision with watermark + diff support. Use this for the recommended persist-first workflow."),scene:v.any().optional().describe("Inline scene document (the same shape dsp_validate_scene accepts). Use for stateless one-off renders without persisting. Available in both anonymous and authenticated modes; in authenticated mode prefer `sceneId` so you get the watermark and diff."),format:v.string().default("svg"),theme:v.string().optional().describe("Render theme: clean (default), dark, blueprint, sketch"),showRevision:v.boolean().optional().describe('Persisted-scene only. Show the full watermark: "scene-id | Rev N - date" in the bottom-right and "zindex.ai" in the bottom-left. Default true. Set to false for completely clean output - the watermark is binary (full metadata or nothing).'),diffFromRevision:v.number().optional().describe("Persisted-scene only. Render visual diff from this revision to current. Added=green, removed=red, modified=amber.")},async({sceneId:r,scene:d,format:i,theme:a,showRevision:f,diffFromRevision:m})=>{if(!r&&!d)return{content:[{type:"text",text:JSON.stringify({error:"Either `sceneId` (persisted scene) or `scene` (inline scene) must be provided."})}],isError:!0};if(r&&d)return{content:[{type:"text",text:JSON.stringify({error:"Provide `sceneId` OR `scene`, not both. Use `sceneId` for a persisted scene, `scene` for an inline render."})}],isError:!0};if(r&&n)return{content:[{type:"text",text:JSON.stringify({error:"Rendering a persisted scene by sceneId requires an API key. This MCP server is in anonymous mode (no ZINDEX_API_KEY set). Either set ZINDEX_API_KEY (free key at https://zindex.ai/signup) and restart the MCP host, or pass an inline `scene` to use the public render endpoint without authentication.",mode:"anonymous",hint:"dsp_validate_scene and dsp_normalize_scene also work in anonymous mode and accept the same inline scene shape."})}],isError:!0};if(r){let p=await e.getScene(r);if(!p)return{content:[{type:"text",text:JSON.stringify({error:`Scene '${r}' not found`})}],isError:!0};let A;try{A=await t.render({scene:p.scene,target:i,context:{sceneId:r,revision:p.revision,theme:a,showRevision:f!==!1,diffFromRevision:m}})}catch(M){let U=g(M);if(U)return U;throw M}let h=A.output,N=(await e.listRevisions(r).catch(()=>null))?.revisions?.length??p.revision;return{content:[{type:"text",text:JSON.stringify({mimeType:h.mimeType,...h.content?{content:h.content}:{},...h.contentBase64?{contentBase64:h.contentBase64}:{},...h.width?{width:h.width}:{},...h.height?{height:h.height}:{},...h.fileName?{fileName:h.fileName}:{},diagnostics:A.diagnostics??[],sceneId:r,revision:p.revision,revisionCount:N,hint:N>1?`Rendered scene '${r}' at revision ${p.revision} (${N} revisions). Use dsp_diff_scene to compare revisions.`:`Rendered scene '${r}' at revision ${p.revision}. Use dsp_apply_ops to make incremental edits (each edit creates a new revision).`})}]}}let k=await t.render({scene:d,target:i,context:{theme:a}}),l=k.output;return{content:[{type:"text",text:JSON.stringify({mimeType:l.mimeType,...l.content?{content:l.content}:{},...l.contentBase64?{contentBase64:l.contentBase64}:{},...l.width?{width:l.width}:{},...l.height?{height:l.height}:{},...l.fileName?{fileName:l.fileName}:{},diagnostics:k.diagnostics??[],mode:"stateless",hint:n?"Stateless render in anonymous mode (no API key). For persistence (sceneIds, revisions, diff, watermark), set ZINDEX_API_KEY (free at https://zindex.ai/signup) and use dsp_create_scene + dsp_apply_ops + dsp_render_scene with sceneId.":"Stateless render: no scene ID, no revision tracking, no watermark. For persistence, use dsp_create_scene + dsp_apply_ops + dsp_render_scene with sceneId."})}]}})}import{z as R}from"zod";function V(s,e){s.tool("dsp_get_scene","Retrieve a persisted scene by ID. Pass revision to fetch a specific historical version. By default returns the scene WITHOUT the computed layout section (bounds, edge paths) to save tokens - pass includeComputed: true only when you need layout data. Always read the latest scene before editing with dsp_apply_ops.",{sceneId:R.string(),revision:R.number().optional(),includeComputed:R.boolean().optional().default(!1)},async({sceneId:t,revision:n,includeComputed:o})=>{let r=await e.getScene(t,n);if(!r){let i=n?`Scene '${t}' revision ${n} not found`:`Scene '${t}' not found`;return{content:[{type:"text",text:JSON.stringify({error:i})}],isError:!0}}let d=o?r.scene:{...r.scene,computed:{layout:{},edgePaths:{},diagnostics:[]}};return{content:[{type:"text",text:JSON.stringify({sceneId:r.sceneId,revision:r.revision,scene:d})}]}})}import{z as ne}from"zod";function j(s,e){s.tool("dsp_normalize_scene",`Normalize a scene document - applies defaults, resolves layout, computes positions, and auto-extends scene.canvas to fit the computed bbox if necessary (emits a CANVAS_AUTO_EXTENDED info diagnostic when this happens; declared canvas dimensions are treated as a minimum, not a hard cap). Returns the result without persisting it. For production diagrams, create a persisted scene with dsp_create_scene instead (persistence gives you revision tracking and incremental edits).
|
|
43
|
+
|
|
44
|
+
INPUT: the 'scene' argument must be a COMPLETE inline scene document, NOT a sceneId string. If you only have a sceneId (e.g. from dsp_create_scene), fetch the document first with dsp_get_scene and pass the returned envelope here. This tool does not look up scenes by id.
|
|
40
45
|
|
|
41
|
-
The 'scene' argument
|
|
46
|
+
The 'scene' argument shape:
|
|
42
47
|
|
|
43
48
|
{
|
|
44
49
|
"schemaVersion": "0.1",
|
|
@@ -48,11 +53,12 @@ The 'scene' argument must be a COMPLETE scene document with this shape:
|
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
Common mistakes:
|
|
56
|
+
- DO NOT pass a sceneId string (e.g. "my-scene-123"); fetch with dsp_get_scene first, then pass the returned envelope.
|
|
51
57
|
- DO NOT pass just the inner scene object ({"id":...,"title":...}); pass the full envelope above.
|
|
52
58
|
- DO NOT wrap in another envelope ({scene: {...}}); the 'scene' field at this tool's input level IS the envelope.
|
|
53
59
|
- DO NOT pass a string of JSON; pass a parsed object.
|
|
54
60
|
|
|
55
|
-
Returns { scene, computed, diagnostics } where 'scene' is the normalized document with defaults filled in.`,{scene:ne.any()},async({scene:t})=>{let n=E(t,"dsp_normalize_scene"),o=await e.normalize(n);return{content:[{type:"text",text:JSON.stringify(o)}]}})}import{z as D}from"zod";function $(s,e){s.tool("dsp_diff_scene","Diff two revisions of a scene. Returns added, removed, and modified element IDs with summary counts. Use this to see what changed between any two revisions.",{sceneId:D.string(),from:D.number().describe("Starting revision number"),to:D.number().optional().describe("Ending revision number (defaults to current/latest)")},async({sceneId:t,from:n,to:o})=>{try{let r=await e.diffScenes(t,n,o);return{content:[{type:"text",text:JSON.stringify(r)}]}}catch(r){return{content:[{type:"text",text:JSON.stringify({error:r?.message??"Diff failed"})}],isError:!0}}})}import{z as se}from"zod";function q(s,e){s.tool("dsp_list_revisions","List all revisions of a scene with timestamps, messages, and change summaries. Returns a complete changelog.",{sceneId:se.string()},async({sceneId:t})=>{try{let n=await e.listRevisions(t);return{content:[{type:"text",text:JSON.stringify(n)}]}}catch(n){return{content:[{type:"text",text:JSON.stringify({error:n?.message??"List revisions failed"})}],isError:!0}}})}import{z as W}from"zod";function H(s,e){s.tool("dsp_delete_scene","Soft-delete a persisted scene. The scene is reversible for 24 hours via dsp_undelete_scene; after that the scene is permanently removed by a scheduled cron. CONFIRM WITH THE HUMAN BEFORE CALLING. Tell them which scene you're about to delete (by id and any known title) and wait for their explicit confirmation. Never delete scenes as part of a cleanup pass, batch operation, or 'let me start over' reset without per-scene human confirmation. The server enforces a rate limit of 10 deletions per workspace per minute.",{sceneId:W.string(),confirm:W.boolean().describe("Must be `true`. Set this only after the human has confirmed they want this scene deleted.")},async({sceneId:t,confirm:n})=>{if(n!==!0)return{content:[{type:"text",text:JSON.stringify({error:"dsp_delete_scene requires confirm: true. Confirm with the human first, then call again with confirm: true."})}],isError:!0};try{return await e.deleteScene(t),{content:[{type:"text",text:JSON.stringify({deleted:!0,sceneId:t,soft:!0,note:"Scene is soft-deleted. It is recoverable for 24 hours via dsp_undelete_scene. After that it is permanently removed."})}]}}catch(o){return{content:[{type:"text",text:JSON.stringify({error:o?.message??"Delete failed"})}],isError:!0}}})}import{z as oe}from"zod";function J(s,e){s.tool("dsp_undelete_scene","Restore a soft-deleted scene during the 24-hour grace window. Use this if you realize you deleted the wrong scene. After 24 hours the scene has been hard-deleted by a scheduled cron and cannot be restored - this tool returns an error in that case. Confirm with the human before calling to make sure they want the restore.",{sceneId:oe.string()},async({sceneId:t})=>{try{let n=await e.undeleteScene(t);return{content:[{type:"text",text:JSON.stringify({restored:n?.restored===!0,sceneId:n?.sceneId??t,revision:n?.revision})}]}}catch(n){return{content:[{type:"text",text:JSON.stringify({error:n?.message??"Undelete failed. The scene either never existed or its 24-hour grace window has expired."})}],isError:!0}}})}function Y(s,e){s.tool("dsp_list_recently_deleted","List scenes in the caller's workspace that were soft-deleted within the past 24 hours and are still recoverable. Returns sceneId, title (when available), deletedAt timestamp, and expiresAt (when the scene will be permanently removed by the scheduled cron). Use this when a human asks to restore a previously-deleted scene but doesn't remember the id - read the returned list back to the human, then call dsp_undelete_scene on the one they confirm. Returns an empty list when no scenes are recoverable.",{},async()=>{try{let t=await e.listRecentlyDeleted();return{content:[{type:"text",text:JSON.stringify(t??{items:[]})}]}}catch(t){return{content:[{type:"text",text:JSON.stringify({error:t?.message??"Failed to list recently-deleted scenes"})}],isError:!0}}})}import{z as P}from"zod";function K(s,e){s.tool("dsp_submit_to_support","Submit a persisted scene to Zindex support when you cannot resolve a rendering, validation, or layout issue alone. Sets the scene's privacy state to 'support' so Zindex admins can view it; the customer can withdraw at any time from the playground. Pass `anonymize: true` (recommended when acting without explicit user confirmation) to replace labels + IDs with same-width random characters before admins see the scene - visual structure is preserved so support can still diagnose layout issues without seeing the customer's actual content. Confirm with the human before calling: this exposes their scene (anonymized or raw) to Zindex support.",{sceneId:P.string().min(1,"sceneId is required"),problem:P.string().min(1,"problem must be a non-empty description of what's wrong").max(500,"problem must be 500 characters or fewer"),anonymize:P.boolean().optional().describe("When true, the platform replaces labels + IDs with same-width random characters before admins see the scene. Default false - matches the playground modal's opt-in semantics. Set true when submitting without explicit human confirmation.")},async({sceneId:t,problem:n,anonymize:o})=>{try{let r=await e.submitToSupport({sceneId:t,problem:n,anonymize:o===!0});return{content:[{type:"text",text:JSON.stringify({sceneId:r?.sceneId??t,privacyStatus:r?.privacyStatus??"support",supportSubmittedAt:r?.supportSubmittedAt??null,supportAnonymized:r?.supportAnonymized===!0,auditUrl:`https://zindex.ai/playground?id=${encodeURIComponent(t)}`})}]}}catch(r){let d=r?.message??"Submit-to-support failed.";return{content:[{type:"text",text:JSON.stringify({error:d,hint:d.includes("not found")?"Confirm the sceneId is correct + the scene is owned by the API key's workspace.":void 0})}],isError:!0}}})}import{z as re}from"zod";function Z(s,e){s.tool("dsp_publish_scene","Make a persisted scene publicly addressable at https://zindex.ai/s/<sceneId>. Anyone with the URL - including search engines and AI crawlers - can view the rendered scene and its source JSON. ALWAYS confirm with the human BEFORE calling: this exposes their scene to the public Internet. The scene's source JSON is fetchable without authentication. Use dsp_make_scene_private to revert (the public URL stops resolving immediately; the scene itself stays in the workspace). Returns the new privacy status + the public URL + the madePublicAt timestamp.",{sceneId:re.string().min(1,"sceneId is required")},async({sceneId:t})=>{try{let n=await e.publishScene(t),o=n?.sceneId??t;return{content:[{type:"text",text:JSON.stringify({sceneId:o,privacyStatus:n?.privacyStatus??"public",madePublicAt:n?.madePublicAt??null,publicUrl:`https://zindex.ai/s/${encodeURIComponent(o)}`})}]}}catch(n){let o=n?.message??"Publish failed.";return{content:[{type:"text",text:JSON.stringify({error:o,hint:/support/i.test(o)?"The API rejects support -> public transitions. Have the customer withdraw the support submission from the playground first, then re-call dsp_publish_scene.":/not found/i.test(o)?"Confirm the sceneId is correct + the scene is owned by the API key's workspace.":void 0})}],isError:!0}}})}import{z as ae}from"zod";function X(s,e){s.tool("dsp_make_scene_private","Make a persisted scene private again. The public URL (https://zindex.ai/s/<sceneId>) stops resolving immediately - the public read endpoint sets Cache-Control: no-store so revocation propagates without a TTL wait. Also withdraws a scene from Zindex support if it was previously submitted via dsp_submit_to_support (private is the canonical 'undo' for both public and support). The scene document itself is unchanged. No confirmation gate - this strictly reduces exposure.",{sceneId:ae.string().min(1,"sceneId is required")},async({sceneId:t})=>{try{let n=await e.makeScenePrivate(t);return{content:[{type:"text",text:JSON.stringify({sceneId:n?.sceneId??t,privacyStatus:n?.privacyStatus??"private"})}]}}catch(n){let o=n?.message??"Make-private failed.";return{content:[{type:"text",text:JSON.stringify({error:o,hint:/not found/i.test(o)?"Confirm the sceneId is correct + the scene is owned by the API key's workspace.":void 0})}],isError:!0}}})}var
|
|
61
|
+
Returns { scene, computed, diagnostics } where 'scene' is the normalized document with defaults filled in.`,{scene:ne.any()},async({scene:t})=>{let n=E(t,"dsp_normalize_scene"),o=await e.normalize(n);return{content:[{type:"text",text:JSON.stringify(o)}]}})}import{z as D}from"zod";function $(s,e){s.tool("dsp_diff_scene","Diff two revisions of a scene. Returns added, removed, and modified element IDs with summary counts. Use this to see what changed between any two revisions.",{sceneId:D.string(),from:D.number().describe("Starting revision number"),to:D.number().optional().describe("Ending revision number (defaults to current/latest)")},async({sceneId:t,from:n,to:o})=>{try{let r=await e.diffScenes(t,n,o);return{content:[{type:"text",text:JSON.stringify(r)}]}}catch(r){return{content:[{type:"text",text:JSON.stringify({error:r?.message??"Diff failed"})}],isError:!0}}})}import{z as se}from"zod";function q(s,e){s.tool("dsp_list_revisions","List all revisions of a scene with timestamps, messages, and change summaries. Returns a complete changelog.",{sceneId:se.string()},async({sceneId:t})=>{try{let n=await e.listRevisions(t);return{content:[{type:"text",text:JSON.stringify(n)}]}}catch(n){return{content:[{type:"text",text:JSON.stringify({error:n?.message??"List revisions failed"})}],isError:!0}}})}import{z as W}from"zod";function H(s,e){s.tool("dsp_delete_scene","Soft-delete a persisted scene. The scene is reversible for 24 hours via dsp_undelete_scene; after that the scene is permanently removed by a scheduled cron. CONFIRM WITH THE HUMAN BEFORE CALLING. Tell them which scene you're about to delete (by id and any known title) and wait for their explicit confirmation. Never delete scenes as part of a cleanup pass, batch operation, or 'let me start over' reset without per-scene human confirmation. The server enforces a rate limit of 10 deletions per workspace per minute.",{sceneId:W.string(),confirm:W.boolean().describe("Must be `true`. Set this only after the human has confirmed they want this scene deleted.")},async({sceneId:t,confirm:n})=>{if(n!==!0)return{content:[{type:"text",text:JSON.stringify({error:"dsp_delete_scene requires confirm: true. Confirm with the human first, then call again with confirm: true."})}],isError:!0};try{return await e.deleteScene(t),{content:[{type:"text",text:JSON.stringify({deleted:!0,sceneId:t,soft:!0,note:"Scene is soft-deleted. It is recoverable for 24 hours via dsp_undelete_scene. After that it is permanently removed."})}]}}catch(o){return{content:[{type:"text",text:JSON.stringify({error:o?.message??"Delete failed"})}],isError:!0}}})}import{z as oe}from"zod";function J(s,e){s.tool("dsp_undelete_scene","Restore a soft-deleted scene during the 24-hour grace window. Use this if you realize you deleted the wrong scene. After 24 hours the scene has been hard-deleted by a scheduled cron and cannot be restored - this tool returns an error in that case. Confirm with the human before calling to make sure they want the restore.",{sceneId:oe.string()},async({sceneId:t})=>{try{let n=await e.undeleteScene(t);return{content:[{type:"text",text:JSON.stringify({restored:n?.restored===!0,sceneId:n?.sceneId??t,revision:n?.revision})}]}}catch(n){return{content:[{type:"text",text:JSON.stringify({error:n?.message??"Undelete failed. The scene either never existed or its 24-hour grace window has expired."})}],isError:!0}}})}function Y(s,e){s.tool("dsp_list_recently_deleted","List scenes in the caller's workspace that were soft-deleted within the past 24 hours and are still recoverable. Returns sceneId, title (when available), deletedAt timestamp, and expiresAt (when the scene will be permanently removed by the scheduled cron). Use this when a human asks to restore a previously-deleted scene but doesn't remember the id - read the returned list back to the human, then call dsp_undelete_scene on the one they confirm. Returns an empty list when no scenes are recoverable.",{},async()=>{try{let t=await e.listRecentlyDeleted();return{content:[{type:"text",text:JSON.stringify(t??{items:[]})}]}}catch(t){return{content:[{type:"text",text:JSON.stringify({error:t?.message??"Failed to list recently-deleted scenes"})}],isError:!0}}})}import{z as P}from"zod";function K(s,e){s.tool("dsp_submit_to_support","Submit a persisted scene to Zindex support when you cannot resolve a rendering, validation, or layout issue alone. Sets the scene's privacy state to 'support' so Zindex admins can view it; the customer can withdraw at any time from the playground. Pass `anonymize: true` (recommended when acting without explicit user confirmation) to replace labels + IDs with same-width random characters before admins see the scene - visual structure is preserved so support can still diagnose layout issues without seeing the customer's actual content. Confirm with the human before calling: this exposes their scene (anonymized or raw) to Zindex support.",{sceneId:P.string().min(1,"sceneId is required"),problem:P.string().min(1,"problem must be a non-empty description of what's wrong").max(500,"problem must be 500 characters or fewer"),anonymize:P.boolean().optional().describe("When true, the platform replaces labels + IDs with same-width random characters before admins see the scene. Default false - matches the playground modal's opt-in semantics. Set true when submitting without explicit human confirmation.")},async({sceneId:t,problem:n,anonymize:o})=>{try{let r=await e.submitToSupport({sceneId:t,problem:n,anonymize:o===!0});return{content:[{type:"text",text:JSON.stringify({sceneId:r?.sceneId??t,privacyStatus:r?.privacyStatus??"support",supportSubmittedAt:r?.supportSubmittedAt??null,supportAnonymized:r?.supportAnonymized===!0,auditUrl:`https://zindex.ai/playground?id=${encodeURIComponent(t)}`})}]}}catch(r){let d=r?.message??"Submit-to-support failed.";return{content:[{type:"text",text:JSON.stringify({error:d,hint:d.includes("not found")?"Confirm the sceneId is correct + the scene is owned by the API key's workspace.":void 0})}],isError:!0}}})}import{z as re}from"zod";function Z(s,e){s.tool("dsp_publish_scene","Make a persisted scene publicly addressable at https://zindex.ai/s/<sceneId>. Anyone with the URL - including search engines and AI crawlers - can view the rendered scene and its source JSON. ALWAYS confirm with the human BEFORE calling: this exposes their scene to the public Internet. The scene's source JSON is fetchable without authentication. Use dsp_make_scene_private to revert (the public URL stops resolving immediately; the scene itself stays in the workspace). Returns the new privacy status + the public URL + the madePublicAt timestamp.",{sceneId:re.string().min(1,"sceneId is required")},async({sceneId:t})=>{try{let n=await e.publishScene(t),o=n?.sceneId??t;return{content:[{type:"text",text:JSON.stringify({sceneId:o,privacyStatus:n?.privacyStatus??"public",madePublicAt:n?.madePublicAt??null,publicUrl:`https://zindex.ai/s/${encodeURIComponent(o)}`})}]}}catch(n){let o=n?.message??"Publish failed.";return{content:[{type:"text",text:JSON.stringify({error:o,hint:/support/i.test(o)?"The API rejects support -> public transitions. Have the customer withdraw the support submission from the playground first, then re-call dsp_publish_scene.":/not found/i.test(o)?"Confirm the sceneId is correct + the scene is owned by the API key's workspace.":void 0})}],isError:!0}}})}import{z as ae}from"zod";function X(s,e){s.tool("dsp_make_scene_private","Make a persisted scene private again. The public URL (https://zindex.ai/s/<sceneId>) stops resolving immediately - the public read endpoint sets Cache-Control: no-store so revocation propagates without a TTL wait. Also withdraws a scene from Zindex support if it was previously submitted via dsp_submit_to_support (private is the canonical 'undo' for both public and support). The scene document itself is unchanged. No confirmation gate - this strictly reduces exposure.",{sceneId:ae.string().min(1,"sceneId is required")},async({sceneId:t})=>{try{let n=await e.makeScenePrivate(t);return{content:[{type:"text",text:JSON.stringify({sceneId:n?.sceneId??t,privacyStatus:n?.privacyStatus??"private"})}]}}catch(n){let o=n?.message??"Make-private failed.";return{content:[{type:"text",text:JSON.stringify({error:o,hint:/not found/i.test(o)?"Confirm the sceneId is correct + the scene is owned by the API key's workspace.":void 0})}],isError:!0}}})}var I=JSON.parse(ie(le(de(L(import.meta.url)),"..","package.json"),"utf8")).version,he=`Zindex MCP server v${I}
|
|
56
62
|
|
|
57
63
|
A thin MCP client for the Zindex API. Exposes tools for the full DSP
|
|
58
64
|
protocol - scene lifecycle (create, apply ops, render), validation,
|
|
@@ -301,5 +307,5 @@ The anonymize parameter on dsp_submit_to_support is the agent-layer expression o
|
|
|
301
307
|
- Layout engine details: https://zindex.ai/docs/reference/layout-engine/
|
|
302
308
|
- Element types reference: https://zindex.ai/docs/reference/element-types/
|
|
303
309
|
- Privacy commitments: https://zindex.ai/privacy
|
|
304
|
-
`.trim();function fe(s){let e=s?.services??T(),{sceneService:t,renderService:n,validator:o,normalizeService:r,mode:d}=e,i=new ue({name:"zindex",version:
|
|
305
|
-
`),process.exit(0));let e=T(),{server:t,toolCount:n}=fe({services:e}),o=new pe;t.connect(o).then(()=>{let r=L(import.meta.url),d=e.mode==="authenticated"?`authenticated mode (${n} tools)`:`anonymous mode (${n} public tools: dsp_validate_scene, dsp_normalize_scene, dsp_render_scene). For persisted scenes (sceneIds, revisions, diff, full MCP tool set) set ZINDEX_API_KEY - free key at https://zindex.ai/signup`;console.error(`Zindex MCP server v${
|
|
310
|
+
`.trim();function fe(s){let e=s?.services??T(),{sceneService:t,renderService:n,validator:o,normalizeService:r,mode:d}=e,i=new ue({name:"zindex",version:I},{instructions:me}),a=0;return G(i,o),a++,j(i,r),a++,B(i,t,n),a++,d==="authenticated"&&t&&(C(i,t),a++,F(i,t),a++,V(i,t),a++,$(i,t),a++,q(i,t),a++,H(i,t),a++,J(i,t),a++,Y(i,t),a++,K(i,t),a++,Z(i,t),a++,X(i,t),a++),{server:i,services:e,toolCount:a}}function ge(){let s=process.argv.slice(2);(s.includes("--help")||s.includes("-h"))&&(process.stdout.write(he),process.exit(0)),(s.includes("--version")||s.includes("-v"))&&(process.stdout.write(`${I}
|
|
311
|
+
`),process.exit(0));let e=T(),{server:t,toolCount:n}=fe({services:e}),o=new pe;t.connect(o).then(()=>{let r=L(import.meta.url),d=e.mode==="authenticated"?`authenticated mode (${n} tools)`:`anonymous mode (${n} public tools: dsp_validate_scene, dsp_normalize_scene, dsp_render_scene). For persisted scenes (sceneIds, revisions, diff, full MCP tool set) set ZINDEX_API_KEY - free key at https://zindex.ai/signup`;console.error(`Zindex MCP server v${I} running on stdio [connected to ${e.apiUrl}, ${d}]`),console.error(` Binary: ${r}`)})}function ye(){let s=process.argv[1];if(s===void 0)return!1;let e=L(import.meta.url);if(s===e)return!0;try{return ce(s)===e}catch{return!1}}ye()&&ge();export{S as HttpNormalizeService,b as HttpRenderService,w as HttpSceneService,_ as HttpValidator,O as MissingApiKeyError,fe as createMcpServer,T as createServices};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zindex-ai/mcp",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.13",
|
|
4
4
|
"description": "MCP server for Zindex - agent-native diagram state infrastructure. A thin HTTP client exposing tools (create, patch, validate, normalize, diff, render, list-revisions, get-scene, delete-scene, undelete-scene, list-recently-deleted, submit-to-support, publish-scene, make-scene-private) backed by the Zindex API. ZINDEX_API_KEY is optional: with a key the server runs in authenticated mode (full MCP tool set, persisted scenes with revisions and diff); without a key it runs in anonymous mode and exposes the public-endpoint tools (dsp_validate_scene, dsp_normalize_scene, dsp_render_scene with inline scenes). Free key at https://zindex.ai/signup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|