@octane-xplat/mcp 0.0.1 → 0.15.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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -1
  3. package/package.json +34 -8
  4. package/src/actions/bridge.mjs +2 -0
  5. package/src/contracts/actions.d.ts +209 -0
  6. package/src/contracts/actions.mjs +3 -0
  7. package/src/contracts/boundaries.d.ts +6 -0
  8. package/src/contracts/boundaries.mjs +3 -0
  9. package/src/contracts/capabilities.d.ts +106 -0
  10. package/src/contracts/capabilities.mjs +3 -0
  11. package/src/contracts/code.d.ts +134 -0
  12. package/src/contracts/code.mjs +3 -0
  13. package/src/contracts/errors.d.ts +75 -0
  14. package/src/contracts/errors.mjs +3 -0
  15. package/src/contracts/evidence.d.ts +115 -0
  16. package/src/contracts/evidence.mjs +3 -0
  17. package/src/contracts/identity.d.ts +127 -0
  18. package/src/contracts/identity.mjs +3 -0
  19. package/src/contracts/index.d.ts +11 -0
  20. package/src/contracts/index.mjs +3 -0
  21. package/src/contracts/inspection.d.ts +45 -0
  22. package/src/contracts/inspection.mjs +3 -0
  23. package/src/contracts/policy.d.ts +86 -0
  24. package/src/contracts/policy.mjs +3 -0
  25. package/src/contracts/sessions.d.ts +160 -0
  26. package/src/contracts/sessions.mjs +3 -0
  27. package/src/contracts/status.d.ts +40 -0
  28. package/src/contracts/status.mjs +3 -0
  29. package/src/discovery.mjs +2 -0
  30. package/src/docs.mjs +84 -0
  31. package/src/inspection/service.mjs +5 -0
  32. package/src/inspection/tools.mjs +53 -0
  33. package/src/runtime/logs.mjs +2 -0
  34. package/src/runtime/process.mjs +2 -0
  35. package/src/runtime/service.mjs +2 -0
  36. package/src/runtime/targets.mjs +2 -0
  37. package/src/runtime/tools.mjs +126 -0
  38. package/src/server.mjs +565 -0
  39. package/src/status.mjs +2 -0
  40. package/src/xplat-mcp.mjs +85 -0
  41. package/index.d.ts +0 -1
  42. package/index.js +0 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Alec Larson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,340 @@
1
1
  # @octane-xplat/mcp
2
2
 
