@50arms/platform-mcp 0.0.0-stage → 0.1.436

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 (75) hide show
  1. package/README.md +114 -2
  2. package/dist/access-request.js +101 -0
  3. package/dist/agent-gate.js +69 -0
  4. package/dist/board-id.js +27 -0
  5. package/dist/bounded-retry.js +25 -0
  6. package/dist/checkout-docs-root.js +54 -0
  7. package/dist/clear-control.js +77 -0
  8. package/dist/compaction-control.js +142 -0
  9. package/dist/credential.js +104 -0
  10. package/dist/danxbot-api-examples.js +97 -0
  11. package/dist/dashboard-identity.js +14 -0
  12. package/dist/design-screenshot.js +138 -0
  13. package/dist/digest-hold.js +67 -0
  14. package/dist/docs-tools.js +140 -0
  15. package/dist/durable-write.js +56 -0
  16. package/dist/entrypoint.js +35 -0
  17. package/dist/forbidden-hint.js +23 -0
  18. package/dist/guarded-calls.js +21 -0
  19. package/dist/handlers.js +385 -0
  20. package/dist/heartbeat-reporter.js +156 -0
  21. package/dist/http-client.js +190 -0
  22. package/dist/index.js +1128 -0
  23. package/dist/json-body.js +12 -0
  24. package/dist/key-revoked-halt.js +30 -0
  25. package/dist/lifecycle.js +113 -0
  26. package/dist/listen.js +999 -0
  27. package/dist/listener-reminder.js +119 -0
  28. package/dist/machine-daemon/autostart.js +160 -0
  29. package/dist/machine-daemon/beat.js +77 -0
  30. package/dist/machine-daemon/cli.js +42 -0
  31. package/dist/machine-daemon/install.js +168 -0
  32. package/dist/machine-daemon/state.js +64 -0
  33. package/dist/machine-daemon/version.js +13 -0
  34. package/dist/machine-daemon.js +553 -0
  35. package/dist/machine-toolkit.js +460 -0
  36. package/dist/mini-app-build.js +159 -0
  37. package/dist/mini-app-checkout.js +215 -0
  38. package/dist/mini-app-output.js +111 -0
  39. package/dist/mini-app-publish.js +154 -0
  40. package/dist/mini-app-restore.js +175 -0
  41. package/dist/one-line.js +25 -0
  42. package/dist/pacing-relay.js +16 -0
  43. package/dist/permission-request.js +134 -0
  44. package/dist/permission-watch.js +145 -0
  45. package/dist/plan-events-relay.js +572 -0
  46. package/dist/plan-events-tool.js +9 -0
  47. package/dist/plugin-publish.js +38 -0
  48. package/dist/plugin-screenshot.js +81 -0
  49. package/dist/request-bound.js +21 -0
  50. package/dist/restart-notice.js +271 -0
  51. package/dist/result-limit.js +31 -0
  52. package/dist/role-instruction-file.js +36 -0
  53. package/dist/route-board-scope.generated.js +93 -0
  54. package/dist/route-board-scope.js +71 -0
  55. package/dist/session-access.js +322 -0
  56. package/dist/session-connection.js +264 -0
  57. package/dist/session-control-file.js +58 -0
  58. package/dist/session-key.js +125 -0
  59. package/dist/session-options.js +60 -0
  60. package/dist/session-stats.js +235 -0
  61. package/dist/session-stream.js +345 -0
  62. package/dist/session-title.js +129 -0
  63. package/dist/shutdown.js +38 -0
  64. package/dist/stop-fixes.js +50 -0
  65. package/dist/subagents-live.js +162 -0
  66. package/dist/terms-halt.js +39 -0
  67. package/dist/text-limit.js +24 -0
  68. package/dist/tool-access.js +114 -0
  69. package/dist/tool-defs.js +58 -0
  70. package/dist/tool-surface.js +59 -0
  71. package/dist/transcript-lineage.js +105 -0
  72. package/dist/types.js +45 -0
  73. package/dist/urgent-records.js +47 -0
  74. package/dist/version-floor.js +146 -0
  75. package/package.json +51 -4
