@50arms/platform-mcp 0.0.0-stage → 0.1.437
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 +114 -2
- package/dist/access-request.js +101 -0
- package/dist/agent-gate.js +69 -0
- package/dist/board-id.js +27 -0
- package/dist/bounded-retry.js +25 -0
- package/dist/checkout-docs-root.js +54 -0
- package/dist/clear-control.js +77 -0
- package/dist/compaction-control.js +142 -0
- package/dist/credential.js +104 -0
- package/dist/danxbot-api-examples.js +97 -0
- package/dist/dashboard-identity.js +14 -0
- package/dist/design-screenshot.js +138 -0
- package/dist/digest-hold.js +67 -0
- package/dist/docs-tools.js +140 -0
- package/dist/durable-write.js +56 -0
- package/dist/entrypoint.js +35 -0
- package/dist/forbidden-hint.js +23 -0
- package/dist/guarded-calls.js +21 -0
- package/dist/handlers.js +385 -0
- package/dist/heartbeat-reporter.js +156 -0
- package/dist/http-client.js +190 -0
- package/dist/index.js +1128 -0
- package/dist/json-body.js +12 -0
- package/dist/key-revoked-halt.js +30 -0
- package/dist/lifecycle.js +113 -0
- package/dist/listen.js +999 -0
- package/dist/listener-reminder.js +119 -0
- package/dist/machine-daemon/autostart.js +160 -0
- package/dist/machine-daemon/beat.js +77 -0
- package/dist/machine-daemon/cli.js +42 -0
- package/dist/machine-daemon/install.js +168 -0
- package/dist/machine-daemon/state.js +64 -0
- package/dist/machine-daemon/version.js +13 -0
- package/dist/machine-daemon.js +553 -0
- package/dist/machine-toolkit.js +460 -0
- package/dist/mini-app-build.js +159 -0
- package/dist/mini-app-checkout.js +215 -0
- package/dist/mini-app-output.js +111 -0
- package/dist/mini-app-publish.js +154 -0
- package/dist/mini-app-restore.js +175 -0
- package/dist/one-line.js +25 -0
- package/dist/pacing-relay.js +16 -0
- package/dist/permission-request.js +134 -0
- package/dist/permission-watch.js +145 -0
- package/dist/plan-events-relay.js +572 -0
- package/dist/plan-events-tool.js +9 -0
- package/dist/plugin-publish.js +38 -0
- package/dist/plugin-screenshot.js +81 -0
- package/dist/request-bound.js +21 -0
- package/dist/restart-notice.js +271 -0
- package/dist/result-limit.js +31 -0
- package/dist/role-instruction-file.js +36 -0
- package/dist/route-board-scope.generated.js +95 -0
- package/dist/route-board-scope.js +71 -0
- package/dist/session-access.js +322 -0
- package/dist/session-connection.js +264 -0
- package/dist/session-control-file.js +58 -0
- package/dist/session-key.js +125 -0
- package/dist/session-options.js +60 -0
- package/dist/session-stats.js +235 -0
- package/dist/session-stream.js +345 -0
- package/dist/session-title.js +129 -0
- package/dist/shutdown.js +38 -0
- package/dist/stop-fixes.js +50 -0
- package/dist/subagents-live.js +162 -0
- package/dist/terms-halt.js +39 -0
- package/dist/text-limit.js +24 -0
- package/dist/tool-access.js +114 -0
- package/dist/tool-defs.js +58 -0
- package/dist/tool-surface.js +59 -0
- package/dist/transcript-lineage.js +105 -0
- package/dist/types.js +45 -0
- package/dist/urgent-records.js +47 -0
- package/dist/version-floor.js +146 -0
- 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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.437",
|
|
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
|
+
}
|