@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.
Files changed (3) hide show
  1. package/README.md +33 -22
  2. package/dist/bin.js +107 -1
  3. 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 — that
12
- is everything the server's tools need (see [Security notes](#security-notes)).
13
- Pass it as `EXCALIWOW_TOKEN`.
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.2.1"],
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.2.1` fails with `ENOENT … /@excaliwow/mcp@0.2.1/package.json`.**
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.2.1
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
- Five tools, scoped to safe agent use:
103
+ Eight tools, scoped to safe agent use:
103
104
 
104
- | Tool | Capability | What it does |
105
- | ------------------ | ---------- | --------------------------------------------------------------------------- |
106
- | `generate_diagram` | `write` | Create a diagram from the high-level node/edge DSL; returns the editor URL. |
107
- | `read_diagram` | `read` | Compact summary (title + per-type element counts) **plus** a rendered PNG. |
108
- | `list_diagrams` | `read` | Page through your diagrams. |
109
- | `move_diagram` | `write` | Move a diagram to a folder (or to root). |
110
- | `edit_diagram` | `write` | Additively merge a DSL fragment (add nodes/edges, update node style/label). |
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. Making a diagram publicly shareable is deliberately **not** an
114
- agent tool so a misled agent can't be tricked into exposing your diagram. Use
115
- the dashboard or the CLI to publish.
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 only `read` + `write`.** Those are the only capabilities the five
138
- tools use (see the table above); none publish or delete. So a `read` + `write`
139
- PAT can neither expose your diagrams publicly nor delete them, even if the
140
- agent is misled. Pick those two capabilities specifically — the coarse
141
- `read-write` preset additionally grants `publish` and `delete`.
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.2.1";
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.2.1",
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": {