@happyvertical/smrt-dev-mcp 0.51.23 → 0.51.33

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 (47) hide show
  1. package/AGENTS.md +40 -3
  2. package/README.md +35 -3
  3. package/dist/dev-plane.d.ts.map +1 -1
  4. package/dist/dev-plane.js +6 -4
  5. package/dist/dev-plane.js.map +1 -1
  6. package/dist/{http-TeoaK9Xr.js → http-C302xXnO.js} +5 -4
  7. package/dist/{http-TeoaK9Xr.js.map → http-C302xXnO.js.map} +1 -1
  8. package/dist/http.d.ts +18 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +31 -454
  11. package/dist/index.js.map +1 -1
  12. package/dist/introspect-project-Peu2LC2T.js +490 -0
  13. package/dist/introspect-project-Peu2LC2T.js.map +1 -0
  14. package/dist/knowledge/index.d.ts +3 -1
  15. package/dist/knowledge/index.d.ts.map +1 -1
  16. package/dist/knowledge/mcp-docs.d.ts +1 -1
  17. package/dist/{knowledge-DYe5lCfq.js → knowledge-CvNlDTLz.js} +9 -6
  18. package/dist/knowledge-CvNlDTLz.js.map +1 -0
  19. package/dist/knowledge.js +2 -2
  20. package/dist/observation-CqE0tBgS.js +386 -0
  21. package/dist/observation-CqE0tBgS.js.map +1 -0
  22. package/dist/runtime-test-fixture.d.ts +3 -0
  23. package/dist/runtime-test-fixture.d.ts.map +1 -0
  24. package/dist/runtime.d.ts +1 -1
  25. package/dist/runtime.js +2 -1
  26. package/dist/runtime.js.map +1 -1
  27. package/dist/{tool-catalog-Zl-bcimC.js → tool-catalog-RHq7nyCm.js} +26 -2
  28. package/dist/tool-catalog-RHq7nyCm.js.map +1 -0
  29. package/dist/tool-catalog.d.ts +1 -1
  30. package/dist/tool-catalog.d.ts.map +1 -1
  31. package/dist/tools/introspect-project.d.ts.map +1 -1
  32. package/dist/tools/runtime/boot.d.ts.map +1 -1
  33. package/dist/tools/runtime/connection.d.ts +23 -3
  34. package/dist/tools/runtime/connection.d.ts.map +1 -1
  35. package/dist/tools/runtime/observation.d.ts +17 -12
  36. package/dist/tools/runtime/observation.d.ts.map +1 -1
  37. package/dist/tools/runtime/project-runtime.d.ts +16 -0
  38. package/dist/tools/runtime/project-runtime.d.ts.map +1 -0
  39. package/dist/tools/runtime/tools.d.ts +23 -3
  40. package/dist/tools/runtime/tools.d.ts.map +1 -1
  41. package/dist/tools-ma2JFPVY.js +568 -0
  42. package/dist/tools-ma2JFPVY.js.map +1 -0
  43. package/package.json +11 -9
  44. package/dist/knowledge-DYe5lCfq.js.map +0 -1
  45. package/dist/observation-VJgHaPps.js +0 -802
  46. package/dist/observation-VJgHaPps.js.map +0 -1
  47. package/dist/tool-catalog-Zl-bcimC.js.map +0 -1
package/AGENTS.md CHANGED
@@ -61,7 +61,7 @@ discovery alone sees nothing. Every installed `@happyvertical/smrt-*` package,
61
61
  plus the known SDK packages, is resolved from the project's and each workspace
62
62
  package's `node_modules/@happyvertical` scope directory and marked
63
63
  `isInstalledDependency`. The filesystem enumeration and canonical doc reader are shared with CLI snapshots
64
- through `smrt-core/knowledge`; enrichment stays here. This is an enumeration, not a walk: pnpm materializes
64
+ through `smrt-scanner/knowledge`; enrichment stays here. This is an enumeration, not a walk: pnpm materializes
65
65
  a store whose entries link back out to their siblings, so a descent reads the
66
66
  same package once per path that reaches it. Real paths deduplicate the result;
67
67
  each package is still read through its `node_modules` path, because a realpath
@@ -116,6 +116,32 @@ Skill-aware harnesses can parse the frontmatter; other harnesses can ignore it.
116
116
  deterministic context, inspect the actual diff, and produce a findings-first
117
117
  review.
118
118
 
119
+
120
+ ## Project runtime prerequisite
121
+
122
+ The standalone MCP installation includes static tooling and the scanner, not
123
+ SMRT core, SQL drivers, AI, or filesystem providers. All 21 tools remain listed.
124
+ Code generation, knowledge/context tools, agent skills, and introspection of a
125
+ built manifest work without an installed runtime.
126
+
127
+ Source-scan enrichment (`introspect-project` without a built manifest) and the
128
+ runtime tools require `@happyvertical/smrt-core` installed in the selected
129
+ project. Install the project's dependencies first. The server resolves core's
130
+ public exports from that project and resolves SQL/config from the same core
131
+ installation using normal Node resolution, including hoisted workspace dependencies.
132
+ It never retries resolution from the MCP server module location. Use `directory`
133
+ for introspection, `projectPath` for runtime tools, or launch from the project
134
+ root. HTTP `--project` and the in-app host pin the project explicitly.
135
+
136
+ A server process loads one runtime project. A request for another project fails
137
+ with `runtime_project_mismatch`; start a separate server or restart it for that
138
+ project. Missing/incompatible runtime installations return an actionable,
139
+ sanitized `runtime_dependency_unavailable` diagnostic. Static tools remain usable.
140
+ No DB configuration still returns the existing static-only diagnostic. Runtime
141
+ module import executes installed framework code, never the project's app entry.
142
+ Source enrichment runs in an isolated process with captured logs, preserving
143
+ schema/tenant/inheritance detail without writing onto MCP stdout.
144
+
119
145
  ## Usage
120
146
 
