@excaliwow/mcp 0.2.1 → 0.4.0
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/README.md +33 -22
- package/dist/bin.js +107 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,9 +8,10 @@ client (Claude Desktop, Claude Code, etc.) can launch it with `npx`.
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
10
|
First mint a Personal Access Token at https://excaliwow.com/app/settings
|
|
11
|
-
(Settings → Developer / API tokens) with **`read` + `write`** capabilities —
|
|
12
|
-
|
|
13
|
-
Pass it as
|
|
11
|
+
(Settings → Developer / API tokens) with **`read` + `write`** capabilities —
|
|
12
|
+
enough for six of the eight tools. Add **`delete`** only if you want the agent
|
|
13
|
+
to trash and restore diagrams (see [Security notes](#security-notes)). Pass it as
|
|
14
|
+
`EXCALIWOW_TOKEN`.
|
|
14
15
|
|
|
15
16
|
### Claude Code (CLI)
|
|
16
17
|
|
|
@@ -36,7 +37,7 @@ token to your account:
|
|
|
36
37
|
"mcpServers": {
|
|
37
38
|
"excaliwow": {
|
|
38
39
|
"command": "npx",
|
|
39
|
-
"args": ["-y", "@excaliwow/mcp@0.
|
|
40
|
+
"args": ["-y", "@excaliwow/mcp@0.4.0"],
|
|
40
41
|
"env": {
|
|
41
42
|
"EXCALIWOW_TOKEN": "excw_pat_…"
|
|
42
43
|
}
|
|
@@ -52,7 +53,7 @@ you've reviewed a new release.
|
|
|
52
53
|
|
|
53
54
|
## Troubleshooting
|
|
54
55
|
|
|
55
|
-
**`npx -y @excaliwow/mcp@0.
|
|
56
|
+
**`npx -y @excaliwow/mcp@0.4.0` fails with `ENOENT … /@excaliwow/mcp@0.4.0/package.json`.**
|
|
56
57
|
On some npm/Node versions, `npx` misreads a scoped package + `@version` spec as a
|
|
57
58
|
local directory. It's an upstream npm bug (it reproduces with other scoped
|
|
58
59
|
packages, e.g. `@modelcontextprotocol/server-filesystem@1.0.0`), not an Excaliwow one. Either
|
|
@@ -61,7 +62,7 @@ above does), or pin safely by installing once and pointing the client at the
|
|
|
61
62
|
binary:
|
|
62
63
|
|
|
63
64
|
```sh
|
|
64
|
-
npm i -g @excaliwow/mcp@0.
|
|
65
|
+
npm i -g @excaliwow/mcp@0.4.0
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
```json
|
|
@@ -99,20 +100,27 @@ npx -y @excaliwow/mcp --health
|
|
|
99
100
|
|
|
100
101
|
## Tools
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
Eight tools, scoped to safe agent use:
|
|
103
104
|
|
|
104
|
-
| Tool
|
|
105
|
-
|
|
|
106
|
-
| `generate_diagram`
|
|
107
|
-
| `read_diagram`
|
|
108
|
-
| `list_diagrams`
|
|
109
|
-
| `move_diagram`
|
|
110
|
-
| `edit_diagram`
|
|
105
|
+
| Tool | Capability | What it does |
|
|
106
|
+
| -------------------- | ---------- | ----------------------------------------------------------------------------- |
|
|
107
|
+
| `generate_diagram` | `write` | Create a diagram from the high-level node/edge DSL; returns the editor URL. |
|
|
108
|
+
| `read_diagram` | `read` | Compact summary (title + per-type element counts) **plus** a rendered PNG. |
|
|
109
|
+
| `list_diagrams` | `read` | Page through your diagrams (`filter: active \| trash`). |
|
|
110
|
+
| `move_diagram` | `write` | Move a diagram to a folder (or to root). |
|
|
111
|
+
| `edit_diagram` | `write` | Additively merge a DSL fragment (add nodes/edges, update node style/label). |
|
|
112
|
+
| `regenerate_diagram` | `write` | Replace a diagram's contents in place from a fresh spec (re-layout, same id). |
|
|
113
|
+
| `trash_diagram` | `delete` | Soft-delete a diagram to trash. **Reversible** (see `restore_diagram`). |
|
|
114
|
+
| `restore_diagram` | `delete` | Restore a trashed diagram, reopening it at its original id and URL. |
|
|
111
115
|
|
|
112
116
|
`read_diagram` returns a summary + image, **never** the raw scene JSON, to keep
|
|
113
|
-
context small.
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
context small. `trash_diagram` / `restore_diagram` are a **reversible** pair
|
|
118
|
+
gated on the `delete` capability — registered always, they return a clean
|
|
119
|
+
`insufficient_scope` error (changing nothing) unless the token carries `delete`,
|
|
120
|
+
so a `read` + `write` token can't trash anything. Hard-delete/purge and making a
|
|
121
|
+
diagram publicly shareable are deliberately **not** agent tools — those are
|
|
122
|
+
irreversible, so a misled agent can't destroy or expose your diagram. Purge or
|
|
123
|
+
publish from the dashboard or the CLI.
|
|
116
124
|
|
|
117
125
|
### DSL discovery
|
|
118
126
|
|
|
@@ -134,11 +142,14 @@ resource at **`excaliwow://dsl/reference`**.
|
|
|
134
142
|
project`) puts the token into git history. Use a user- or local-scoped config
|
|
135
143
|
(`--scope local`), or reference an environment variable instead of pasting the
|
|
136
144
|
literal token.
|
|
137
|
-
- **Mint with
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
- **Mint with `read` + `write` (add `delete` only if you want trash/restore).**
|
|
146
|
+
Six of the eight tools need just `read` + `write`; a `read` + `write` PAT can
|
|
147
|
+
neither expose your diagrams publicly nor delete them, even if the agent is
|
|
148
|
+
misled (`trash_diagram` / `restore_diagram` simply return `insufficient_scope`
|
|
149
|
+
and do nothing). Add the `delete` capability only if you want the agent to be
|
|
150
|
+
able to soft-delete and restore — it's reversible, but it's still a capability
|
|
151
|
+
to grant deliberately. Pick capabilities specifically rather than the coarse
|
|
152
|
+
`read-write` preset, which additionally grants `publish` (and `delete`).
|
|
142
153
|
- **Pin the version** in your client config rather than floating on `@latest`,
|
|
143
154
|
and review release notes before bumping.
|
|
144
155
|
- The token is a credential to your account. It lives only in your MCP client
|
package/dist/bin.js
CHANGED
|
@@ -205,6 +205,36 @@ function moveDiagram(ctx, id, folderId) {
|
|
|
205
205
|
fetchImpl: ctx.fetchImpl
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
|
+
function deleteDiagram(ctx, id) {
|
|
209
|
+
return request({
|
|
210
|
+
method: "DELETE",
|
|
211
|
+
path: `${PREFIX}/diagrams/${encodeURIComponent(id)}`,
|
|
212
|
+
token: ctx.token,
|
|
213
|
+
baseUrl: ctx.baseUrl,
|
|
214
|
+
fetchImpl: ctx.fetchImpl
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function restoreDiagram(ctx, id) {
|
|
218
|
+
return request({
|
|
219
|
+
method: "POST",
|
|
220
|
+
path: `${PREFIX}/diagrams/${encodeURIComponent(id)}/restore`,
|
|
221
|
+
token: ctx.token,
|
|
222
|
+
baseUrl: ctx.baseUrl,
|
|
223
|
+
fetchImpl: ctx.fetchImpl
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function regenerateDiagram(ctx, id, args) {
|
|
227
|
+
const body = { spec: args.spec };
|
|
228
|
+
if (args.title !== void 0) body.title = args.title;
|
|
229
|
+
return request({
|
|
230
|
+
method: "POST",
|
|
231
|
+
path: `${PREFIX}/diagrams/${encodeURIComponent(id)}/regenerate`,
|
|
232
|
+
token: ctx.token,
|
|
233
|
+
baseUrl: ctx.baseUrl,
|
|
234
|
+
body,
|
|
235
|
+
fetchImpl: ctx.fetchImpl
|
|
236
|
+
});
|
|
237
|
+
}
|
|
208
238
|
|
|
209
239
|
// src/server.ts
|
|
210
240
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -427,7 +457,7 @@ var EditFragmentZ = z.object({
|
|
|
427
457
|
});
|
|
428
458
|
|
|
429
459
|
// src/version.ts
|
|
430
|
-
var VERSION = "0.
|
|
460
|
+
var VERSION = "0.4.0";
|
|
431
461
|
|
|
432
462
|
// src/server.ts
|
|
433
463
|
function textResult(text) {
|
|
@@ -625,6 +655,82 @@ preview fidelity: ${png.quality} \u2014 faithful renderer unavailable; layout/fo
|
|
|
625
655
|
}
|
|
626
656
|
}
|
|
627
657
|
);
|
|
658
|
+
server2.registerTool(
|
|
659
|
+
"trash_diagram",
|
|
660
|
+
{
|
|
661
|
+
title: "Trash a diagram (soft delete)",
|
|
662
|
+
description: "Soft-delete a diagram to trash. REVERSIBLE \u2014 restore_diagram brings it back, and it stays visible under list_diagrams with filter='trash'. Use this to clean up diagrams you no longer want (e.g. earlier drafts from iterating). Requires a Personal Access Token with the `delete` capability; without it the call returns a clean 'insufficient_scope' error and changes nothing. Idempotent on an already-trashed id.",
|
|
663
|
+
inputSchema: { id: z2.string() }
|
|
664
|
+
},
|
|
665
|
+
async ({ id }) => {
|
|
666
|
+
try {
|
|
667
|
+
const ctx = buildCtx(deps);
|
|
668
|
+
await deleteDiagram(ctx, id);
|
|
669
|
+
return textResult(JSON.stringify({ id, trashed: true }));
|
|
670
|
+
} catch (err) {
|
|
671
|
+
return toErrorResult(err);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
server2.registerTool(
|
|
676
|
+
"restore_diagram",
|
|
677
|
+
{
|
|
678
|
+
title: "Restore a diagram from trash",
|
|
679
|
+
description: "Restore a soft-deleted diagram from trash, reopening it in the editor at its original id and url. The inverse of trash_diagram. Requires a Personal Access Token with the `delete` capability; without it the call returns a clean 'insufficient_scope' error and changes nothing. Idempotent on an already-active id.",
|
|
680
|
+
inputSchema: { id: z2.string() }
|
|
681
|
+
},
|
|
682
|
+
async ({ id }) => {
|
|
683
|
+
try {
|
|
684
|
+
const ctx = buildCtx(deps);
|
|
685
|
+
await restoreDiagram(ctx, id);
|
|
686
|
+
return textResult(JSON.stringify({ id, restored: true }));
|
|
687
|
+
} catch (err) {
|
|
688
|
+
return toErrorResult(err);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
);
|
|
692
|
+
server2.registerTool(
|
|
693
|
+
"regenerate_diagram",
|
|
694
|
+
{
|
|
695
|
+
title: "Regenerate a diagram (replace in place)",
|
|
696
|
+
description: [
|
|
697
|
+
"REPLACE an existing diagram's entire contents from a fresh DiagramSpec: the spec is",
|
|
698
|
+
"re-laid-out server-side and swapped in, keeping the SAME id and url. Use this to ITERATE",
|
|
699
|
+
"on one diagram (fix layout, restructure) instead of generate_diagram, which mints a NEW",
|
|
700
|
+
"id+url each time and orphans the old one. Unlike edit_diagram (which only appends/patches),",
|
|
701
|
+
"this discards the old scene entirely. Comments anchored to a shape the new layout no longer",
|
|
702
|
+
"contains are detached (returned as orphanedComments); comments on shapes whose node id you",
|
|
703
|
+
"reuse stay attached. Pass an optional title to rename in the same call. Returns the id, the",
|
|
704
|
+
"new element count (replaced), orphanedComments, and the editor url.",
|
|
705
|
+
"",
|
|
706
|
+
COMPACT_GRAMMAR,
|
|
707
|
+
"",
|
|
708
|
+
"See the `" + DSL_REFERENCE_URI + "` resource for the full DSL reference."
|
|
709
|
+
].join("\n"),
|
|
710
|
+
inputSchema: {
|
|
711
|
+
id: z2.string(),
|
|
712
|
+
title: z2.string().nullish(),
|
|
713
|
+
spec: DiagramSpecZ
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
async ({ id, title, spec }) => {
|
|
717
|
+
try {
|
|
718
|
+
const ctx = buildCtx(deps);
|
|
719
|
+
const result = await regenerateDiagram(ctx, id, { spec, title: title ?? void 0 });
|
|
720
|
+
return textResult(
|
|
721
|
+
JSON.stringify({
|
|
722
|
+
id: result.id,
|
|
723
|
+
replaced: result.replaced,
|
|
724
|
+
orphanedComments: result.orphanedComments,
|
|
725
|
+
...result.title !== void 0 ? { title: result.title } : {},
|
|
726
|
+
url: editorUrl(ctx, result.id)
|
|
727
|
+
})
|
|
728
|
+
);
|
|
729
|
+
} catch (err) {
|
|
730
|
+
return toErrorResult(err);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
);
|
|
628
734
|
server2.registerResource(
|
|
629
735
|
"dsl-reference",
|
|
630
736
|
DSL_REFERENCE_URI,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@excaliwow/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Excaliwow Model Context Protocol (MCP) server — lets AI agents create, read, render, and edit Excaliwow diagrams over the public REST API, via stdio.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|