@drawpro/mcp 0.4.0 → 0.5.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 +34 -4
  2. package/dist/server.js +76 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,13 +3,24 @@
3
3
  Local MCP server for DrawPro. Lets Claude read and create diagrams in your
4
4
  account from Claude Code, Claude Desktop, or any MCP client.
5
5
 
6
+ Full documentation:
7
+ [setup](https://github.com/deBilla/drawpro/blob/main/docs/connect-claude-code.md) ·
8
+ [tools](https://github.com/deBilla/drawpro/blob/main/docs/mcp-tools.md) ·
9
+ [diagram specs](https://github.com/deBilla/drawpro/blob/main/docs/diagram-specs.md) ·
10
+ [privacy](https://github.com/deBilla/drawpro/blob/main/docs/privacy.md)
11
+
6
12
  ## Setup
7
13
 
8
14
  ```bash
9
- claude mcp add drawpro --scope user -e DRAWPRO_TOKEN="dp_live_..." -- npx -y @drawpro/mcp
15
+ claude mcp add drawpro --scope user -- npx -y @drawpro/mcp
16
+ npx -y @drawpro/mcp auth dp_live_...
10
17
  ```
11
18
 
12
- Mint the token in DrawPro under **Connect to Claude Code**.
19
+ Mint the token in DrawPro under **Connect to Claude Code**. `auth` verifies it
20
+ against the API before saving, and `auth dp_live_<new>` rotates it later —
21
+ Claude Code can add and remove a server but not edit one, so a token passed with
22
+ `-e DRAWPRO_TOKEN` can only be changed by removing and re-adding the server.
23
+ `DRAWPRO_TOKEN` still works and takes precedence.
13
24
 
14
25
  `--scope user` registers it for every project on your machine. The scope
15
26
  otherwise defaults to `local`, which loads the server only in the directory the
@@ -123,7 +134,21 @@ coordinates.
123
134
 
124
135
  ## Usage log
125
136
 
126
- Set `DRAWPRO_MCP_LOG` to record every tool call as JSONL:
137
+ Turning telemetry on is enough to start recording:
138
+
139
+ ```bash
140
+ npx -y @drawpro/mcp telemetry # show the state and the exact payload
141
+ npx -y @drawpro/mcp telemetry on # records to ~/.drawpro/usage.jsonl and shares an aggregate
142
+ npx -y @drawpro/mcp telemetry off
143
+ npx -y @drawpro/mcp report # send once, without turning anything on
144
+ ```
145
+
146
+ Consenting to *send* usage implies consent to *record* it — recording is the
147
+ lesser act — so opting in does not also require configuring a log. The
148
+ implication does not run the other way: setting `DRAWPRO_MCP_LOG` records
149
+ locally and shares nothing.
150
+
151
+ To record without ever sharing, set the path yourself:
127
152
 
128
153
  ```bash
129
154
  claude mcp add drawpro --scope user \
@@ -131,9 +156,14 @@ claude mcp add drawpro --scope user \
131
156
  -e DRAWPRO_MCP_LOG="$HOME/.drawpro/usage.jsonl" \
132
157
  -- npx -y @drawpro/mcp
133
158
 
134
- drawpro-mcp stats # or: npx -y @drawpro/mcp stats
159
+ npx -y @drawpro/mcp stats
135
160
  ```
136
161
 
162
+ > Run these from anywhere except a checkout of this repository. Inside it, npm
163
+ > resolves `@drawpro/mcp` to the workspace copy, whose bin is not linked, and
164
+ > npx fails with `drawpro-mcp: command not found`. Use
165
+ > `node packages/mcp/dist/server.js <command>` there instead.
166
+
137
167
  ```
138
168
  5 calls 2026-08-29 .. 2026-08-29
139
169
 
package/dist/server.js CHANGED
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/server.ts
27
27
  var import_node_fs3 = require("node:fs");
28
+ var import_node_os3 = require("node:os");
28
29
  var import_node_path3 = require("node:path");
29
30
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
30
31
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
@@ -429,6 +430,9 @@ function installId() {
429
430
  function telemetryEnabled() {
430
431
  return readConfig().telemetry === "on";
431
432
  }
433
+ function resolveToken() {
434
+ return process.env.DRAWPRO_TOKEN || readConfig().token;
435
+ }
432
436
 
433
437
  // ../diagram/src/ids.ts
434
438
  var import_fractional_indexing = require("fractional-indexing");
@@ -1395,10 +1399,10 @@ var ConfigError = class extends Error {
1395
1399
  var clientInstance = null;
1396
1400
  function api() {
1397
1401
  if (!clientInstance) {
1398
- const token = process.env.DRAWPRO_TOKEN;
1402
+ const token = resolveToken();
1399
1403
  if (!token) {
1400
1404
  throw new ConfigError(
1401
- 'DRAWPRO_TOKEN is not set. Create a token in DrawPro under "Connect to Claude Code", then:\n claude mcp add drawpro --scope user -e DRAWPRO_TOKEN="dp_live_..." -- npx -y @drawpro/mcp'
1405
+ 'No API token. Create one in DrawPro under "Connect to Claude Code", then:\n npx -y @drawpro/mcp auth dp_live_...\n\nOr supply it through the environment when registering the server:\n claude mcp add drawpro --scope user -e DRAWPRO_TOKEN="dp_live_..." -- npx -y @drawpro/mcp'
1402
1406
  );
1403
1407
  }
1404
1408
  clientInstance = new DrawProClient(BASE_URL, token, (t) => inFlight.push(t));
@@ -1406,7 +1410,7 @@ function api() {
1406
1410
  return clientInstance;
1407
1411
  }
1408
1412
  var SESSION_ID = Math.random().toString(36).slice(2, 10);
1409
- var VERSION = "0.4.0";
1413
+ var VERSION = "0.5.0";
1410
1414
  var inFlight = [];
1411
1415
  var cachedUser = null;
1412
1416
  async function currentUser() {
@@ -1432,10 +1436,17 @@ function text(body) {
1432
1436
  function sheetUrl(workspaceId, sheetId) {
1433
1437
  return `${APP_URL}/workspace/${workspaceId}/sheet/${sheetId}`;
1434
1438
  }
1439
+ var DEFAULT_LOG = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".drawpro", "usage.jsonl");
1440
+ function logPath() {
1441
+ const explicit = process.env.DRAWPRO_MCP_LOG;
1442
+ if (explicit) return explicit;
1443
+ return telemetryEnabled() ? DEFAULT_LOG : void 0;
1444
+ }
1435
1445
  function logCall(entry) {
1436
- const path = process.env.DRAWPRO_MCP_LOG;
1446
+ const path = logPath();
1437
1447
  if (!path) return;
1438
1448
  try {
1449
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)((0, import_node_os3.homedir)(), ".drawpro"), { recursive: true, mode: 448 });
1439
1450
  (0, import_node_fs3.appendFileSync)(path, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry }) + "\n");
1440
1451
  } catch {
1441
1452
  }
@@ -1843,9 +1854,11 @@ function summariseLog(file) {
1843
1854
  };
1844
1855
  }
1845
1856
  function stats(path, asJson) {
1846
- const file = path ?? process.env.DRAWPRO_MCP_LOG;
1857
+ const file = path ?? logPath();
1847
1858
  if (!file) {
1848
- console.error("Set DRAWPRO_MCP_LOG, or pass a path: drawpro-mcp stats <file>");
1859
+ console.error(
1860
+ "No usage is being recorded. Either set DRAWPRO_MCP_LOG, turn on telemetry\n(which records to ~/.drawpro/usage.jsonl), or pass a path: drawpro-mcp stats <file>"
1861
+ );
1849
1862
  process.exit(2);
1850
1863
  }
1851
1864
  const summary = summariseLog(file);
@@ -1874,7 +1887,7 @@ function stats(path, asJson) {
1874
1887
  console.log(" Add --json for a machine-readable copy.");
1875
1888
  }
1876
1889
  function buildReport() {
1877
- const file = process.env.DRAWPRO_MCP_LOG;
1890
+ const file = logPath();
1878
1891
  if (!file) return null;
1879
1892
  const summary = summariseLog(file);
1880
1893
  if (!summary) return null;
@@ -1907,8 +1920,11 @@ async function telemetry(action) {
1907
1920
  const report = buildReport();
1908
1921
  if (action === "on") {
1909
1922
  writeConfig({ telemetry: "on" });
1910
- console.log("Telemetry on. Roughly once a day, this is sent:\n");
1911
- console.log(report ? JSON.stringify(report, null, 2) : " (nothing yet \u2014 set DRAWPRO_MCP_LOG to record usage)");
1923
+ console.log(`Telemetry on. Usage is recorded to ${logPath()}`);
1924
+ console.log("Roughly once a day, an aggregate of it is sent:\n");
1925
+ console.log(
1926
+ report ? JSON.stringify(report, null, 2) : " (no calls recorded yet \u2014 the first report goes out once you have used the tools)"
1927
+ );
1912
1928
  console.log("\nTurn it off any time with: drawpro-mcp telemetry off");
1913
1929
  return;
1914
1930
  }
@@ -1916,7 +1932,9 @@ async function telemetry(action) {
1916
1932
  `);
1917
1933
  console.log("If enabled, this is the entire payload \u2014 tool counts and timings,");
1918
1934
  console.log("no account, no token, no workspace or sheet ids, nothing drawn:\n");
1919
- console.log(report ? JSON.stringify(report, null, 2) : " (nothing recorded yet \u2014 set DRAWPRO_MCP_LOG first)");
1935
+ console.log(
1936
+ report ? JSON.stringify(report, null, 2) : ` (no calls recorded yet; recording ${logPath() ? `to ${logPath()}` : "is off"})`
1937
+ );
1920
1938
  console.log("\n drawpro-mcp telemetry on share it");
1921
1939
  console.log(" drawpro-mcp telemetry off stop");
1922
1940
  console.log(" drawpro-mcp report send once now, without turning it on");
@@ -1924,7 +1942,9 @@ async function telemetry(action) {
1924
1942
  async function reportOnce() {
1925
1943
  const report = buildReport();
1926
1944
  if (!report) {
1927
- console.error("Nothing to report. Set DRAWPRO_MCP_LOG to record usage first.");
1945
+ console.error(
1946
+ "Nothing to report \u2014 no usage has been recorded. Set DRAWPRO_MCP_LOG, or\nturn on telemetry, which records to ~/.drawpro/usage.jsonl."
1947
+ );
1928
1948
  process.exit(2);
1929
1949
  }
1930
1950
  console.log("Sending:\n");
@@ -1943,12 +1963,56 @@ function maybeSendInBackground() {
1943
1963
  if (ok) writeConfig({ lastReportAt: (/* @__PURE__ */ new Date()).toISOString() });
1944
1964
  });
1945
1965
  }
1966
+ async function auth(arg) {
1967
+ if (arg === "--forget") {
1968
+ writeConfig({ token: void 0 });
1969
+ console.log("Token cleared.");
1970
+ return;
1971
+ }
1972
+ if (!arg) {
1973
+ const current = readConfig().token;
1974
+ const fromEnv = Boolean(process.env.DRAWPRO_TOKEN);
1975
+ if (fromEnv) {
1976
+ console.log("Using DRAWPRO_TOKEN from the environment, which takes precedence.");
1977
+ } else if (current) {
1978
+ console.log(`Stored token: ${current.slice(0, 16)}\u2026`);
1979
+ } else {
1980
+ console.log("No token stored.");
1981
+ }
1982
+ console.log("\n drawpro-mcp auth dp_live_... store or replace it");
1983
+ console.log(" drawpro-mcp auth --forget clear it");
1984
+ return;
1985
+ }
1986
+ if (!arg.startsWith("dp_live_")) {
1987
+ throw new ConfigError("That does not look like a DrawPro token \u2014 they begin with dp_live_.");
1988
+ }
1989
+ const probe = new DrawProClient(BASE_URL, arg);
1990
+ let email;
1991
+ try {
1992
+ email = (await probe.me()).email;
1993
+ } catch (err) {
1994
+ throw new ConfigError(
1995
+ `That token was rejected, so it has not been saved: ${err.message}`
1996
+ );
1997
+ }
1998
+ writeConfig({ token: arg });
1999
+ console.log(`Token saved for ${email}.`);
2000
+ if (process.env.DRAWPRO_TOKEN) {
2001
+ console.log(
2002
+ "\nNote: DRAWPRO_TOKEN is also set in this environment and takes precedence.\nRemove it from the MCP registration for the stored token to be used."
2003
+ );
2004
+ }
2005
+ }
1946
2006
  async function main() {
1947
2007
  const command = process.argv[2];
1948
2008
  if (command === "login") {
1949
2009
  await login(process.argv.includes("--forget"));
1950
2010
  return;
1951
2011
  }
2012
+ if (command === "auth") {
2013
+ await auth(process.argv[3]);
2014
+ return;
2015
+ }
1952
2016
  if (command === "telemetry") {
1953
2017
  await telemetry(process.argv[3]);
1954
2018
  return;
@@ -1964,7 +2028,7 @@ async function main() {
1964
2028
  }
1965
2029
  if (command && command !== "serve") {
1966
2030
  console.error(
1967
- `Unknown command "${command}". Use: drawpro-mcp [serve|login|stats|telemetry|report]`
2031
+ `Unknown command "${command}". Use: drawpro-mcp [serve|auth|login|stats|telemetry|report]`
1968
2032
  );
1969
2033
  process.exit(2);
1970
2034
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawpro/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server for DrawPro \u2014 read and create end-to-end encrypted Excalidraw diagrams from Claude",
5
5
  "license": "MIT",
6
6
  "repository": {