@@ -0,0 +1,47 @@
1
+ /**
2
+ * DX-4721 - THE ONE PLACE that decides whether a relayed plan-event record is URGENT.
3
+ *
4
+ * Background. `plan_events_wait` hands the caller (the 50arms plugin's relay, or an orchestrator's waiter sub-agent) a
5
+ * stream of records. The caller delivers an ordinary record after the session's running turn ends, and adds an URGENT one to
6
+ * the running turn at once. Which record is which is decided here, by the record's KIND, and nowhere else: every site that
7
+ * creates a record names its source and calls `isUrgent`; none writes an `urgent` literal of its own. The flag is a required
8
+ * boolean on `ListenEvent`, so a site that forgets it fails `make typecheck`, and no reader defaults a missing one.
9
+ *
10
+ * Urgent: a critical usage-pacing stop (the account is about to hit its limit and the session must wind down now), the
11
+ * notice that this session's key was revoked (it can no longer call the dashboard), 50 Arms' request to prepare for a compaction, its ask to get ready to clear once a ready-list job ended (DX-6692), and the notice that the agent this session proposed was created (DX-5554). Everything else is not: activity events
12
+ * (comments, answers, problems, blocks, design and pull-request outcomes), the idle nudge, the auto-resume message, a digest,
13
+ * the relay's own failure notice, and any line that reports an unreadable event or frame (a fault line).
14
+ *
15
+ * The operator's direct stop. The dashboard writes no stop record today (the activity vocabulary, `IssueActivityKind` in
16
+ * `src/issues/db/issue-activity.ts`, and the stream's frames have none), so there is no kind to key on. When the dashboard adds
17
+ * one, it becomes a `RecordSource` here, urgent, in this same function: never a text match.
18
+ */
19
+ /** DX-4721: true for a critical pacing stop and a revoked key; false for every other source (see the module docblock). */
20
+ export function isUrgent(record) {
21
+ switch (record.source) {
22
+ case "pacing":
23
+ return record.level === "critical";
24
+ case "key_revoked":
25
+ return true;
26
+ // DX-4993: 50 Arms' request to prepare for a compaction must reach the agent inside a running turn, not after it: the compaction waits for the agent's ready.
27
+ case "compaction_ask":
28
+ return true;
29
+ // DX-6692: 50 Arms' ask to get ready to clear after the session's job ended must reach the agent inside a running turn, and wake an idle one, like a compaction request.
30
+ case "clear_ask":
31
+ return true;
32
+ // DX-5554: a person created the agent this session proposed; the session waits on that and must act at once, even mid-turn.
33
+ case "agent_created":
34
+ return true;
35
+ case "activity":
36
+ case "nudge":
37
+ case "resume":
38
+ case "assignment":
39
+ case "docs_land":
40
+ case "docs_draft":
41
+ case "owed_comment":
42
+ case "digest":
43
+ case "relay_notice":
44
+ case "fault":
45
+ return false;
46
+ }
47
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * DX-3597 — this package's half of the MCP-version-floor guard whose
3
+ * server-side half shipped in DX-3525 (`src/mcp-contract.ts`,
4
+ * `src/dashboard/serve-request.ts`, `src/resources/field-tree.ts`, all in
5
+ * the main danxbot repo — this package cannot import them, see below).
6
+ *
7
+ * The dashboard stamps `X-Danx-Mcp-Min-Version` on every routed `/api/*`
8
+ * response with the floor of the `api` surface (`MCP_SURFACE_MIN_VERSIONS.api`
9
+ * in `src/mcp-contract.ts` — DX-3597 made a floor track the version where
10
+ * its surface's MCP-exposed contract last changed, not "whatever's currently
11
+ * published"; DX-4215 scoped floors per surface, so only a change to the
12
+ * `50arms_api` passthrough itself blocks this client's calls. A floor for
13
+ * another surface, such as the stats wire, is enforced by the server on
14
+ * requests to that surface alone and never reaches this header; see that
15
+ * file's module doc for the full decision).
16
+ *
17
+ * `DashboardHttpClient.request()` (`./http-client.ts`) reads this header on
18
+ * the FIRST call this process makes (regardless of outcome — a proactive
19
+ * check, since a stale client can otherwise appear to work for a while on
20
+ * routes whose shape didn't change) and on every subsequent 4xx (a response
21
+ * that might otherwise read as an ordinary caller error, when it's really
22
+ * "you're running an old MCP"). When this package's own version is behind
23
+ * the advertised floor, it throws `McpOutdatedError` instead of returning
24
+ * the ordinary envelope — the same "throw rather than fabricate/silently
25
+ * pass through" contract `http-client.ts` already applies to a 5xx/network
26
+ * failure (see that file's own doc): an outdated MCP is exactly that class
27
+ * of failure, nothing the calling tool can meaningfully work around.
28
+ *
29
+ * Everything here is duplicated, not imported, from the server's
30
+ * `src/mcp-contract.ts` — this package is an INDEPENDENTLY PUBLISHED npm
31
+ * artifact (DX-2103) and cannot depend on the danxbot service's own `src/`
32
+ * tree. Keep the header name, the package name, and the version-compare
33
+ * behavior in agreement with that file by hand when either changes.
34
+ */
35
+ import { readFileSync } from "node:fs";
36
+ import { dirname, join } from "node:path";
37
+ import { fileURLToPath } from "node:url";
38
+ /** Must match `MCP_MIN_VERSION_HEADER` in danxbot's `src/mcp-contract.ts` verbatim. */
39
+ export const MCP_MIN_VERSION_HEADER = "X-Danx-Mcp-Min-Version";
40
+ /**
41
+ * DX-5897 - request header this package stamps on every call with its own version (`readOwnVersion`), so a dashboard route that enforces a
42
+ * surface floor of its own (`MCP_SURFACE_MIN_VERSIONS.profiles`: the agent-profile fetch) can refuse a stale session. Must match
43
+ * `MCP_VERSION_HEADER` in danxbot's `src/mcp-contract.ts` verbatim.
44
+ */
45
+ export const MCP_VERSION_HEADER = "X-Danx-Mcp-Version";
46
+ /** DX-6272 - response header of the session event stream carrying its `stream` floor. Must match `MCP_STREAM_MIN_VERSION_HEADER` in danxbot's `src/mcp-contract.ts` verbatim. */
47
+ export const MCP_STREAM_MIN_VERSION_HEADER = "X-Danx-Stream-Min-Version";
48
+ /** Must match `MCP_PACKAGE_NAME` in danxbot's `src/mcp-contract.ts` verbatim. */
49
+ export const MCP_PACKAGE_NAME = "@50arms/platform-mcp";
50
+ /**
51
+ * Numeric-dotted version compare (major.minor.patch, ...) — mirrors
52
+ * `compareMcpVersions` in danxbot's `src/mcp-contract.ts` behavior
53
+ * byte-for-byte (duplicated, not imported — see module doc). Both sides of
54
+ * this contract have only ever used plain numeric dotted versions, so this
55
+ * throws rather than silently falling back to a lexical compare, which
56
+ * would misorder "0.1.9" vs "0.1.10".
57
+ */
58
+ export function compareMcpVersions(a, b) {
59
+ const parse = (v) => {
60
+ if (!/^\d+(\.\d+)*$/.test(v)) {
61
+ throw new Error(`[platform-mcp] "${v}" is not a plain numeric dotted version (major.minor.patch)`);
62
+ }
63
+ return v.split(".").map(Number);
64
+ };
65
+ const pa = parse(a);
66
+ const pb = parse(b);
67
+ const len = Math.max(pa.length, pb.length);
68
+ for (let i = 0; i < len; i++) {
69
+ const da = pa[i] ?? 0;
70
+ const db = pb[i] ?? 0;
71
+ if (da !== db)
72
+ return da - db;
73
+ }
74
+ return 0;
75
+ }
76
+ let cachedOwnVersion;
77
+ /**
78
+ * Reads this package's OWN version from its `package.json` — always present
79
+ * next to `dist/` (or `src/`, under `npm run dev`'s `tsx`) at runtime even
80
+ * though `files: ["dist","README.md"]` excludes it from the tarball
81
+ * MANIFEST: npm always includes `package.json` in what it publishes and
82
+ * what it installs, regardless of `files` (the same guarantee `README` /
83
+ * `LICENSE` / the `main` entry get) — this reads the copy npm actually
84
+ * installed next to this module, not danxbot's own source tree. Both
85
+ * `dist/version-floor.js` and `src/version-floor.ts` sit exactly one
86
+ * directory below the package root, so `../package.json` resolves
87
+ * correctly under both the published (dist) and dev (`tsx src/index.ts`)
88
+ * shapes. Memoized — the version cannot change mid-process.
89
+ */
90
+ export function readOwnVersion() {
91
+ if (cachedOwnVersion !== undefined)
92
+ return cachedOwnVersion;
93
+ const here = dirname(fileURLToPath(import.meta.url));
94
+ const pkgJsonPath = join(here, "..", "package.json");
95
+ const raw = readFileSync(pkgJsonPath, "utf8");
96
+ const parsed = JSON.parse(raw);
97
+ if (typeof parsed.version !== "string") {
98
+ throw new Error(`[platform-mcp] could not read this package's own version from ${pkgJsonPath}`);
99
+ }
100
+ cachedOwnVersion = parsed.version;
101
+ return cachedOwnVersion;
102
+ }
103
+ /** Test-only escape hatch — real callers never need to override the memoized read. */
104
+ export function __resetOwnVersionCacheForTests() {
105
+ cachedOwnVersion = undefined;
106
+ }
107
+ /**
108
+ * Thrown by `assertVersionFloorSatisfied` when this package is running
109
+ * behind the server's advertised floor. Carries structured `have`/`need`
110
+ * fields (not just a prose message) so a caller that wants to branch on
111
+ * "is this specifically an outdated-MCP failure" can check `error.code`
112
+ * rather than pattern-match the message text.
113
+ */
114
+ export class McpOutdatedError extends Error {
115
+ code = "mcp_outdated";
116
+ have;
117
+ need;
118
+ constructor(have, need) {
119
+ super(`[platform-mcp] mcp_outdated: this MCP is running ${MCP_PACKAGE_NAME}@${have}, but the dashboard's ` +
120
+ `50arms_api surface requires >= ${need}. Restart your Claude Code session to pick up the published update.`);
121
+ this.name = "McpOutdatedError";
122
+ this.have = have;
123
+ this.need = need;
124
+ }
125
+ }
126
+ /**
127
+ * Whether this package's own version is below the floor a response header
128
+ * advertised. `false` when `headerValue` is `null`/empty (an old-enough
129
+ * server, or a test stub, that never stamped one: nothing to compare
130
+ * against); a malformed floor throws (`compareMcpVersions`), never reads as
131
+ * no floor.
132
+ */
133
+ export function ownVersionBelow(headerValue) {
134
+ if (!headerValue)
135
+ return false;
136
+ return compareMcpVersions(readOwnVersion(), headerValue) < 0;
137
+ }
138
+ /**
139
+ * Throws `McpOutdatedError` when this package is behind the floor a response
140
+ * header advertised (`ownVersionBelow`); returns otherwise.
141
+ */
142
+ export function assertVersionFloorSatisfied(headerValue) {
143
+ if (headerValue && ownVersionBelow(headerValue)) {
144
+ throw new McpOutdatedError(readOwnVersion(), headerValue);
145
+ }
146
+ }
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@50arms/platform-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.436",
4
+ "description": "Stdio MCP server fronting the 50 Arms dashboard API for agents (operator sessions and dispatched workers alike): the generic 50arms_api/50arms_api_list/50arms_api_spec tools reach any route the caller's credential may call, plus dedicated agent-specific tools (plan_connect, request_permission, attach_file, and 50arms_slack_reply) (DX-3465, PLN-11 R-25).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "bin": {
9
+ "platform-mcp": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "!dist/machine-daemon/run.js",
14
+ "!dist/machine-daemon/entry.js",
15
+ "!dist/machine-daemon/report.js",
16
+ "!dist/machine-toolkit/**",
17
+ "README.md"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/newms87/danxbot.git",
22
+ "directory": "packages/danx-dashboard-mcp"
23
+ },
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "scripts": {
28
+ "prepublishOnly": "bash ../../scripts/assert-tree-contains-origin-main.sh @50arms/platform-mcp",
29
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\" && node scripts/build-machine-daemon.mjs",
30
+ "prepack": "node scripts/build-machine-daemon.mjs",
31
+ "verify:boot": "bash scripts/verify-boot.sh",
32
+ "start": "node dist/index.js",
33
+ "dev": "tsx src/index.ts",
34
+ "gen-tool-defs": "tsx scripts/gen-tool-defs.ts",
35
+ "test": "vitest run",
36
+ "test:watch": "vitest"
37
+ },
38
+ "dependencies": {
39
+ "@danxbot/docs": "0.1.63",
40
+ "@danxbot/plugin-sdk": "0.2.39",
41
+ "@modelcontextprotocol/sdk": "1.29.0",
42
+ "@50arms/session-telemetry": "0.1.66",
43
+ "zod": "^3.25.76"
44
+ },
45
+ "devDependencies": {
46
+ "@danxbot/machine-vitals": "file:../machine-vitals",
47
+ "@types/node": "^22.0.0",
48
+ "esbuild": "^0.27.3",
49
+ "tsx": "^4.0.0",
50
+ "typescript": "^5.7.0",
51
+ "vitest": "^4.1.5"
52
+ }
53
+ }