@excaliwow/mcp 0.2.0 → 0.2.1
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 +90 -21
- package/dist/bin.js +86 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,25 @@ client (Claude Desktop, Claude Code, etc.) can launch it with `npx`.
|
|
|
7
7
|
|
|
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`.
|
|
14
|
+
|
|
15
|
+
### Claude Code (CLI)
|
|
16
|
+
|
|
17
|
+
One command. `--scope local` stores the server in your own settings, so the
|
|
18
|
+
token never lands in a file you might commit:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
claude mcp add excaliwow --scope local \
|
|
22
|
+
--env EXCALIWOW_TOKEN=excw_pat_… \
|
|
23
|
+
-- npx -y @excaliwow/mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Claude Desktop (and other JSON-config clients)
|
|
27
|
+
|
|
28
|
+
Add it to your client's config file (e.g. Claude Desktop's
|
|
11
29
|
`claude_desktop_config.json`, or your client's MCP settings — see your client's
|
|
12
30
|
MCP setup docs). **Pin the version** — `npx -y` otherwise always pulls the newest
|
|
13
31
|
release, which is an avoidable supply-chain surface for a process that holds a
|
|
@@ -18,7 +36,7 @@ token to your account:
|
|
|
18
36
|
"mcpServers": {
|
|
19
37
|
"excaliwow": {
|
|
20
38
|
"command": "npx",
|
|
21
|
-
"args": ["-y", "@excaliwow/mcp@0.
|
|
39
|
+
"args": ["-y", "@excaliwow/mcp@0.2.1"],
|
|
22
40
|
"env": {
|
|
23
41
|
"EXCALIWOW_TOKEN": "excw_pat_…"
|
|
24
42
|
}
|
|
@@ -27,25 +45,69 @@ token to your account:
|
|
|
27
45
|
}
|
|
28
46
|
```
|
|
29
47
|
|
|
30
|
-
`EXCALIWOW_TOKEN`
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
The server reads `EXCALIWOW_TOKEN` per call from the environment (or, if you also
|
|
49
|
+
use `@excaliwow/cli` and have run `excaliwow auth login`, that stored login) and
|
|
50
|
+
never writes the token to disk itself. Bump the pinned version deliberately when
|
|
51
|
+
you've reviewed a new release.
|
|
52
|
+
|
|
53
|
+
## Troubleshooting
|
|
54
|
+
|
|
55
|
+
**`npx -y @excaliwow/mcp@0.2.1` fails with `ENOENT … /@excaliwow/mcp@0.2.1/package.json`.**
|
|
56
|
+
On some npm/Node versions, `npx` misreads a scoped package + `@version` spec as a
|
|
57
|
+
local directory. It's an upstream npm bug (it reproduces with other scoped
|
|
58
|
+
packages, e.g. `@modelcontextprotocol/server-filesystem@1.0.0`), not an Excaliwow one. Either
|
|
59
|
+
use the unversioned spec `npx -y @excaliwow/mcp` (as the Claude Code command
|
|
60
|
+
above does), or pin safely by installing once and pointing the client at the
|
|
61
|
+
binary:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
npm i -g @excaliwow/mcp@0.2.1
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"excaliwow": {
|
|
71
|
+
"command": "excaliwow-mcp",
|
|
72
|
+
"env": { "EXCALIWOW_TOKEN": "excw_pat_…" }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
With Claude Code: `claude mcp add excaliwow --scope local --env EXCALIWOW_TOKEN=… -- excaliwow-mcp`.
|
|
79
|
+
|
|
80
|
+
**"Not authenticated" / 401 / the agent's tool calls fail.** The server starts
|
|
81
|
+
even without a token (so it can list its tools), so a missing or invalid
|
|
82
|
+
`EXCALIWOW_TOKEN` only surfaces when the agent first calls a tool. Starting with
|
|
83
|
+
no token prints a one-line `EXCALIWOW_TOKEN is not set` warning to **stderr**. To
|
|
84
|
+
check a token directly, run the health probe — it makes one authenticated read
|
|
85
|
+
and prints a clear verdict (`ok`, `401 — token is invalid or expired`, or
|
|
86
|
+
unreachable):
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
npx -y @excaliwow/mcp --health
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### CLI flags
|
|
93
|
+
|
|
94
|
+
| Flag | Effect |
|
|
95
|
+
| ----------- | ------------------------------------------------------------------ |
|
|
96
|
+
| `--health` | Check the token + API reachability, then exit (0 ok, 1 bad token). |
|
|
97
|
+
| `--version` | Print the installed version and exit. |
|
|
98
|
+
| `--help` | Print usage (flags + env vars) and exit. |
|
|
37
99
|
|
|
38
100
|
## Tools
|
|
39
101
|
|
|
40
102
|
Five tools, scoped to safe agent use:
|
|
41
103
|
|
|
42
|
-
| Tool | What it does |
|
|
43
|
-
| ------------------ | --------------------------------------------------------------------------- |
|
|
44
|
-
| `generate_diagram` | Create a diagram from the high-level node/edge DSL; returns the editor URL. |
|
|
45
|
-
| `read_diagram` | Compact summary (title + per-type element counts) **plus** a rendered PNG. |
|
|
46
|
-
| `list_diagrams` | Page through your diagrams. |
|
|
47
|
-
| `move_diagram` | Move a diagram to a folder (or to root). |
|
|
48
|
-
| `edit_diagram` | Additively merge a DSL fragment (add nodes/edges, update node style/label). |
|
|
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). |
|
|
49
111
|
|
|
50
112
|
`read_diagram` returns a summary + image, **never** the raw scene JSON, to keep
|
|
51
113
|
context small. Making a diagram publicly shareable is deliberately **not** an
|
|
@@ -67,14 +129,21 @@ resource at **`excaliwow://dsl/reference`**.
|
|
|
67
129
|
|
|
68
130
|
## Security notes
|
|
69
131
|
|
|
70
|
-
- **
|
|
71
|
-
|
|
132
|
+
- **Keep the token out of anything you commit.** A project-scoped config that
|
|
133
|
+
lives in the repo (a committed `.mcp.json`, or `claude mcp add --scope
|
|
134
|
+
project`) puts the token into git history. Use a user- or local-scoped config
|
|
135
|
+
(`--scope local`), or reference an environment variable instead of pasting the
|
|
136
|
+
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`.
|
|
142
|
+
- **Pin the version** in your client config rather than floating on `@latest`,
|
|
143
|
+
and review release notes before bumping.
|
|
72
144
|
- The token is a credential to your account. It lives only in your MCP client
|
|
73
145
|
config / environment; this server does not persist it. Treat that config the
|
|
74
146
|
way you'd treat any secrets file.
|
|
75
|
-
- The token authorizes the same operations as a `read-write` PAT used by the
|
|
76
|
-
REST API and CLI — there is no separate per-capability scope, so treat it as
|
|
77
|
-
full read-write access to your account.
|
|
78
147
|
|
|
79
148
|
## License
|
|
80
149
|
|
package/dist/bin.js
CHANGED
|
@@ -3,10 +3,6 @@
|
|
|
3
3
|
// src/bin.ts
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
|
|
6
|
-
// src/server.ts
|
|
7
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { z as z2 } from "zod";
|
|
9
|
-
|
|
10
6
|
// ../core/src/config.ts
|
|
11
7
|
import { chmodSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
12
8
|
import { homedir } from "os";
|
|
@@ -210,6 +206,10 @@ function moveDiagram(ctx, id, folderId) {
|
|
|
210
206
|
});
|
|
211
207
|
}
|
|
212
208
|
|
|
209
|
+
// src/server.ts
|
|
210
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
211
|
+
import { z as z2 } from "zod";
|
|
212
|
+
|
|
213
213
|
// src/bootstrap.ts
|
|
214
214
|
var DSL_BOOTSTRAP_EXAMPLE = {
|
|
215
215
|
version: "1",
|
|
@@ -426,6 +426,9 @@ var EditFragmentZ = z.object({
|
|
|
426
426
|
updateNodes: z.array(UpdateNodePatchZ).optional()
|
|
427
427
|
});
|
|
428
428
|
|
|
429
|
+
// src/version.ts
|
|
430
|
+
var VERSION = "0.2.1";
|
|
431
|
+
|
|
429
432
|
// src/server.ts
|
|
430
433
|
function textResult(text) {
|
|
431
434
|
return { content: [{ type: "text", text }] };
|
|
@@ -467,7 +470,7 @@ function elementBreakdown(detail) {
|
|
|
467
470
|
return { total: elements.length, byType };
|
|
468
471
|
}
|
|
469
472
|
function createServer(deps) {
|
|
470
|
-
const server2 = new McpServer({ name: "excaliwow", version:
|
|
473
|
+
const server2 = new McpServer({ name: "excaliwow", version: VERSION });
|
|
471
474
|
server2.registerTool(
|
|
472
475
|
"generate_diagram",
|
|
473
476
|
{
|
|
@@ -640,5 +643,83 @@ preview fidelity: ${png.quality} \u2014 faithful renderer unavailable; layout/fo
|
|
|
640
643
|
}
|
|
641
644
|
|
|
642
645
|
// src/bin.ts
|
|
646
|
+
var argv = process.argv.slice(2);
|
|
647
|
+
var has = (...flags) => argv.some((a) => flags.includes(a));
|
|
648
|
+
if (has("--version", "-v")) {
|
|
649
|
+
process.stdout.write(`${VERSION}
|
|
650
|
+
`);
|
|
651
|
+
process.exit(0);
|
|
652
|
+
}
|
|
653
|
+
if (has("--help", "-h")) {
|
|
654
|
+
process.stdout.write(
|
|
655
|
+
[
|
|
656
|
+
`excaliwow-mcp ${VERSION} \u2014 Excaliwow Model Context Protocol server (stdio).`,
|
|
657
|
+
"",
|
|
658
|
+
"An MCP client (Claude Desktop, Claude Code, \u2026) launches this on demand; you",
|
|
659
|
+
"do not normally run it by hand. The flags below are for setup/debugging.",
|
|
660
|
+
"",
|
|
661
|
+
"Usage:",
|
|
662
|
+
" excaliwow-mcp Start the MCP server on stdio (what a client runs).",
|
|
663
|
+
" excaliwow-mcp --health Check the token + API reachability, then exit.",
|
|
664
|
+
" excaliwow-mcp --version Print the version and exit.",
|
|
665
|
+
" excaliwow-mcp --help Print this help and exit.",
|
|
666
|
+
"",
|
|
667
|
+
"Environment:",
|
|
668
|
+
" EXCALIWOW_TOKEN Personal Access Token with read + write. Required.",
|
|
669
|
+
" Mint one at https://excaliwow.com/app/settings.",
|
|
670
|
+
" EXCALIWOW_API_URL API origin. Defaults to https://excaliwow.com.",
|
|
671
|
+
""
|
|
672
|
+
].join("\n")
|
|
673
|
+
);
|
|
674
|
+
process.exit(0);
|
|
675
|
+
}
|
|
676
|
+
if (has("--health")) {
|
|
677
|
+
process.exit(await health());
|
|
678
|
+
}
|
|
679
|
+
if (!resolveToken()) {
|
|
680
|
+
process.stderr.write(
|
|
681
|
+
"[excaliwow-mcp] warning: EXCALIWOW_TOKEN is not set (and no `excaliwow auth login` config was found). The server will start, but every tool call will fail with an auth error until you set a Personal Access Token. See https://excaliwow.com/docs/mcp\n"
|
|
682
|
+
);
|
|
683
|
+
}
|
|
643
684
|
var server = createServer();
|
|
644
685
|
await server.connect(new StdioServerTransport());
|
|
686
|
+
async function health() {
|
|
687
|
+
const token = resolveToken();
|
|
688
|
+
const baseUrl = resolveBaseUrl({});
|
|
689
|
+
if (!token) {
|
|
690
|
+
process.stderr.write(
|
|
691
|
+
"[excaliwow-mcp] health: EXCALIWOW_TOKEN is not set \u2014 no token to check.\n"
|
|
692
|
+
);
|
|
693
|
+
return 1;
|
|
694
|
+
}
|
|
695
|
+
try {
|
|
696
|
+
await listDiagrams({ token, baseUrl }, { limit: 1 });
|
|
697
|
+
process.stderr.write(`[excaliwow-mcp] health: ok \u2014 token authenticates against ${baseUrl}.
|
|
698
|
+
`);
|
|
699
|
+
return 0;
|
|
700
|
+
} catch (err) {
|
|
701
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
702
|
+
process.stderr.write(
|
|
703
|
+
`[excaliwow-mcp] health: 401 \u2014 token is invalid or expired (${baseUrl}).
|
|
704
|
+
`
|
|
705
|
+
);
|
|
706
|
+
return 1;
|
|
707
|
+
}
|
|
708
|
+
if (err instanceof ApiError && err.status === 403) {
|
|
709
|
+
process.stderr.write(
|
|
710
|
+
`[excaliwow-mcp] health: token authenticates against ${baseUrl}, but lacks the \`read\` capability. Mint a token with read + write.
|
|
711
|
+
`
|
|
712
|
+
);
|
|
713
|
+
return 0;
|
|
714
|
+
}
|
|
715
|
+
if (err instanceof NotAuthenticatedError) {
|
|
716
|
+
process.stderr.write(`[excaliwow-mcp] health: ${err.message}
|
|
717
|
+
`);
|
|
718
|
+
return 1;
|
|
719
|
+
}
|
|
720
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
721
|
+
process.stderr.write(`[excaliwow-mcp] health: could not reach ${baseUrl} \u2014 ${msg}
|
|
722
|
+
`);
|
|
723
|
+
return 2;
|
|
724
|
+
}
|
|
725
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@excaliwow/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
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": {
|