@drawpro/mcp 0.4.1 → 0.6.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 +14 -2
  2. package/dist/server.js +122 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,13 +3,25 @@
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
+ npx -y @drawpro/mcp connect dp_live_...
10
16
  ```
11
17
 
12
- Mint the token in DrawPro under **Connect to Claude Code**.
18
+ Mint the token in DrawPro under **Connect to Claude Code**. One command: it
19
+ verifies the token, registers the server with Claude Code for all projects, and
20
+ offers to unlock reading. Re-run it with a new token to rotate. Nothing changes
21
+ if the token is rejected.
22
+
23
+ `auth`, `login`, and `claude mcp add` remain available separately if you would
24
+ rather do the steps yourself.
13
25
 
14
26
  `--scope user` registers it for every project on your machine. The scope
15
27
  otherwise defaults to `local`, which loads the server only in the directory the
package/dist/server.js CHANGED
@@ -24,6 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/server.ts
27
+ var import_node_child_process2 = require("node:child_process");
27
28
  var import_node_fs3 = require("node:fs");
28
29
  var import_node_os3 = require("node:os");
29
30
  var import_node_path3 = require("node:path");
@@ -430,6 +431,9 @@ function installId() {
430
431
  function telemetryEnabled() {
431
432
  return readConfig().telemetry === "on";
432
433
  }
434
+ function resolveToken() {
435
+ return process.env.DRAWPRO_TOKEN || readConfig().token;
436
+ }
433
437
 
434
438
  // ../diagram/src/ids.ts
435
439
  var import_fractional_indexing = require("fractional-indexing");
@@ -1396,10 +1400,10 @@ var ConfigError = class extends Error {
1396
1400
  var clientInstance = null;
1397
1401
  function api() {
1398
1402
  if (!clientInstance) {
1399
- const token = process.env.DRAWPRO_TOKEN;
1403
+ const token = resolveToken();
1400
1404
  if (!token) {
1401
1405
  throw new ConfigError(
1402
- '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'
1406
+ 'No API token. Create one in DrawPro under "Connect to Claude Code", then:\n npx -y @drawpro/mcp connect 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'
1403
1407
  );
1404
1408
  }
1405
1409
  clientInstance = new DrawProClient(BASE_URL, token, (t) => inFlight.push(t));
@@ -1407,7 +1411,7 @@ function api() {
1407
1411
  return clientInstance;
1408
1412
  }
1409
1413
  var SESSION_ID = Math.random().toString(36).slice(2, 10);
1410
- var VERSION = "0.4.1";
1414
+ var VERSION = "0.6.0";
1411
1415
  var inFlight = [];
1412
1416
  var cachedUser = null;
1413
1417
  async function currentUser() {
@@ -1960,12 +1964,126 @@ function maybeSendInBackground() {
1960
1964
  if (ok) writeConfig({ lastReportAt: (/* @__PURE__ */ new Date()).toISOString() });
1961
1965
  });
1962
1966
  }
1967
+ async function auth(arg) {
1968
+ if (arg === "--forget") {
1969
+ writeConfig({ token: void 0 });
1970
+ console.log("Token cleared.");
1971
+ return;
1972
+ }
1973
+ if (!arg) {
1974
+ const current = readConfig().token;
1975
+ const fromEnv = Boolean(process.env.DRAWPRO_TOKEN);
1976
+ if (fromEnv) {
1977
+ console.log("Using DRAWPRO_TOKEN from the environment, which takes precedence.");
1978
+ } else if (current) {
1979
+ console.log(`Stored token: ${current.slice(0, 16)}\u2026`);
1980
+ } else {
1981
+ console.log("No token stored.");
1982
+ }
1983
+ console.log("\n drawpro-mcp auth dp_live_... store or replace it");
1984
+ console.log(" drawpro-mcp auth --forget clear it");
1985
+ return;
1986
+ }
1987
+ if (!arg.startsWith("dp_live_")) {
1988
+ throw new ConfigError("That does not look like a DrawPro token \u2014 they begin with dp_live_.");
1989
+ }
1990
+ const probe = new DrawProClient(BASE_URL, arg);
1991
+ let email;
1992
+ try {
1993
+ email = (await probe.me()).email;
1994
+ } catch (err) {
1995
+ throw new ConfigError(
1996
+ `That token was rejected, so it has not been saved: ${err.message}`
1997
+ );
1998
+ }
1999
+ writeConfig({ token: arg });
2000
+ console.log(`Token saved for ${email}.`);
2001
+ if (process.env.DRAWPRO_TOKEN) {
2002
+ console.log(
2003
+ "\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."
2004
+ );
2005
+ }
2006
+ }
2007
+ function claudeAvailable() {
2008
+ try {
2009
+ (0, import_node_child_process2.execFileSync)("claude", ["--version"], { stdio: "ignore" });
2010
+ return true;
2011
+ } catch {
2012
+ return false;
2013
+ }
2014
+ }
2015
+ async function connect(token) {
2016
+ if (!token) {
2017
+ throw new ConfigError(
2018
+ 'Usage: drawpro-mcp connect dp_live_...\nCreate a token in DrawPro under "Connect to Claude Code".'
2019
+ );
2020
+ }
2021
+ if (!token.startsWith("dp_live_")) {
2022
+ throw new ConfigError("That does not look like a DrawPro token \u2014 they begin with dp_live_.");
2023
+ }
2024
+ process.stdout.write("Checking the token... ");
2025
+ let user;
2026
+ try {
2027
+ user = await new DrawProClient(BASE_URL, token).me();
2028
+ } catch (err) {
2029
+ console.log("");
2030
+ throw new ConfigError(`rejected, nothing has been changed: ${err.message}`);
2031
+ }
2032
+ console.log(`ok \u2014 ${user.email}`);
2033
+ writeConfig({ token });
2034
+ if (!claudeAvailable()) {
2035
+ console.log("\nToken saved, but the `claude` command was not found, so the server could not");
2036
+ console.log("be registered. Register it yourself, without -e DRAWPRO_TOKEN:");
2037
+ console.log("\n claude mcp add drawpro --scope user -- npx -y @drawpro/mcp");
2038
+ return;
2039
+ }
2040
+ try {
2041
+ (0, import_node_child_process2.execFileSync)("claude", ["mcp", "remove", "drawpro", "-s", "user"], { stdio: "ignore" });
2042
+ } catch {
2043
+ }
2044
+ try {
2045
+ (0, import_node_child_process2.execFileSync)("claude", ["mcp", "add", "drawpro", "-s", "user", "--", "npx", "-y", "@drawpro/mcp"], {
2046
+ stdio: "ignore"
2047
+ });
2048
+ } catch (err) {
2049
+ throw new ConfigError(
2050
+ `Token saved, but registering with Claude Code failed: ${err.message}
2051
+ Register it yourself: claude mcp add drawpro --scope user -- npx -y @drawpro/mcp`
2052
+ );
2053
+ }
2054
+ console.log("Registered with Claude Code for all your projects.");
2055
+ if (user.encryptedPrivateKey && !loadKey(user.email)) {
2056
+ console.log("\nCreating diagrams will work now. Reading them needs your passcode, which");
2057
+ console.log("unwraps your private key on this machine \u2014 it is never sent anywhere.");
2058
+ console.log("Unlock now, or skip and run `npx -y @drawpro/mcp login` later.\n");
2059
+ const passcode = await askHidden("passcode (or press enter to skip): ");
2060
+ if (passcode) {
2061
+ process.stdout.write("unlocking... ");
2062
+ try {
2063
+ const key = await decryptPrivateKey(user.encryptedPrivateKey, passcode, user.salt);
2064
+ const { location } = storeKey(user.email, key);
2065
+ console.log(`done, stored in the ${location}.`);
2066
+ } catch {
2067
+ console.log("incorrect passcode. Run `npx -y @drawpro/mcp login` when ready.");
2068
+ }
2069
+ }
2070
+ }
2071
+ console.log("\nRestart Claude Code, then ask it to list your DrawPro workspaces.");
2072
+ }
1963
2073
  async function main() {
1964
2074
  const command = process.argv[2];
1965
2075
  if (command === "login") {
1966
2076
  await login(process.argv.includes("--forget"));
1967
2077
  return;
1968
2078
  }
2079
+ if (command === "connect") {
2080
+ await connect(process.argv[3]);
2081
+ return;
2082
+ }
2083
+ if (command === "auth") {
2084
+ await auth(process.argv[3]);
2085
+ return;
2086
+ }
1969
2087
  if (command === "telemetry") {
1970
2088
  await telemetry(process.argv[3]);
1971
2089
  return;
@@ -1981,7 +2099,7 @@ async function main() {
1981
2099
  }
1982
2100
  if (command && command !== "serve") {
1983
2101
  console.error(
1984
- `Unknown command "${command}". Use: drawpro-mcp [serve|login|stats|telemetry|report]`
2102
+ `Unknown command "${command}". Use: drawpro-mcp [serve|connect|auth|login|stats|telemetry|report]`
1985
2103
  );
1986
2104
  process.exit(2);
1987
2105
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawpro/mcp",
3
- "version": "0.4.1",
3
+ "version": "0.6.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": {