121
147
  ```jsonc
@@ -177,10 +203,11 @@ launcher or a small wrapper script with an absolute Node path.
177
203
  validate against the packaged `schemas/agent-plugins-1.0.0/` snapshots
178
204
  offline, and do not add streamable HTTP until #2147 provides the endpoint.
179
205
  - **Field type mapping**: `text`, `integer`, `decimal`, `boolean`, `datetime`, `json` — maps to SMRT field helpers (but prefer TypeScript defaults per framework convention)
180
- - **Never call `ManifestGenerator` from a tool path**: `generateSchemas` and
206
+ - **Capture manifest-enrichment output**: `generateSchemas` and
181
207
  friends write progress lines to **stdout** through the SDK logger, which is the
182
208
  MCP server's JSON-RPC channel. Suppressing `console.log` is not enough. The
183
- knowledge scanner fallback therefore skips schema enrichment, so
209
+ source introspection therefore captures an isolated process’s output. The
210
+ knowledge scanner fallback skips schema enrichment, so
184
211
  scanner-provenance packages carry no `columnType` and contribute 0 to the
185
212
  `uuidColumns` fact — `objectSource` makes that visible.
186
213
  - **Aggregate manifests poison relationship facts**: a runtime
@@ -241,3 +268,13 @@ launcher or a small wrapper script with an absolute Node path.
241
268
  - **Coverage and diagnostics are computed before scope filtering**: they answer
242
269
  "did discovery work", which is a whole-workspace property. Deriving them from
243
270
  the scoped subset made `scope: 'sdk'` report a false discovery failure.
271
+
272
+ ## Validation
273
+
274
+ Run package `test`, `typecheck`, and `build`, then
275
+ `pnpm --filter @happyvertical/smrt-dev-mcp verify:pack`. The packaging gate packs
276
+ MCP/scanner/types, installs only their production dependencies into a fresh
277
+ external npm consumer/cache, rejects a core/SQL/files/AI closure, and exercises
278
+ stdio initialization, all 21 tool declarations, static calls, and missing-runtime
279
+ setup. It requires registry access. Core knowledge compatibility tests cover the
280
+ scanner-owned discovery/graph algorithms through the existing core entrypoints.
package/README.md CHANGED
@@ -4,6 +4,32 @@ Development MCP server for the s-m-r-t framework providing code generation,
4
4
  project introspection, deterministic ecosystem knowledge, and portable review
5
5
  or architecture prompt bundles.
6
6
 
7
+
8
+ ## Project runtime prerequisite
9
+
10
+ The standalone MCP installation includes static tooling and the scanner, not
11
+ s-m-r-t core, SQL drivers, AI, or filesystem providers. All 21 tools remain listed.
12
+ Code generation, knowledge/context tools, agent skills, and introspection of a
13
+ built manifest work without an installed runtime.
14
+
15
+ Source-scan enrichment (`introspect-project` without a built manifest) and the
16
+ runtime tools require `@happyvertical/smrt-core` installed in the selected
17
+ project. Install the project's dependencies first. The server resolves core's
18
+ public exports from that project and resolves SQL/config from the same core
19
+ installation using normal Node resolution, including hoisted workspace dependencies.
20
+ It never retries resolution from the MCP server module location. Use `directory`
21
+ for introspection, `projectPath` for runtime tools, or launch from the project
22
+ root. HTTP `--project` and the in-app host pin the project explicitly.
23
+
24
+ A server process loads one runtime project. A request for another project fails
25
+ with `runtime_project_mismatch`; start a separate server or restart it for that
26
+ project. Missing/incompatible runtime installations return an actionable,
27
+ sanitized `runtime_dependency_unavailable` diagnostic. Static tools remain usable.
28
+ No DB configuration still returns the existing static-only diagnostic. Runtime
29
+ module import executes installed framework code, never the project's app entry.
30
+ Source enrichment runs in an isolated process with captured logs, preserving
31
+ schema/tenant/inheritance detail without writing onto MCP stdout.
32
+
7
33
  ## Installation
8
34
 
9
35
  New to agent-assisted s-m-r-t development? Start with the end-to-end guide on
@@ -439,6 +465,7 @@ returns a successful static-only result.
439
465
 
440
466
  | Parameter | Type | Required | Description |
441
467
  |-----------|------|----------|-------------|
468
+ | `projectPath` | `string` | No | Project root with the installed s-m-r-t runtime (default: server working directory) |
442
469
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
443
470
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
444
471
  | `limit` | `number` | No | Row budget for result lists (default 50, capped at 500) |
@@ -452,6 +479,7 @@ read-only.
452
479
 
453
480
  | Parameter | Type | Required | Description |
454
481
  |-----------|------|----------|-------------|
482
+ | `projectPath` | `string` | No | Project root with the installed s-m-r-t runtime (default: server working directory) |
455
483
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
456
484
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
457
485
  | `limit` | `number` | No | Row budget for result lists (default 50, capped at 500) |
@@ -465,6 +493,7 @@ read-only.
465
493
 
466
494
  | Parameter | Type | Required | Description |
467
495
  |-----------|------|----------|-------------|
496
+ | `projectPath` | `string` | No | Project root with the installed s-m-r-t runtime (default: server working directory) |
468
497
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
469
498
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
470
499
  | `limit` | `number` | No | Row budget for result lists (default 50, capped at 500) |
@@ -478,6 +507,7 @@ read. Runtime provenance; read-only.
478
507
 
479
508
  | Parameter | Type | Required | Description |
480
509
  |-----------|------|----------|-------------|
510
+ | `projectPath` | `string` | No | Project root with the installed s-m-r-t runtime (default: server working directory) |
481
511
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
482
512
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
483
513
  | `limit` | `number` | No | Row budget for result lists (default 50, capped at 500) |
@@ -489,6 +519,7 @@ filterable by table and tenant. Runtime provenance; read-only.
489
519
 
490
520
  | Parameter | Type | Required | Description |
491
521
  |-----------|------|----------|-------------|
522
+ | `projectPath` | `string` | No | Project root with the installed s-m-r-t runtime (default: server working directory) |
492
523
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
493
524
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
494
525
  | `since` | `number` | No | Cursor to read after (default 0) |
@@ -505,6 +536,7 @@ static manifest tools. Read-only.
505
536
 
506
537
  | Parameter | Type | Required | Description |
507
538
  |-----------|------|----------|-------------|
539
+ | `projectPath` | `string` | No | Project root with the installed s-m-r-t runtime (default: server working directory) |
508
540
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
509
541
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
510
542
 
@@ -518,7 +550,7 @@ provenance (`booted (registry)`); read-only.
518
550
 
519
551
  | Parameter | Type | Required | Description |
520
552
  |-----------|------|----------|-------------|
521
- | `projectPath` | `string` | No | Project root to boot manifests from (default: the server working directory; ignored by the HTTP host, which boots once) |
553
+ | `projectPath` | `string` | No | Project root to boot manifests from (default: the server working directory; rejected after a different runtime project loads; pinned by the HTTP host) |
522
554
  | `objects` | `string[]` | No | Restrict field/method detail to these simple or qualified object names |
523
555
  | `detail` | `boolean` | No | Include field and method detail for every object (default: only when `objects` is given) |
524
556
  | `cursor` | `string` | No | Resume after this qualified object name; pass a previous response's `page.nextCursor` |
@@ -531,7 +563,7 @@ DDL the registry would generate for it. Booted provenance; read-only.
531
563
 
532
564
  | Parameter | Type | Required | Description |
533
565
  |-----------|------|----------|-------------|
534
- | `projectPath` | `string` | No | Project root to boot manifests from (default: the server working directory; ignored by the HTTP host, which boots once) |
566
+ | `projectPath` | `string` | No | Project root to boot manifests from (default: the server working directory; rejected after a different runtime project loads; pinned by the HTTP host) |
535
567
  | `name` | `string` | Yes | Simple or qualified object name |
536
568
  | `engine` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Engine for the DDL preview (default: registry default) |
537
569
 
@@ -544,7 +576,7 @@ configured connection it returns a successful static-only result.
544
576
 
545
577
  | Parameter | Type | Required | Description |
546
578
  |-----------|------|----------|-------------|
547
- | `projectPath` | `string` | No | Project root to boot manifests from (default: the server working directory; ignored by the HTTP host, which boots once) |
579
+ | `projectPath` | `string` | No | Project root to boot manifests from (default: the server working directory; rejected after a different runtime project loads; pinned by the HTTP host) |
548
580
  | `dbUrl` | `string` | No | Optional dev database URL override (read-only diagnostics); prefer `SMRT_DEV_DB_URL` or `cli.database` config |
549
581
  | `dbType` | `'sqlite' \| 'postgres' \| 'duckdb'` | No | Optional engine hint for `dbUrl` or the environment connection; inferred from the URL scheme when omitted |
550
582
 
@@ -1 +1 @@
1
- {"version":3,"file":"dev-plane.d.ts","sourceRoot":"","sources":["../src/dev-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AA6BH,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,EAAE,CAAC,EAAE,gBAAgB,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,2CAA2C;IAC3C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtE;AAID,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,qMAWvB,CAAC;AAwFX,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CA+IjE"}
1
+ {"version":3,"file":"dev-plane.d.ts","sourceRoot":"","sources":["../src/dev-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAgCH,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,EAAE,CAAC,EAAE,gBAAgB,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ;IACvB,2CAA2C;IAC3C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtE;AAID,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,qMAWvB,CAAC;AA2FX,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CA+IjE"}
package/dist/dev-plane.js CHANGED
@@ -1,8 +1,8 @@
1
- import { i as SERVER_VERSION, r as SERVER_NAME, t as bearerMatches } from "./http-TeoaK9Xr.js";
2
- import { n as TOOLS } from "./tool-catalog-Zl-bcimC.js";
3
- import { c as runtimeMigrationStatus, d as runtimeScheduleHealth, i as RUNTIME_PROVENANCE, l as runtimeRecentChanges, n as runtimeRegistry, o as runtimeDispatchHealth, p as safeErrorMessage, r as runtimeSchemaDiff, s as runtimeJobHealth, t as runtimeObject, u as runtimeRegistryDrift } from "./observation-VJgHaPps.js";
1
+ import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, g as loadProjectCore, i as runtimeJobHealth, m as ProjectRuntimeResolutionError, o as runtimeRecentChanges, p as safeErrorMessage, r as runtimeDispatchHealth, s as runtimeRegistryDrift, t as RUNTIME_PROVENANCE } from "./tools-ma2JFPVY.js";
2
+ import { i as SERVER_VERSION, r as SERVER_NAME, t as bearerMatches } from "./http-C302xXnO.js";
3
+ import { n as TOOLS } from "./tool-catalog-RHq7nyCm.js";
4
+ import { i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-CqE0tBgS.js";
4
5
  import { Server, createMcpHandler } from "@modelcontextprotocol/server";
5
- import { snapshotRegistry } from "@happyvertical/smrt-core";
6
6
  //#region src/dev-plane.ts
7
7
  /**
8
8
  * In-app runtime dev-plane (#2782, Level 3 of #2307).
@@ -71,6 +71,8 @@ function json(status, body, headers = {}) {
71
71
  */
72
72
  async function registryLive(args, projectRoot) {
73
73
  const objects = Array.isArray(args.objects) ? args.objects.map(String) : typeof args.objects === "string" ? args.objects.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
74
+ const { snapshotRegistry } = await loadProjectCore(projectRoot);
75
+ if (typeof snapshotRegistry !== "function") throw new ProjectRuntimeResolutionError("@happyvertical/smrt-core");
74
76
  return {
75
77
  ok: true,
76
78
  coverage: null,
@@ -1 +1 @@
1
- {"version":3,"file":"dev-plane.js","names":[],"sources":["../src/dev-plane.ts"],"sourcesContent":["/**\n * In-app runtime dev-plane (#2782, Level 3 of #2307).\n *\n * The standalone `--http` host (Level 2) boots manifests in its own process.\n * This module runs *inside* the application's dev server, where the real\n * decorated `ObjectRegistry` is already live and the app's own database\n * configuration is at hand. It serves the same read-only runtime tools as\n * Level 2 plus `registry-live`, over two surfaces on one mount:\n *\n * - JSON: `GET <base>` (catalog), `GET|POST <base>/<tool>` (envelope);\n * arguments come from the query string or a JSON body.\n * - MCP: `POST <base>/mcp`, stateless Streamable HTTP (#2147).\n *\n * Security boundary: the caller (a generated SvelteKit route) only mounts\n * this in dev mode; every request must present `Authorization: Bearer` with\n * the configured token and arrive on a loopback `Host`. No principal exists,\n * so scope is fail-closed global-only. The catalog is positive: generated\n * CRUD, custom actions, `do()`, and tool-backed `is()` are never mounted.\n */\n\nimport { snapshotRegistry } from '@happyvertical/smrt-core';\nimport {\n type CallToolResult,\n createMcpHandler,\n Server,\n type Tool,\n} from '@modelcontextprotocol/server';\nimport { bearerMatches } from './http.js';\nimport { SERVER_NAME, SERVER_VERSION } from './server-info.js';\nimport { TOOLS } from './tool-catalog.js';\nimport { safeErrorMessage } from './tools/runtime/connection.js';\nimport {\n runtimeObject,\n runtimeRegistry,\n runtimeSchemaDiff,\n} from './tools/runtime/observation.js';\nimport {\n RUNTIME_PROVENANCE,\n type RuntimeToolEnvelope,\n runtimeDispatchHealth,\n runtimeJobHealth,\n runtimeMigrationStatus,\n runtimeRecentChanges,\n runtimeRegistryDrift,\n runtimeScheduleHealth,\n} from './tools/runtime/tools.js';\n\n/** Provenance label for the application's own live registry. */\nexport const LIVE_REGISTRY_PROVENANCE = 'live (app registry)';\n\nexport interface DevPlaneDatabase {\n url?: string;\n type?: string;\n}\n\nexport interface DevPlaneOptions {\n /** Bearer token every request must present. */\n token: string;\n /** Project root; used only to relativize paths in snapshots. */\n projectRoot?: string;\n /**\n * The app's database configuration (typically `getSmrtConfig(name).db`\n * from the generated `$lib/server/smrt`). Passed to the runtime tools as\n * `dbUrl`/`dbType`, so the SDK's connection cache hands back the same\n * handle the app uses. `:memory:` or absent means static-only answers.\n */\n db?: DevPlaneDatabase;\n}\n\nexport interface DevPlane {\n /** Tool names served, in catalog order. */\n tools: readonly string[];\n /** Serve one request mounted at `basePath` (e.g. `/api/_dev`). */\n handleRequest(request: Request, basePath: string): Promise<Response>;\n}\n\ntype ToolHandler = (args: Record<string, unknown>) => Promise<unknown>;\n\n/** Positive catalog: the nine Level 2 tools plus the in-process-only one. */\nexport const DEV_PLANE_TOOL_NAMES = [\n 'registry-live',\n 'runtime-registry',\n 'runtime-object',\n 'runtime-schema-diff',\n 'migration-status',\n 'job-health',\n 'schedule-health',\n 'dispatch-health',\n 'recent-changes',\n 'registry-drift',\n] as const;\n\nconst LOOPBACK = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);\n\nfunction hostIsLoopback(request: Request): boolean {\n const host = request.headers.get('host') ?? '';\n const hostname = host.replace(/:\\d+$/, '').toLowerCase();\n if (!LOOPBACK.has(hostname)) return false;\n const origin = request.headers.get('origin');\n if (!origin) return true;\n try {\n return LOOPBACK.has(new URL(origin).hostname.toLowerCase());\n } catch {\n return false;\n }\n}\n\nfunction json(\n status: number,\n body: unknown,\n headers: Record<string, string> = {},\n): Response {\n return new Response(JSON.stringify(body, null, 2), {\n status,\n headers: { 'content-type': 'application/json', ...headers },\n });\n}\n\n/**\n * `registry-live`: snapshot the registry as the app has it right now. No\n * manifest boot; the app's own imports (via `$lib/server/smrt`) registered\n * these classes, and Vite's SSR HMR re-registers them on change.\n */\nasync function registryLive(\n args: Record<string, unknown>,\n projectRoot: string | undefined,\n): Promise<RuntimeToolEnvelope> {\n const objects = Array.isArray(args.objects)\n ? args.objects.map(String)\n : typeof args.objects === 'string'\n ? args.objects\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n const snapshot = snapshotRegistry({\n projectRoot,\n objects,\n detail:\n typeof args.detail === 'boolean'\n ? args.detail\n : args.detail === 'true' || Boolean(objects?.length),\n });\n return {\n ok: true,\n coverage: null,\n diagnostics: [],\n data: {\n provenance: LIVE_REGISTRY_PROVENANCE,\n registrySource: 'live',\n snapshot,\n },\n };\n}\n\nasync function parseArgs(\n request: Request,\n url: URL,\n): Promise<Record<string, unknown>> {\n const args: Record<string, unknown> = {};\n for (const [key, value] of url.searchParams) {\n if (value === 'true') args[key] = true;\n else if (value === 'false') args[key] = false;\n else if (/^-?\\d+(\\.\\d+)?$/.test(value)) args[key] = Number(value);\n else args[key] = value;\n }\n if (request.method === 'POST') {\n const type = request.headers.get('content-type') ?? '';\n if (type.includes('application/json')) {\n const body = (await request.json().catch(() => null)) as unknown;\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n Object.assign(args, body as Record<string, unknown>);\n }\n }\n }\n return args;\n}\n\nexport function createDevPlane(options: DevPlaneOptions): DevPlane {\n if (!options.token) {\n throw new Error('createDevPlane requires a non-empty bearer token');\n }\n const projectRoot = options.projectRoot;\n const dbArgs =\n options.db?.url && options.db.url !== ':memory:'\n ? {\n dbUrl: options.db.url,\n ...(options.db.type ? { dbType: options.db.type } : {}),\n }\n : {};\n\n // Every call is pinned to the app's root and database; client-supplied\n // projectPath/dbUrl/dbType are dropped so a caller cannot re-point it.\n const pin = (args: Record<string, unknown>): Record<string, unknown> => {\n const { projectPath: _p, dbUrl: _u, dbType: _t, ...rest } = args;\n return { ...rest, projectPath: projectRoot, ...dbArgs };\n };\n\n const handlers: Record<(typeof DEV_PLANE_TOOL_NAMES)[number], ToolHandler> = {\n 'registry-live': (args) => registryLive(args, projectRoot),\n 'runtime-registry': (args) => runtimeRegistry(pin(args) as never),\n 'runtime-object': (args) => runtimeObject(pin(args) as never),\n 'runtime-schema-diff': (args) => runtimeSchemaDiff(pin(args) as never),\n 'migration-status': (args) => runtimeMigrationStatus(pin(args) as never),\n 'job-health': (args) => runtimeJobHealth(pin(args) as never),\n 'schedule-health': (args) => runtimeScheduleHealth(pin(args) as never),\n 'dispatch-health': (args) => runtimeDispatchHealth(pin(args) as never),\n 'recent-changes': (args) => runtimeRecentChanges(pin(args) as never),\n 'registry-drift': (args) => runtimeRegistryDrift(pin(args) as never),\n };\n\n const catalogTools = (): Tool[] => {\n const known = new Map(TOOLS.map((tool) => [tool.name, tool]));\n return DEV_PLANE_TOOL_NAMES.map(\n (name) =>\n known.get(name) ?? {\n name,\n description:\n \"Sanitized snapshot of the application's live ObjectRegistry (no manifest boot; live provenance; read-only)\",\n inputSchema: {\n type: 'object',\n properties: {\n objects: { type: 'array', items: { type: 'string' } },\n detail: { type: 'boolean' },\n },\n },\n },\n ) as Tool[];\n };\n\n const mcp = createMcpHandler(\n () => {\n const server = new Server(\n { name: `${SERVER_NAME}-dev-plane`, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler('tools/list', async () => ({\n tools: catalogTools(),\n }));\n server.setRequestHandler('tools/call', async (request) => {\n const name = request.params\n .name as (typeof DEV_PLANE_TOOL_NAMES)[number];\n const handler = handlers[name];\n if (!handler)\n throw new Error(`Unknown dev-plane tool: ${String(name)}`);\n const result = await handler(\n (request.params.arguments ?? {}) as Record<string, unknown>,\n );\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n structuredContent: result as Record<string, unknown>,\n } satisfies CallToolResult;\n });\n return server;\n },\n { legacy: 'reject', maxSubscriptions: 0 },\n );\n\n return {\n tools: DEV_PLANE_TOOL_NAMES,\n async handleRequest(request, basePath) {\n if (!hostIsLoopback(request)) {\n return json(403, {\n error: 'dev-plane requests must arrive on loopback',\n });\n }\n if (\n !bearerMatches(\n request.headers.get('authorization') ?? undefined,\n options.token,\n )\n ) {\n return json(\n 401,\n { error: 'missing or invalid bearer token' },\n {\n 'www-authenticate': 'Bearer realm=\"smrt dev-plane\"',\n },\n );\n }\n const url = new URL(request.url);\n const base = basePath.replace(/\\/+$/, '');\n if (!url.pathname.startsWith(base))\n return json(404, { error: 'not found' });\n const rest = url.pathname.slice(base.length).replace(/^\\/+/, '');\n if (rest === '') {\n return json(200, {\n provenance: LIVE_REGISTRY_PROVENANCE,\n tools: catalogTools().map((t) => ({\n name: t.name,\n description: t.description,\n })),\n mcp: `${base}/mcp`,\n });\n }\n if (rest === 'mcp') {\n return mcp.fetch(request);\n }\n const handler = handlers[rest as (typeof DEV_PLANE_TOOL_NAMES)[number]];\n if (!handler || (request.method !== 'GET' && request.method !== 'POST')) {\n return json(404, { error: `unknown dev-plane tool: ${rest}` });\n }\n try {\n return json(200, await handler(await parseArgs(request, url)));\n } catch (error) {\n return json(200, {\n ok: false,\n coverage: null,\n diagnostics: [\n {\n severity: 'warning',\n code: 'dev_plane_tool_error',\n // Same redaction the runtime tools apply to driver errors.\n message: safeErrorMessage(error),\n },\n ],\n data: { provenance: RUNTIME_PROVENANCE },\n });\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev-plane.js","names":[],"sources":["../src/dev-plane.ts"],"sourcesContent":["/**\n * In-app runtime dev-plane (#2782, Level 3 of #2307).\n *\n * The standalone `--http` host (Level 2) boots manifests in its own process.\n * This module runs *inside* the application's dev server, where the real\n * decorated `ObjectRegistry` is already live and the app's own database\n * configuration is at hand. It serves the same read-only runtime tools as\n * Level 2 plus `registry-live`, over two surfaces on one mount:\n *\n * - JSON: `GET <base>` (catalog), `GET|POST <base>/<tool>` (envelope);\n * arguments come from the query string or a JSON body.\n * - MCP: `POST <base>/mcp`, stateless Streamable HTTP (#2147).\n *\n * Security boundary: the caller (a generated SvelteKit route) only mounts\n * this in dev mode; every request must present `Authorization: Bearer` with\n * the configured token and arrive on a loopback `Host`. No principal exists,\n * so scope is fail-closed global-only. The catalog is positive: generated\n * CRUD, custom actions, `do()`, and tool-backed `is()` are never mounted.\n */\n\nimport {\n type CallToolResult,\n createMcpHandler,\n Server,\n type Tool,\n} from '@modelcontextprotocol/server';\nimport { bearerMatches } from './http.js';\nimport { SERVER_NAME, SERVER_VERSION } from './server-info.js';\nimport { TOOLS } from './tool-catalog.js';\nimport { safeErrorMessage } from './tools/runtime/connection.js';\nimport {\n runtimeObject,\n runtimeRegistry,\n runtimeSchemaDiff,\n} from './tools/runtime/observation.js';\nimport {\n loadProjectCore,\n ProjectRuntimeResolutionError,\n} from './tools/runtime/project-runtime.js';\nimport {\n RUNTIME_PROVENANCE,\n type RuntimeToolEnvelope,\n runtimeDispatchHealth,\n runtimeJobHealth,\n runtimeMigrationStatus,\n runtimeRecentChanges,\n runtimeRegistryDrift,\n runtimeScheduleHealth,\n} from './tools/runtime/tools.js';\n\n/** Provenance label for the application's own live registry. */\nexport const LIVE_REGISTRY_PROVENANCE = 'live (app registry)';\n\nexport interface DevPlaneDatabase {\n url?: string;\n type?: string;\n}\n\nexport interface DevPlaneOptions {\n /** Bearer token every request must present. */\n token: string;\n /** Project root; used only to relativize paths in snapshots. */\n projectRoot?: string;\n /**\n * The app's database configuration (typically `getSmrtConfig(name).db`\n * from the generated `$lib/server/smrt`). Passed to the runtime tools as\n * `dbUrl`/`dbType`, so the SDK's connection cache hands back the same\n * handle the app uses. `:memory:` or absent means static-only answers.\n */\n db?: DevPlaneDatabase;\n}\n\nexport interface DevPlane {\n /** Tool names served, in catalog order. */\n tools: readonly string[];\n /** Serve one request mounted at `basePath` (e.g. `/api/_dev`). */\n handleRequest(request: Request, basePath: string): Promise<Response>;\n}\n\ntype ToolHandler = (args: Record<string, unknown>) => Promise<unknown>;\n\n/** Positive catalog: the nine Level 2 tools plus the in-process-only one. */\nexport const DEV_PLANE_TOOL_NAMES = [\n 'registry-live',\n 'runtime-registry',\n 'runtime-object',\n 'runtime-schema-diff',\n 'migration-status',\n 'job-health',\n 'schedule-health',\n 'dispatch-health',\n 'recent-changes',\n 'registry-drift',\n] as const;\n\nconst LOOPBACK = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);\n\nfunction hostIsLoopback(request: Request): boolean {\n const host = request.headers.get('host') ?? '';\n const hostname = host.replace(/:\\d+$/, '').toLowerCase();\n if (!LOOPBACK.has(hostname)) return false;\n const origin = request.headers.get('origin');\n if (!origin) return true;\n try {\n return LOOPBACK.has(new URL(origin).hostname.toLowerCase());\n } catch {\n return false;\n }\n}\n\nfunction json(\n status: number,\n body: unknown,\n headers: Record<string, string> = {},\n): Response {\n return new Response(JSON.stringify(body, null, 2), {\n status,\n headers: { 'content-type': 'application/json', ...headers },\n });\n}\n\n/**\n * `registry-live`: snapshot the registry as the app has it right now. No\n * manifest boot; the app's own imports (via `$lib/server/smrt`) registered\n * these classes, and Vite's SSR HMR re-registers them on change.\n */\nasync function registryLive(\n args: Record<string, unknown>,\n projectRoot: string | undefined,\n): Promise<RuntimeToolEnvelope> {\n const objects = Array.isArray(args.objects)\n ? args.objects.map(String)\n : typeof args.objects === 'string'\n ? args.objects\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n const { snapshotRegistry } = await loadProjectCore(projectRoot);\n if (typeof snapshotRegistry !== 'function')\n throw new ProjectRuntimeResolutionError('@happyvertical/smrt-core');\n const snapshot = snapshotRegistry({\n projectRoot,\n objects,\n detail:\n typeof args.detail === 'boolean'\n ? args.detail\n : args.detail === 'true' || Boolean(objects?.length),\n });\n return {\n ok: true,\n coverage: null,\n diagnostics: [],\n data: {\n provenance: LIVE_REGISTRY_PROVENANCE,\n registrySource: 'live',\n snapshot,\n },\n };\n}\n\nasync function parseArgs(\n request: Request,\n url: URL,\n): Promise<Record<string, unknown>> {\n const args: Record<string, unknown> = {};\n for (const [key, value] of url.searchParams) {\n if (value === 'true') args[key] = true;\n else if (value === 'false') args[key] = false;\n else if (/^-?\\d+(\\.\\d+)?$/.test(value)) args[key] = Number(value);\n else args[key] = value;\n }\n if (request.method === 'POST') {\n const type = request.headers.get('content-type') ?? '';\n if (type.includes('application/json')) {\n const body = (await request.json().catch(() => null)) as unknown;\n if (body && typeof body === 'object' && !Array.isArray(body)) {\n Object.assign(args, body as Record<string, unknown>);\n }\n }\n }\n return args;\n}\n\nexport function createDevPlane(options: DevPlaneOptions): DevPlane {\n if (!options.token) {\n throw new Error('createDevPlane requires a non-empty bearer token');\n }\n const projectRoot = options.projectRoot;\n const dbArgs =\n options.db?.url && options.db.url !== ':memory:'\n ? {\n dbUrl: options.db.url,\n ...(options.db.type ? { dbType: options.db.type } : {}),\n }\n : {};\n\n // Every call is pinned to the app's root and database; client-supplied\n // projectPath/dbUrl/dbType are dropped so a caller cannot re-point it.\n const pin = (args: Record<string, unknown>): Record<string, unknown> => {\n const { projectPath: _p, dbUrl: _u, dbType: _t, ...rest } = args;\n return { ...rest, projectPath: projectRoot, ...dbArgs };\n };\n\n const handlers: Record<(typeof DEV_PLANE_TOOL_NAMES)[number], ToolHandler> = {\n 'registry-live': (args) => registryLive(args, projectRoot),\n 'runtime-registry': (args) => runtimeRegistry(pin(args) as never),\n 'runtime-object': (args) => runtimeObject(pin(args) as never),\n 'runtime-schema-diff': (args) => runtimeSchemaDiff(pin(args) as never),\n 'migration-status': (args) => runtimeMigrationStatus(pin(args) as never),\n 'job-health': (args) => runtimeJobHealth(pin(args) as never),\n 'schedule-health': (args) => runtimeScheduleHealth(pin(args) as never),\n 'dispatch-health': (args) => runtimeDispatchHealth(pin(args) as never),\n 'recent-changes': (args) => runtimeRecentChanges(pin(args) as never),\n 'registry-drift': (args) => runtimeRegistryDrift(pin(args) as never),\n };\n\n const catalogTools = (): Tool[] => {\n const known = new Map(TOOLS.map((tool) => [tool.name, tool]));\n return DEV_PLANE_TOOL_NAMES.map(\n (name) =>\n known.get(name) ?? {\n name,\n description:\n \"Sanitized snapshot of the application's live ObjectRegistry (no manifest boot; live provenance; read-only)\",\n inputSchema: {\n type: 'object',\n properties: {\n objects: { type: 'array', items: { type: 'string' } },\n detail: { type: 'boolean' },\n },\n },\n },\n ) as Tool[];\n };\n\n const mcp = createMcpHandler(\n () => {\n const server = new Server(\n { name: `${SERVER_NAME}-dev-plane`, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler('tools/list', async () => ({\n tools: catalogTools(),\n }));\n server.setRequestHandler('tools/call', async (request) => {\n const name = request.params\n .name as (typeof DEV_PLANE_TOOL_NAMES)[number];\n const handler = handlers[name];\n if (!handler)\n throw new Error(`Unknown dev-plane tool: ${String(name)}`);\n const result = await handler(\n (request.params.arguments ?? {}) as Record<string, unknown>,\n );\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n structuredContent: result as Record<string, unknown>,\n } satisfies CallToolResult;\n });\n return server;\n },\n { legacy: 'reject', maxSubscriptions: 0 },\n );\n\n return {\n tools: DEV_PLANE_TOOL_NAMES,\n async handleRequest(request, basePath) {\n if (!hostIsLoopback(request)) {\n return json(403, {\n error: 'dev-plane requests must arrive on loopback',\n });\n }\n if (\n !bearerMatches(\n request.headers.get('authorization') ?? undefined,\n options.token,\n )\n ) {\n return json(\n 401,\n { error: 'missing or invalid bearer token' },\n {\n 'www-authenticate': 'Bearer realm=\"smrt dev-plane\"',\n },\n );\n }\n const url = new URL(request.url);\n const base = basePath.replace(/\\/+$/, '');\n if (!url.pathname.startsWith(base))\n return json(404, { error: 'not found' });\n const rest = url.pathname.slice(base.length).replace(/^\\/+/, '');\n if (rest === '') {\n return json(200, {\n provenance: LIVE_REGISTRY_PROVENANCE,\n tools: catalogTools().map((t) => ({\n name: t.name,\n description: t.description,\n })),\n mcp: `${base}/mcp`,\n });\n }\n if (rest === 'mcp') {\n return mcp.fetch(request);\n }\n const handler = handlers[rest as (typeof DEV_PLANE_TOOL_NAMES)[number]];\n if (!handler || (request.method !== 'GET' && request.method !== 'POST')) {\n return json(404, { error: `unknown dev-plane tool: ${rest}` });\n }\n try {\n return json(200, await handler(await parseArgs(request, url)));\n } catch (error) {\n return json(200, {\n ok: false,\n coverage: null,\n diagnostics: [\n {\n severity: 'warning',\n code: 'dev_plane_tool_error',\n // Same redaction the runtime tools apply to driver errors.\n message: safeErrorMessage(error),\n },\n ],\n data: { provenance: RUNTIME_PROVENANCE },\n });\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
- import { n as TOOLS } from "./tool-catalog-Zl-bcimC.js";
2
- import { c as runtimeMigrationStatus, d as runtimeScheduleHealth, l as runtimeRecentChanges, m as bootRuntime, n as runtimeRegistry, o as runtimeDispatchHealth, r as runtimeSchemaDiff, s as runtimeJobHealth, t as runtimeObject, u as runtimeRegistryDrift } from "./observation-VJgHaPps.js";
1
+ import { a as runtimeMigrationStatus, c as runtimeScheduleHealth, i as runtimeJobHealth, o as runtimeRecentChanges, r as runtimeDispatchHealth, s as runtimeRegistryDrift } from "./tools-ma2JFPVY.js";
2
+ import { n as TOOLS } from "./tool-catalog-RHq7nyCm.js";
3
+ import { a as bootRuntime, i as runtimeSchemaDiff, n as runtimeObject, r as runtimeRegistry } from "./observation-CqE0tBgS.js";
3
4
  import { readFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
4
6
  import { fileURLToPath } from "node:url";
5
7
  import { Server, createMcpHandler } from "@modelcontextprotocol/server";
6
- import { dirname, join } from "node:path";
7
8
  import { randomBytes, timingSafeEqual } from "node:crypto";
8
9
  import { createServer } from "node:http";
9
10
  import { localhostHostValidation, localhostOriginValidation, toNodeHandler } from "@modelcontextprotocol/node";
@@ -165,4 +166,4 @@ async function startRuntimeHttpHost(options = {}) {
165
166
  //#endregion
166
167
  export { SERVER_VERSION as i, startRuntimeHttpHost as n, SERVER_NAME as r, bearerMatches as t };
167
168
 
168
- //# sourceMappingURL=http-TeoaK9Xr.js.map
169
+ //# sourceMappingURL=http-C302xXnO.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-TeoaK9Xr.js","names":[],"sources":["../src/server-info.ts","../src/http.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport const SERVER_NAME = 'smrt-dev-mcp';\n\nfunction readPackageVersion(): string {\n try {\n const packageRoot = dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = join(packageRoot, '..', 'package.json');\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {\n version?: unknown;\n };\n return typeof packageJson.version === 'string'\n ? packageJson.version\n : '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nexport const SERVER_VERSION = readPackageVersion();\n","/**\n * Standalone Level 2 runtime dev-plane host (#1831).\n *\n * Serves a *positive* read-only tool catalog over the stateless 2026-07-28\n * Streamable HTTP transport (#2147): `createMcpHandler` with `legacy: 'reject'`\n * and `maxSubscriptions: 0`, adapted to Node with `toNodeHandler`. No SSE,\n * no session header, no sticky routing.\n *\n * Security boundary (development only):\n * - binds loopback only; SDK localhost Host/Origin validation runs first;\n * - every request needs `Authorization: Bearer <token>` (constant-time\n * compare) — from `SMRT_DEV_MCP_TOKEN` or minted per process;\n * - no authenticated principal exists here, so scope stays fail-closed\n * global-only exactly as in Level 1;\n * - the catalog never includes generated CRUD, custom actions, `do()`, or\n * tool-backed `is()`.\n */\n\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport {\n createServer as createHttpServer,\n type Server as HttpServer,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport {\n localhostHostValidation,\n localhostOriginValidation,\n toNodeHandler,\n} from '@modelcontextprotocol/node';\nimport type { CallToolResult } from '@modelcontextprotocol/server';\nimport { createMcpHandler, Server } from '@modelcontextprotocol/server';\nimport { SERVER_NAME, SERVER_VERSION } from './server-info.js';\nimport { TOOLS } from './tool-catalog.js';\nimport { bootRuntime, type RuntimeBoot } from './tools/runtime/boot.js';\nimport {\n runtimeObject,\n runtimeRegistry,\n runtimeSchemaDiff,\n} from './tools/runtime/observation.js';\nimport {\n runtimeDispatchHealth,\n runtimeJobHealth,\n runtimeMigrationStatus,\n runtimeRecentChanges,\n runtimeRegistryDrift,\n runtimeScheduleHealth,\n} from './tools/runtime/tools.js';\n\n/**\n * The complete Level 2 catalog. A tool is exposed over HTTP only if it is\n * named here; the static stdio catalog is deliberately not mounted.\n */\nexport const RUNTIME_HTTP_TOOL_NAMES = [\n 'runtime-registry',\n 'runtime-object',\n 'runtime-schema-diff',\n 'migration-status',\n 'job-health',\n 'schedule-health',\n 'dispatch-health',\n 'recent-changes',\n 'registry-drift',\n] as const;\n\nexport type RuntimeHttpToolName = (typeof RUNTIME_HTTP_TOOL_NAMES)[number];\n\nconst RUNTIME_HTTP_HANDLERS: Record<\n RuntimeHttpToolName,\n (args: Record<string, unknown>) => Promise<unknown>\n> = {\n 'runtime-registry': (args) => runtimeRegistry(args as never),\n 'runtime-object': (args) => runtimeObject(args as never),\n 'runtime-schema-diff': (args) => runtimeSchemaDiff(args as never),\n 'migration-status': (args) => runtimeMigrationStatus(args as never),\n 'job-health': (args) => runtimeJobHealth(args as never),\n 'schedule-health': (args) => runtimeScheduleHealth(args as never),\n 'dispatch-health': (args) => runtimeDispatchHealth(args as never),\n 'recent-changes': (args) => runtimeRecentChanges(args as never),\n 'registry-drift': (args) => runtimeRegistryDrift(args as never),\n};\n\n/** Catalog definitions for the HTTP plane, in catalog order. */\nexport function runtimeHttpTools() {\n const names = new Set<string>(RUNTIME_HTTP_TOOL_NAMES);\n return TOOLS.filter((tool) => names.has(tool.name));\n}\n\n/**\n * Build a fresh protocol server per request (stateless transport contract).\n * `projectRoot` is fixed at host start; per-request `projectPath` arguments\n * are ignored so a client cannot re-point the booted process.\n */\nexport function createRuntimeProtocolServer(projectRoot: string): Server {\n const server = new Server(\n { name: `${SERVER_NAME}-runtime`, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler('tools/list', async () => ({\n tools: runtimeHttpTools(),\n }));\n server.setRequestHandler('tools/call', async (request) => {\n const name = request.params.name as string;\n const handler = (RUNTIME_HTTP_HANDLERS as Record<string, unknown>)[name];\n if (typeof handler !== 'function') {\n throw new Error(`Unknown runtime tool: ${name}`);\n }\n const { projectPath: _ignored, ...args } = (request.params.arguments ??\n {}) as Record<string, unknown>;\n const result = await (\n handler as (a: Record<string, unknown>) => Promise<unknown>\n )({ ...args, projectPath: projectRoot });\n const text = JSON.stringify(result, null, 2);\n return {\n content: [{ type: 'text', text }],\n structuredContent: result as Record<string, unknown>,\n } satisfies CallToolResult;\n });\n return server;\n}\n\nexport interface RuntimeHttpHostOptions {\n projectRoot?: string;\n /** Loopback interface only; anything else is refused at startup. */\n host?: '127.0.0.1' | 'localhost' | '::1';\n /** `0` (default) picks a free port. */\n port?: number;\n /** Bearer token; defaults to `SMRT_DEV_MCP_TOKEN` or a per-process random token. */\n token?: string;\n /** Path the MCP endpoint is mounted on (default `/mcp`). */\n path?: string;\n}\n\nexport interface RuntimeHttpHost {\n url: string;\n token: string;\n boot: RuntimeBoot;\n close(): Promise<void>;\n}\n\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);\n\nexport function bearerMatches(\n header: string | undefined,\n token: string,\n): boolean {\n if (!header?.startsWith('Bearer ')) return false;\n const presented = Buffer.from(header.slice('Bearer '.length));\n const expected = Buffer.from(token);\n return (\n presented.length === expected.length && timingSafeEqual(presented, expected)\n );\n}\n\nfunction deny(res: ServerResponse, status: number, message: string): void {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json');\n if (status === 401) {\n res.setHeader('www-authenticate', 'Bearer realm=\"smrt-dev-mcp runtime\"');\n }\n res.end(JSON.stringify({ error: message }));\n}\n\n/** Start the Level 2 host. Boots the confined runtime before listening. */\nexport async function startRuntimeHttpHost(\n options: RuntimeHttpHostOptions = {},\n): Promise<RuntimeHttpHost> {\n const host = options.host ?? '127.0.0.1';\n if (!LOOPBACK_HOSTS.has(host)) {\n throw new Error(\n 'The runtime dev-plane binds loopback only (127.0.0.1, localhost, ::1).',\n );\n }\n const path = options.path ?? '/mcp';\n const token =\n options.token ??\n process.env.SMRT_DEV_MCP_TOKEN?.trim() ??\n randomBytes(24).toString('base64url');\n if (!token) {\n throw new Error('SMRT_DEV_MCP_TOKEN must not be empty.');\n }\n const projectRoot = options.projectRoot ?? process.cwd();\n const boot = await bootRuntime({ projectRoot });\n\n const mcp = toNodeHandler(\n createMcpHandler(() => createRuntimeProtocolServer(projectRoot), {\n legacy: 'reject',\n maxSubscriptions: 0,\n }),\n );\n const hostGuard = localhostHostValidation();\n const originGuard = localhostOriginValidation();\n\n const httpServer: HttpServer = createHttpServer(\n (req: IncomingMessage, res: ServerResponse) => {\n // Guards answer and end the response themselves when they return false.\n if (!hostGuard(req, res)) return;\n if (!originGuard(req, res)) return;\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== path) {\n deny(res, 404, 'not found');\n return;\n }\n if (!bearerMatches(req.headers.authorization, token)) {\n deny(res, 401, 'missing or invalid bearer token');\n return;\n }\n void mcp(req, res);\n },\n );\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(options.port ?? 0, host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n const address = httpServer.address() as AddressInfo;\n const urlHost =\n address.family === 'IPv6' ? `[${address.address}]` : address.address;\n return {\n url: `http://${urlHost}:${address.port}${path}`,\n token,\n boot,\n close: () =>\n new Promise<void>((resolve, reject) => {\n httpServer.closeAllConnections?.();\n httpServer.close((error) => (error ? reject(error) : resolve()));\n }),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http-C302xXnO.js","names":[],"sources":["../src/server-info.ts","../src/http.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport const SERVER_NAME = 'smrt-dev-mcp';\n\nfunction readPackageVersion(): string {\n try {\n const packageRoot = dirname(fileURLToPath(import.meta.url));\n const packageJsonPath = join(packageRoot, '..', 'package.json');\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {\n version?: unknown;\n };\n return typeof packageJson.version === 'string'\n ? packageJson.version\n : '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\nexport const SERVER_VERSION = readPackageVersion();\n","/**\n * Standalone Level 2 runtime dev-plane host (#1831).\n *\n * Serves a *positive* read-only tool catalog over the stateless 2026-07-28\n * Streamable HTTP transport (#2147): `createMcpHandler` with `legacy: 'reject'`\n * and `maxSubscriptions: 0`, adapted to Node with `toNodeHandler`. No SSE,\n * no session header, no sticky routing.\n *\n * Security boundary (development only):\n * - binds loopback only; SDK localhost Host/Origin validation runs first;\n * - every request needs `Authorization: Bearer <token>` (constant-time\n * compare) — from `SMRT_DEV_MCP_TOKEN` or minted per process;\n * - no authenticated principal exists here, so scope stays fail-closed\n * global-only exactly as in Level 1;\n * - the catalog never includes generated CRUD, custom actions, `do()`, or\n * tool-backed `is()`.\n */\n\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport {\n createServer as createHttpServer,\n type Server as HttpServer,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport {\n localhostHostValidation,\n localhostOriginValidation,\n toNodeHandler,\n} from '@modelcontextprotocol/node';\nimport type { CallToolResult } from '@modelcontextprotocol/server';\nimport { createMcpHandler, Server } from '@modelcontextprotocol/server';\nimport { SERVER_NAME, SERVER_VERSION } from './server-info.js';\nimport { TOOLS } from './tool-catalog.js';\nimport { bootRuntime, type RuntimeBoot } from './tools/runtime/boot.js';\nimport {\n runtimeObject,\n runtimeRegistry,\n runtimeSchemaDiff,\n} from './tools/runtime/observation.js';\nimport {\n runtimeDispatchHealth,\n runtimeJobHealth,\n runtimeMigrationStatus,\n runtimeRecentChanges,\n runtimeRegistryDrift,\n runtimeScheduleHealth,\n} from './tools/runtime/tools.js';\n\n/**\n * The complete Level 2 catalog. A tool is exposed over HTTP only if it is\n * named here; the static stdio catalog is deliberately not mounted.\n */\nexport const RUNTIME_HTTP_TOOL_NAMES = [\n 'runtime-registry',\n 'runtime-object',\n 'runtime-schema-diff',\n 'migration-status',\n 'job-health',\n 'schedule-health',\n 'dispatch-health',\n 'recent-changes',\n 'registry-drift',\n] as const;\n\nexport type RuntimeHttpToolName = (typeof RUNTIME_HTTP_TOOL_NAMES)[number];\n\nconst RUNTIME_HTTP_HANDLERS: Record<\n RuntimeHttpToolName,\n (args: Record<string, unknown>) => Promise<unknown>\n> = {\n 'runtime-registry': (args) => runtimeRegistry(args as never),\n 'runtime-object': (args) => runtimeObject(args as never),\n 'runtime-schema-diff': (args) => runtimeSchemaDiff(args as never),\n 'migration-status': (args) => runtimeMigrationStatus(args as never),\n 'job-health': (args) => runtimeJobHealth(args as never),\n 'schedule-health': (args) => runtimeScheduleHealth(args as never),\n 'dispatch-health': (args) => runtimeDispatchHealth(args as never),\n 'recent-changes': (args) => runtimeRecentChanges(args as never),\n 'registry-drift': (args) => runtimeRegistryDrift(args as never),\n};\n\n/** Catalog definitions for the HTTP plane, in catalog order. */\nexport function runtimeHttpTools() {\n const names = new Set<string>(RUNTIME_HTTP_TOOL_NAMES);\n return TOOLS.filter((tool) => names.has(tool.name));\n}\n\n/**\n * Build a fresh protocol server per request (stateless transport contract).\n * `projectRoot` is fixed at host start; per-request `projectPath` arguments\n * are ignored so a client cannot re-point the booted process.\n */\nexport function createRuntimeProtocolServer(projectRoot: string): Server {\n const server = new Server(\n { name: `${SERVER_NAME}-runtime`, version: SERVER_VERSION },\n { capabilities: { tools: {} } },\n );\n server.setRequestHandler('tools/list', async () => ({\n tools: runtimeHttpTools(),\n }));\n server.setRequestHandler('tools/call', async (request) => {\n const name = request.params.name as string;\n const handler = (RUNTIME_HTTP_HANDLERS as Record<string, unknown>)[name];\n if (typeof handler !== 'function') {\n throw new Error(`Unknown runtime tool: ${name}`);\n }\n const { projectPath: _ignored, ...args } = (request.params.arguments ??\n {}) as Record<string, unknown>;\n const result = await (\n handler as (a: Record<string, unknown>) => Promise<unknown>\n )({ ...args, projectPath: projectRoot });\n const text = JSON.stringify(result, null, 2);\n return {\n content: [{ type: 'text', text }],\n structuredContent: result as Record<string, unknown>,\n } satisfies CallToolResult;\n });\n return server;\n}\n\nexport interface RuntimeHttpHostOptions {\n projectRoot?: string;\n /** Loopback interface only; anything else is refused at startup. */\n host?: '127.0.0.1' | 'localhost' | '::1';\n /** `0` (default) picks a free port. */\n port?: number;\n /** Bearer token; defaults to `SMRT_DEV_MCP_TOKEN` or a per-process random token. */\n token?: string;\n /** Path the MCP endpoint is mounted on (default `/mcp`). */\n path?: string;\n}\n\nexport interface RuntimeHttpHost {\n url: string;\n token: string;\n boot: RuntimeBoot;\n close(): Promise<void>;\n}\n\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);\n\nexport function bearerMatches(\n header: string | undefined,\n token: string,\n): boolean {\n if (!header?.startsWith('Bearer ')) return false;\n const presented = Buffer.from(header.slice('Bearer '.length));\n const expected = Buffer.from(token);\n return (\n presented.length === expected.length && timingSafeEqual(presented, expected)\n );\n}\n\nfunction deny(res: ServerResponse, status: number, message: string): void {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json');\n if (status === 401) {\n res.setHeader('www-authenticate', 'Bearer realm=\"smrt-dev-mcp runtime\"');\n }\n res.end(JSON.stringify({ error: message }));\n}\n\n/** Start the Level 2 host. Boots the confined runtime before listening. */\nexport async function startRuntimeHttpHost(\n options: RuntimeHttpHostOptions = {},\n): Promise<RuntimeHttpHost> {\n const host = options.host ?? '127.0.0.1';\n if (!LOOPBACK_HOSTS.has(host)) {\n throw new Error(\n 'The runtime dev-plane binds loopback only (127.0.0.1, localhost, ::1).',\n );\n }\n const path = options.path ?? '/mcp';\n const token =\n options.token ??\n process.env.SMRT_DEV_MCP_TOKEN?.trim() ??\n randomBytes(24).toString('base64url');\n if (!token) {\n throw new Error('SMRT_DEV_MCP_TOKEN must not be empty.');\n }\n const projectRoot = options.projectRoot ?? process.cwd();\n const boot = await bootRuntime({ projectRoot });\n\n const mcp = toNodeHandler(\n createMcpHandler(() => createRuntimeProtocolServer(projectRoot), {\n legacy: 'reject',\n maxSubscriptions: 0,\n }),\n );\n const hostGuard = localhostHostValidation();\n const originGuard = localhostOriginValidation();\n\n const httpServer: HttpServer = createHttpServer(\n (req: IncomingMessage, res: ServerResponse) => {\n // Guards answer and end the response themselves when they return false.\n if (!hostGuard(req, res)) return;\n if (!originGuard(req, res)) return;\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== path) {\n deny(res, 404, 'not found');\n return;\n }\n if (!bearerMatches(req.headers.authorization, token)) {\n deny(res, 401, 'missing or invalid bearer token');\n return;\n }\n void mcp(req, res);\n },\n );\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(options.port ?? 0, host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n const address = httpServer.address() as AddressInfo;\n const urlHost =\n address.family === 'IPv6' ? `[${address.address}]` : address.address;\n return {\n url: `http://${urlHost}:${address.port}${path}`,\n token,\n boot,\n close: () =>\n new Promise<void>((resolve, reject) => {\n httpServer.closeAllConnections?.();\n httpServer.close((error) => (error ? reject(error) : resolve()));\n }),\n };\n}\n"],"mappings":"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"}
package/dist/http.d.ts CHANGED
@@ -1,5 +1,22 @@
1
+ /**
2
+ * Standalone Level 2 runtime dev-plane host (#1831).
3
+ *
4
+ * Serves a *positive* read-only tool catalog over the stateless 2026-07-28
5
+ * Streamable HTTP transport (#2147): `createMcpHandler` with `legacy: 'reject'`
6
+ * and `maxSubscriptions: 0`, adapted to Node with `toNodeHandler`. No SSE,
7
+ * no session header, no sticky routing.
8
+ *
9
+ * Security boundary (development only):
10
+ * - binds loopback only; SDK localhost Host/Origin validation runs first;
11
+ * - every request needs `Authorization: Bearer <token>` (constant-time
12
+ * compare) — from `SMRT_DEV_MCP_TOKEN` or minted per process;
13
+ * - no authenticated principal exists here, so scope stays fail-closed
14
+ * global-only exactly as in Level 1;
15
+ * - the catalog never includes generated CRUD, custom actions, `do()`, or
16
+ * tool-backed `is()`.
17
+ */
1
18
  import { Server } from '@modelcontextprotocol/server';
2
- import { RuntimeBoot } from './tools/runtime/boot.js';
19
+ import { type RuntimeBoot } from './tools/runtime/boot.js';
3
20
  /**
4
21
  * The complete Level 2 catalog. A tool is exposed over HTTP only if it is
5
22
  * named here; the static stdio catalog is deliberately not mounted.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAGL,MAAM,EACP,MAAM,8BAA8B,CAAC;AAoCtC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AA8B1C,wBAAgB,YAAY,IAAI,MAAM,CA4qBrC;AAyND;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG;IACzD,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAsBA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,MAAM,EACP,MAAM,8BAA8B,CAAC;AAoBtC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AA8B1C,wBAAgB,YAAY,IAAI,MAAM,CA0nBrC;AAyND;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG;IACzD,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAsBA"}