3
- Version 0.0.1 is a reservation stub. It exposes no supported API; the real implementation will arrive in a later release.
3
+ MCP server for agents working on Octane Xplat consumer apps. One entry point
4
+ gives the agent detected project identity, framework versions, guidance,
5
+ capability discovery, and the shared permission / session / evidence contracts
6
+ that every later subsystem (docs, code search, runtime, inspection,
7
+ development actions) plugs into.
8
+
9
+ Published as a normal lockstep package on npm. The qualified surface today is
10
+ the Chromium consumer lane (status/docs/code/build/launch/inspection/
11
+ interaction/development actions); native-platform inspection and input
12
+ adapters report honest `unavailable` results until they are qualified — see
13
+ [Remaining acceptance](#remaining-acceptance).
14
+
15
+ ## Install and run
16
+
17
+ Add the server to a consumer app and point the agent's MCP client at the
18
+ installed `xplat-mcp` bin:
19
+
20
+ ```sh
21
+ pnpm add -D @octane-xplat/mcp
22
+ ```
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "octane-xplat": {
28
+ "command": "xplat-mcp",
29
+ "args": ["--workspace", "/path/to/app-workspace"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ A one-off run without a devDependency also works:
36
+
37
+ ```sh
38
+ pnpm dlx @octane-xplat/mcp --workspace /path/to/app-workspace
39
+ ```
40
+
41
+ For development inside this repository:
42
+
43
+ ```sh
44
+ pnpm --filter @octane-xplat/mcp start -- --workspace <path>
45
+ pnpm --filter @octane-xplat/mcp test
46
+ ```
47
+
48
+ Transport is stdio; stdout is protocol-clean (diagnostics go to stderr).
49
+ Options: `--workspace` / `XPLAT_MCP_WORKSPACE` (default cwd), `--app` /
50
+ `XPLAT_MCP_APP` (pre-select an app id), `--permissions` /
51
+ `XPLAT_MCP_PERMISSIONS` (comma-separated scope grants), `--evidence-dir` /
52
+ `XPLAT_MCP_EVIDENCE_DIR`.
53
+
54
+ ## Tools
55
+
56
+ | Tool | Purpose |
57
+ | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
+ | `xplat_project_status` | Detected apps, selection state, framework version binding, guidance sources, docs/code-index status, declared platforms, runtime prerequisites, capability summary, permissions, boundaries, next steps. |
59
+ | `xplat_select_app` | Explicitly bind an app by id/path/name. Ambiguous or unknown selections fail with the candidate list — the server never picks silently. |
60
+ | `xplat_capability_matrix` | Per-platform capability entries: build, launch, logs, inspection, screenshot, interaction, and the rest are separate rows with status, detail, and alternatives. |
61
+ | `xplat_permissions` | Grant states per scope plus the action-time check result and recovery path. |
62
+ | `xplat_sessions` | Bound sessions: owned vs attached user-owned, state, build identity. |
63
+ | `xplat_targets` | Launchable targets per platform (simulators, devices, host targets) with availability, missing-toolchain reasons, and busy/ownership state. |
64
+ | `xplat_build` | Build the bound app for one platform as a job — same commands `xplat build` runs. `requestKey` dedupes retries. |
65
+ | `xplat_launch` | Owned dev session on one target as a job; completes at confirmed readiness, session owns the processes until `xplat_session_end`. |
66
+ | `xplat_attach` | Bind a compatible already-running dev session (user-owned, preserved). |
67
+ | `xplat_logs` | Bounded ordered logs for a session or build job: cursor, limit, stream filter, truncation/reset indicators, `historical` flag for dead sessions. |
68
+ | `xplat_dev_actions` | The bound app's live development-action registry: instance identity, revision, declarations (contracts, effects, availability). Empty vs disconnected vs no-bridge are distinct states. |
69
+ | `xplat_dev_action_invoke` | Invoke an action against an exact app instance + registry revision. Pre-validates input, availability, grants and queue bounds; returns a job (`requestKey` dedupes retries). |
70
+ | `xplat_dev_action_status` | Invocation status/result — same job data restricted to dev-action jobs, with instance/request identity. |
71
+ | `xplat_job_status` / `xplat_job_cancel` | Bounded job status and cooperative cancellation. |
72
+ | `xplat_session_end` | Ownership-safe cleanup: owned sessions release resources; attached sessions are preserved (our log taps still stop). |
73
+ | `xplat_evidence` | `list` / `retain` / `delete` evidence; retain takes an optional ISO expiration. |
74
+ | `xplat_code_search` | Exact identifier/token search over current source (`mode: 'exact'`), optional local semantic index (`mode: 'semantic'`), or both (`'auto'`). Corpus filters: `app`, `framework`; platform and package filters. |
75
+ | `xplat_code_read` | Bounded read of current source by search ref (`xplat-code://…`) or explicit corpus + path; results carry freshness markers and continuation offsets. |
76
+ | `xplat_docs_search` / `xplat_docs_read` | Installed-version docs search and bounded page reads, with snapshot/version provenance and unavailable/conflict results. |
77
+ | `xplat_guidance` | Tailored guidance for the selected app's installed packages. |
78
+
79
+ ## Retrieve the app's documentation
80
+
81
+ Select your app first. Search defaults to maintained guides for its actual
82
+ installed versions; design notes and recipes require an explicit corpus.
83
+ The provider validates release tags and preserves cached snapshot commits.
84
+ Missing matching docs return an installed-source fallback instead of latest docs.
85
+
86
+ ```json
87
+ {
88
+ "name": "xplat_docs_search",
89
+ "arguments": { "query": "testID", "package": "@octane-xplat/ui", "platform": "web" }
90
+ }
91
+ ```
92
+
93
+ Read a result using its `ref`. A ref from an older installation remains tied
94
+ to that version and is labeled outside the installed binding after an upgrade.
95
+ `xplat_project_status` reports provider readiness; each search reports actual
96
+ snapshot availability, so readiness alone is not a successful retrieval.
97
+
98
+ ```json
99
+ {
100
+ "name": "xplat_docs_read",
101
+ "arguments": {
102
+ "package": "@octane-xplat/ui",
103
+ "path": "docs/verify/test-identifiers.md",
104
+ "limit": 2000
105
+ }
106
+ }
107
+ ```
108
+
109
+ Code search reads the bound application's source plus its installed
110
+ `@octane-xplat/*` package sources — corpora stay labeled and distinguishable.
111
+ The semantic index is local TF-IDF (`local-tfidf`): it builds in the
112
+ background, reports `preparing` / `ready` / `stale` / `unavailable`, and stale
113
+ hits are revalidated against current file content before serving. Nothing
114
+ leaves the machine; there is no remote embedding path. Secrets, private
115
+ configuration, generated output, and paths outside the bound scope are
116
+ excluded and unreadable.
117
+
118
+ Every result is `{ ok: true, data } | { ok: false, error }` where `error` is a
119
+ `ContractError` from the shared vocabulary.
120
+
121
+ The service layer lives in `packages/cli/src/mcp/` — the `xplat` CLI projects
122
+ the same contracts through its agent commands (`xplat catalog --json` is the
123
+ entry point). `packages/mcp/src/` holds the stdio transport adapter: tool
124
+ registration wraps the shared services, and the published `.d.ts` contract
125
+ surface stays here.
126
+
127
+ ## Contract surface (`@octane-xplat/mcp/contracts/*`)
128
+
129
+ Later work packages integrate against these modules; the `.d.ts` files are the
130
+ documented surface and the `.mjs` files carry the runtime values and stores.
131
+
132
+ - `errors` — `ContractErrorCode` vocabulary (`invalid_selection`,
133
+ `ambiguous_selection`, `permission_denied`, `unsupported_capability`,
134
+ `prerequisite_missing`, `target_busy`, `stale_identity`,
135
+ `disconnected_runtime`, `execution_failure`, `not_found`, `cancelled`,
136
+ `timeout`, `unavailable`) with `recovery` (smallest corrective action) and
137
+ `retryable` semantics.
138
+ - `identity` — `Platform`, `AppRef`, `FrameworkBinding` (installed / workspace /
139
+ lockfile / provisional version sources), `GuidanceStatus`,
140
+ `RuntimePrerequisite`, `AppSelection` (`bound` / `ambiguous` / `none`).
141
+ - `capabilities` — `CapabilityId`, `CapabilityStatus`, `CapabilityEntry`,
142
+ `PlatformCapabilityMatrix`, and the `CapabilityProvider` / registry seam
143
+ adapters use to report real support.
144
+ - `policy` — `PermissionScope` grants (`workspace`, `runtime-actions`,
145
+ `visual-capture`, `evidence-retain`, `external-effects`), `PolicyStore` with
146
+ task-authorization baseline, action-time checks, expiry, revocation, and an
147
+ elicitation channel for explicit developer grants. `visual-capture` is never
148
+ baseline-granted.
149
+ - `sessions` — `SessionBinding` (`owned` / `attached`, `starting` / `ready` /
150
+ `disconnected` / `stopped`), `Job` (`queued` → `running` → terminal, with
151
+ `cancelling` / `cancelled`), request-key deduplication, and ownership-safe
152
+ `end` / `shutdown`.
153
+ - `evidence` — `EvidenceRecord` with provenance (app, platform, target,
154
+ session, build identity, time, interaction mechanism), session-scoped
155
+ default retention, `retain` with expiration, ownership-safe deletion, and
156
+ `TRANSCRIPT_LIMITATION_NOTE`. `isOsInputEvidence` is the guard that keeps
157
+ handler-dispatch results from being labeled OS-input verification.
158
+ - `status` — `ProjectStatusReport`, the shape `xplat_project_status` returns.
159
+ - `code` — the `CodeProvider` seam the context's `code` slot consumes, plus
160
+ `CodeHit` / `CodeSearchResult` / provenance and freshness shapes.
161
+ - `boundaries` — `PRODUCT_BOUNDARIES`, the advertised out-of-scope list.
162
+ - `actions` — the app-declared development-action wire contract (package G):
163
+ `DevActionDeclaration` (stable id, title, description, runtime-validatable
164
+ input/result field contracts, disclosed effects, availability, execution
165
+ policy), `DevActionRegistryAnnouncement` (instance identity + revision +
166
+ declarations), invocation request/outcome types, and the field-subset
167
+ validators both sides share. The app announces its registry through a
168
+ dev-bridge (`xplatDevBridge` vite middleware in `@octane-xplat/cli`, included
169
+ in the `xplatNative` preset); the server scans the app's bridge session
170
+ files under `node_modules/.cache/xplat/dev-bridge/` and polls live
171
+ announcements — an empty registry is never confused with a disconnected app.
172
+
173
+ ## Runtime (work item D)
174
+
175
+ The shared `runtime/` service (in `packages/cli/src/mcp/runtime/`, re-exported
176
+ here) implements build/launch/attach/logs/cleanup for consumer apps
177
+ on all six platforms. Adapters (`runtime/adapters/*`) own platform
178
+ mechanics and always spawn the app's own toolchain — `pnpm exec vite`,
179
+ `pnpm exec ns run …`, the app's installed `@octane-xplat/cli` dev entry, or
180
+ its `dev` script — so a session behaves like the app's installed framework
181
+ version. `service.mjs` owns the shared contract behavior: explicit
182
+ app+target binding, `target_busy` contention, job dedupe/cooperative
183
+ cancel, ownership-safe cleanup, bounded logs, and capability-matrix entries.
184
+
185
+ | Platform | Launch | Attach | Host gate |
186
+ | -------------- | ----------------------------------------------------------------- | -------------------------------------------------- | ---------- |
187
+ | web / Chromium | `pnpm exec vite` + headless Chromium (devtools endpoint reported) | probe common dev ports — identity heuristic | none |
188
+ | iOS | `ns run ios --device <udid>` | booted sim + app running → `simctl log stream` tap | macOS |
189
+ | Android | `ns run android --device <serial>` | package running → `adb logcat --pid` tap | none (adb) |
190
+ | macOS | `xplat.targets.macos.dev` → CLI dev entry, else `pnpm run dev` | pgrep on app root — no scrollback | macOS |
191
+ | Linux | `pnpm run dev` (app-owned script) | pgrep on app root | Linux |
192
+ | Windows | `ns run windows` | not yet qualified | Windows |
193
+
194
+ Readiness is probe/marker-confirmed (`ready` session state) — HTTP 200 for
195
+ web, the host-start marker for macOS, `Successfully synced application` /
196
+ first `CONSOLE` line for `ns run`. A timeout or pre-ready exit fails the job
197
+ and ends the owned session honestly. Chromium is located via
198
+ `XPLAT_MCP_CHROMIUM`, the Playwright browser cache, or system installs;
199
+ without it the web session still reports the server URL with an explicit
200
+ `browser.launched: false`.
201
+
202
+ Physical iOS devices, Windows attach detection, and non-simulator iOS log
203
+ taps are reported limitations, not silent gaps — the capability matrix
204
+ carries the detail and an alternative.
205
+
206
+ ## Inspect and interact with Chromium
207
+
208
+ This development package adds `xplat_inspect`, `xplat_interact`, and
209
+ `xplat_screenshot` for the Chromium browser started by `xplat_launch`.
210
+ Select a DOM `id` or shared `testID` explicitly; duplicate names fail.
211
+ Inspection returns bounded text/value/geometry, a document identity, and
212
+ session/build provenance. Main-document DOM hosts are the qualified surface;
213
+ shadow roots, frames, component ownership and signals are excluded.
214
+
215
+ ```json
216
+ {
217
+ "name": "xplat_inspect",
218
+ "arguments": {
219
+ "sessionId": "SESSION_FROM_LAUNCH",
220
+ "control": { "kind": "testID", "name": "counter.increment" }
221
+ }
222
+ }
223
+ ```
224
+
225
+ Pass the returned `documentId` when interacting. A `dom-event` interaction
226
+ clicks the control center through CDP. `os-input` sends Space or Enter through
227
+ the macOS event system to the owned headed Chromium process. It requires
228
+ Swift, OS event-posting permission, and a desktop-input reservation. The
229
+ server does not open permission prompts. Inspect the resulting app state
230
+ separately; sending input alone is not an assertion.
231
+
232
+ ```json
233
+ {
234
+ "name": "xplat_interact",
235
+ "arguments": {
236
+ "sessionId": "SESSION_FROM_LAUNCH",
237
+ "documentId": "DOCUMENT_FROM_INSPECTION",
238
+ "control": { "kind": "testID", "name": "counter.increment" },
239
+ "mechanism": "os-input",
240
+ "key": "Space"
241
+ }
242
+ }
243
+ ```
244
+
245
+ Screenshot requests require an explicit current-task `visual-capture` grant
246
+ before connecting or sending a capture command. No grant means no capture.
247
+ Password fields, hidden inputs and hosts marked `data-xplat-private` are
248
+ redacted in nonvisual inspection; this does not guarantee complete redaction
249
+ of arbitrary application data.
250
+
251
+ ```json
252
+ { "name": "xplat_screenshot", "arguments": { "sessionId": "SESSION_FROM_LAUNCH" } }
253
+ ```
254
+
255
+ ## Run the consumer acceptance lane
256
+
257
+ Use a fresh directory outside your existing app. These commands scaffold the
258
+ normal web starter and add one app-owned development action. The in-repo lane
259
+ links framework packages to this checkout; other dependencies
260
+ install normally. The runner checks docs and current code, build/readiness,
261
+ the counter's `0 → 1` transition, action discovery/invocation, screenshot
262
+ denial, logs, and owned browser/server/profile cleanup. It also checks that
263
+ the fixture declaration and handler are absent from production artifacts.
264
+
265
+ ```sh
266
+ node packages/mcp/tests/prepare-consumer.mjs /tmp/new-xplat-mcp-app
267
+ pnpm --dir /tmp/new-xplat-mcp-app install
268
+ node packages/mcp/tests/consumer-workflow.mjs /tmp/new-xplat-mcp-app
269
+ ```
270
+
271
+ The default run supplies browser-event evidence. On macOS, reserve the full
272
+ resource set and add `--os-input` for a headed Chromium OS keyboard journey.
273
+ Only the owned browser receives the events; the helper attempts to restore the
274
+ previously active application and reports when restoration is unconfirmed.
275
+ The runner captures no images.
276
+
277
+ ```sh
278
+ goddard-agent resource run --json '{"resources":{"native_builds":1,"desktop_input":1},"purpose":"MCP consumer OS keyboard acceptance"}' -- node packages/mcp/tests/consumer-workflow.mjs /tmp/new-xplat-mcp-app --os-input
279
+ ```
280
+
281
+ The packed-artifact lane qualifies the same checks against release-evidence
282
+ packaging: `prepare-packed-consumer.mjs` packs the needed packages, scaffolds
283
+ through the packed `create-octane-xplat` bin, installs `file:` tarballs (no
284
+ registry/latest resolution for framework packages), and tags the framework
285
+ HEAD as `v<installed version>` in a local bare release fixture. The runner
286
+ spawns the consumer's own installed `xplat-mcp` bin over stdin/stdout and
287
+ redirects the canonical release-repo URL to the fixture through a per-process
288
+ `url.insteadOf` (never user git config). Snapshot cache and evidence roots are
289
+ isolated under the work dir. OS keyboard input additionally requires an
290
+ unlocked console — the window server refuses activation while the screen is
291
+ locked, and the run reports that block honestly.
292
+
293
+ ```sh
294
+ goddard-agent resource run --json '{"resources":{"native_builds":1,"desktop_input":1},"purpose":"packed MCP stdio consumer acceptance"}' -- bash -c '
295
+ node packages/mcp/tests/prepare-packed-consumer.mjs /tmp/xplat-packed-mcp &&
296
+ node packages/mcp/tests/packed-stdio-consumer.mjs /tmp/xplat-packed-mcp'
297
+ ```
298
+
299
+ For an existing consumer, declare actions behind the development build gate.
300
+ Registration automatically starts the bridge when the dev-server endpoint is
301
+ present. The action's contracts and effects appear in `xplat_dev_actions`;
302
+ no action-specific MCP tool definition is needed.
303
+
304
+ ```ts
305
+ if (import.meta.env.DEV) {
306
+ const { registerDevAction } = await import('@octane-xplat/platform/dev-actions')
307
+ registerDevAction({
308
+ id: 'fixture.describe-counter',
309
+ title: 'Describe the counter',
310
+ description: 'Identify this test fixture without external effects.',
311
+ result: { fields: { label: { type: 'string' } } },
312
+ effects: { summary: 'Read fixture identity.' },
313
+ run: () => ({ label: 'starter-counter' }),
314
+ })
315
+ }
316
+ ```
317
+
318
+ ## Remaining acceptance
319
+
320
+ The Chromium lane is an incremental milestone. All six platforms remain
321
+ required by the original plan. Native inspection/input adapters, attached
322
+ Chromium inspection, approved screenshot qualification, and the complete
323
+ platform-by-capability acceptance matrix remain unfinished. Runtime adapters
324
+ and honest unavailable responses do not establish native behavior.
325
+
326
+ | Target | Inspection/input qualification boundary |
327
+ | --------------- | ------------------------------------------------------------------------------ |
328
+ | Chromium | Owned main-document DOM; browser events plus macOS scoped OS keyboard path |
329
+ | iOS / Android | Native host inspection and OS-input adapters still required |
330
+ | macOS | AppKit host inspection/input still required; Chromium on macOS is web evidence |
331
+ | Linux / Windows | Target-host adapters and independent consumer qualification still required |
332
+
333
+ ## Boundaries
334
+
335
+ This server complements the agent's editing tools. It is not an autonomous
336
+ code editor or deployment system: no arbitrary shell execution, no arbitrary
337
+ runtime expression evaluation, no universal component tree, no hook/signal
338
+ inspection, no profiling API, no cloud indexing by default, no app-store
339
+ operations, no credential management. Screenshots are permission-gated —
340
+ capability existence never implies capture approval.
package/package.json CHANGED
@@ -1,21 +1,47 @@
1
1
  {
2
2
  "name": "@octane-xplat/mcp",
3
- "version": "0.0.1",
4
- "description": "Reservation stub for @octane-xplat/mcp \u2014 no supported API yet.",
3
+ "version": "0.15.0",
4
+ "description": "MCP server for Octane Xplat consumer apps — project identity, capability discovery, permission and evidence contracts.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/octane-xplat/octane-xplat",
9
9
  "directory": "packages/mcp"
10
10
  },
11
- "main": "./index.js",
12
- "types": "./index.d.ts",
11
+ "bin": {
12
+ "xplat-mcp": "./src/xplat-mcp.mjs"
13
+ },
13
14
  "files": [
14
- "README.md",
15
- "index.js",
16
- "index.d.ts"
15
+ "src",
16
+ "README.md"
17
17
  ],
18
+ "type": "module",
19
+ "exports": {
20
+ "./contracts": {
21
+ "types": "./src/contracts/index.d.ts",
22
+ "default": "./src/contracts/index.mjs"
23
+ },
24
+ "./contracts/*": {
25
+ "types": "./src/contracts/*.d.ts",
26
+ "default": "./src/contracts/*.mjs"
27
+ }
28
+ },
18
29
  "publishConfig": {
19
30
  "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "1.31.0",
34
+ "@octane-xplat/cli": "0.15.0",
35
+ "yaml": "2.9.1",
36
+ "zod": "4.6.5"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "5.9.3"
40
+ },
41
+ "scripts": {
42
+ "start": "node src/xplat-mcp.mjs",
43
+ "test": "node --test tests/*.test.mjs",
44
+ "test:consumer": "node tests/consumer-workflow.mjs",
45
+ "typecheck": "tsc -p tsconfig.json"
20
46
  }
21
- }
47
+ }
@@ -0,0 +1,2 @@
1
+ // Moved to @octane-xplat/cli (shared agent service layer); re-exported for compatibility.
2
+ export * from '@octane-xplat/cli/mcp/actions/bridge'
@@ -0,0 +1,209 @@
1
+ /**
2
+ * App-declared development-action contract (design: plans/octane-xplat-mcp.md,
3
+ * "App-declared development actions").
4
+ *
5
+ * The app owns action semantics; the framework owns discovery and invocation.
6
+ * A dev-mode app announces a registry snapshot (identity + revision + typed
7
+ * declarations) over a platform bridge; this server lists the live registry
8
+ * and invokes actions through that same bridge. The declaration is the single
9
+ * source of truth: handler, runtime-validatable input/result contracts, and
10
+ * metadata all travel in one announcement — there are no MCP-side per-action
11
+ * definitions.
12
+ *
13
+ * Wire invariants:
14
+ * - Every registry carries `appInstanceId` — each running app realm (tab,
15
+ * window, device process) is a distinct invocation destination. Calls are
16
+ * never silently shared or redirected.
17
+ * - `revision` increments on every registry change (add/change/remove, owner
18
+ * disposal, hot reload). Invocation presents the revision it read; a stale
19
+ * revision is rejected with `stale_identity` before anything runs.
20
+ * - Announcements are heartbeats: a stopped heartbeat marks the instance
21
+ * `disconnected` — a live app with zero actions still announces, so an
22
+ * empty registry is always distinguishable from a disconnected runtime.
23
+ */
24
+
25
+ import type { ContractError } from './errors'
26
+ import type { Platform } from './identity'
27
+
28
+ export const DEV_ACTIONS_PROTOCOL = 'xplat-dev-actions'
29
+ export const DEV_ACTIONS_PROTOCOL_VERSION = 1
30
+ /** Heartbeat interval the app-side bridge uses; instances quieter than this ×3 are stale. */
31
+ export const DEV_ACTIONS_ANNOUNCE_INTERVAL_MS: number
32
+ export const DEV_ACTIONS_STALE_AFTER_MS: number
33
+ /** Bounded pending-invocation queue per instance; saturation returns `target_busy`. */
34
+ export const DEV_ACTIONS_QUEUE_LIMIT: number
35
+ /** Cap on overlapping invocations per instance (declared `overlap` actions). */
36
+ export const DEV_ACTIONS_OVERLAP_LIMIT: number
37
+ /** Default and maximum execution timeouts. */
38
+ export const DEV_ACTIONS_DEFAULT_TIMEOUT_MS: number
39
+ export const DEV_ACTIONS_MAX_TIMEOUT_MS: number
40
+
41
+ // ---------- declaration contract ----------
42
+
43
+ /** Runtime-validatable field subset. `fields`/`items` allow one level of structure. */
44
+ export interface DevActionField {
45
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array'
46
+ description?: string
47
+ /** Applied when the caller omits the field. */
48
+ default?: unknown
49
+ /** Closed value set. */
50
+ enum?: readonly unknown[]
51
+ minLength?: number
52
+ maxLength?: number
53
+ /** Regular expression source (must compile). */
54
+ pattern?: string
55
+ minimum?: number
56
+ maximum?: number
57
+ minItems?: number
58
+ maxItems?: number
59
+ /** For `object`: named nested fields. Defaults apply to top-level fields only. */
60
+ fields?: Record<string, DevActionField>
61
+ /** For `array`: element contract. */
62
+ items?: DevActionField
63
+ }
64
+
65
+ /** Named typed fields. `{}` (or omitted) means the action takes no arguments. */
66
+ export interface DevActionFields {
67
+ fields?: Record<string, DevActionField>
68
+ }
69
+
70
+ /**
71
+ * Disclosed effect categories — the agent reads these to decide whether an
72
+ * action fits the existing authorization. `externalEffects` maps to the
73
+ * `external-effects` permission scope and is checked at invocation time.
74
+ */
75
+ export interface DevActionEffects {
76
+ /** Plain-language summary of what the action changes. */
77
+ summary: string
78
+ auth?: boolean
79
+ navigation?: boolean
80
+ appState?: boolean
81
+ testData?: boolean
82
+ /** Effects outside the app itself — external test services, destructive resets. */
83
+ externalEffects?: boolean
84
+ }
85
+
86
+ export type DevActionConcurrency =
87
+ /** Default: actions for one app instance run one at a time through a bounded queue. */
88
+ | 'serialized'
89
+ /** The app declares this action safe to overlap with other executions. */
90
+ | 'overlap'
91
+
92
+ export interface DevActionExecution {
93
+ concurrency?: DevActionConcurrency
94
+ /** Cooperative cancellation supported by the handler. */
95
+ cancellable?: boolean
96
+ /** Safe to retry (idempotent). Non-retryable actions never auto-rerun. */
97
+ retryable?: boolean
98
+ /** Per-action execution deadline; bounded by DEV_ACTIONS_MAX_TIMEOUT_MS. */
99
+ timeoutMs?: number
100
+ }
101
+
102
+ export interface DevActionDeclaration {
103
+ /** Stable machine identity, e.g. `auth.login-as-test-admin`. */
104
+ id: string
105
+ title: string
106
+ /** When to use it and what successful completion means. */
107
+ description: string
108
+ input?: DevActionFields
109
+ result?: DevActionFields
110
+ effects: DevActionEffects
111
+ availability?: {
112
+ /** Platforms this action applies to; absent = all. */
113
+ platforms?: Platform[]
114
+ /** Readiness conditions outside platform selection (free text). */
115
+ readyWhen?: string
116
+ }
117
+ execution?: DevActionExecution
118
+ }
119
+
120
+ /** Declared action plus its live availability, as announced by the app. */
121
+ export interface DevActionListing extends DevActionDeclaration {
122
+ availability?: {
123
+ platforms?: Platform[]
124
+ readyWhen?: string
125
+ /** Live readiness verdict from the app. */
126
+ ready: boolean
127
+ /** Why it is unavailable right now. */
128
+ reason?: string
129
+ }
130
+ }
131
+
132
+ // ---------- registry announcement ----------
133
+
134
+ export interface DevActionRegistryAnnouncement {
135
+ protocol: typeof DEV_ACTIONS_PROTOCOL
136
+ version: number
137
+ /** Unique per app realm — survives hot reload, never crosses processes. */
138
+ appInstanceId: string
139
+ /** App-supplied label for the developer, e.g. package name or window title. */
140
+ appLabel?: string
141
+ platform: Platform
142
+ revision: number
143
+ announcedAt: string
144
+ actions: DevActionListing[]
145
+ }
146
+
147
+ // ---------- invocation ----------
148
+
149
+ export interface DevActionInvocationRequest {
150
+ /** Stable request identity — retries with the same requestId must not re-run the effect. */
151
+ requestId: string
152
+ appInstanceId: string
153
+ actionId: string
154
+ /** The registry revision the caller discovered; rejected when stale. */
155
+ registryRevision: number
156
+ input: Record<string, unknown>
157
+ }
158
+
159
+ export type DevActionInvocationStatus =
160
+ | 'succeeded'
161
+ | 'failed'
162
+ | 'cancelled'
163
+ /** The app may or may not have run the action; reported, never silently retried. */
164
+ | 'unknown'
165
+
166
+ export interface DevActionOutcome {
167
+ requestId: string
168
+ status: DevActionInvocationStatus
169
+ /** Structured result validated against the declared result contract. */
170
+ result?: Record<string, unknown>
171
+ /** Machine-readable failure detail; mapped to ContractError codes server-side. */
172
+ error?: { code: string; message: string }
173
+ /** What the app knows changed before a failure/cancellation; absent = unknown. */
174
+ partialEffects?: string[]
175
+ completedAt: string
176
+ }
177
+
178
+ /** What `validateActionInput` returns. */
179
+ export type DevActionInputResult =
180
+ | { ok: true; value: Record<string, unknown> }
181
+ | { ok: false; errors: string[] }
182
+
183
+ // ---------- validators (shared with the app-side implementation) ----------
184
+
185
+ /** Declaration sanity: id shape, required metadata, compilable field patterns. */
186
+ export declare function validateActionDeclaration(action: unknown): string[]
187
+
188
+ /** Type-check + apply defaults + reject undeclared fields. */
189
+ export declare function validateActionInput(
190
+ contract: DevActionFields | undefined,
191
+ input: unknown,
192
+ ): DevActionInputResult
193
+
194
+ /** Result-contract validation. An invalid output fails explicitly — it does not undo a ran action. */
195
+ export declare function validateActionOutput(
196
+ contract: DevActionFields | undefined,
197
+ output: unknown,
198
+ ): DevActionInputResult
199
+
200
+ /** Structural check on an announcement payload. */
201
+ export declare function isDevActionAnnouncement(
202
+ value: unknown,
203
+ ): value is DevActionRegistryAnnouncement
204
+
205
+ /** Live availability for an announced action on a given platform. */
206
+ export declare function actionAvailability(
207
+ action: DevActionListing,
208
+ platform: Platform,
209
+ ): { ready: boolean; reason?: string }
@@ -0,0 +1,3 @@
1
+ // Runtime contract moved to @octane-xplat/cli (shared agent service layer).
2
+ // This shim preserves the @octane-xplat/mcp ./contracts export surface.
3
+ export * from '@octane-xplat/cli/mcp/contracts/actions'
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Product boundaries — what this server deliberately does not do, surfaced in
3
+ * the project-status report so agents don't probe for excluded features.
4
+ */
5
+
6
+ export const PRODUCT_BOUNDARIES: readonly string[]
@@ -0,0 +1,3 @@
1
+ // Runtime contract moved to @octane-xplat/cli (shared agent service layer).
2
+ // This shim preserves the @octane-xplat/mcp ./contracts export surface.
3
+ export * from '@octane-xplat/cli/mcp/contracts/boundaries'