@happyvertical/smrt-dev-mcp 0.51.27 → 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.
- package/AGENTS.md +40 -3
- package/README.md +35 -3
- package/dist/dev-plane.d.ts.map +1 -1
- package/dist/dev-plane.js +6 -4
- package/dist/dev-plane.js.map +1 -1
- package/dist/{http-TeoaK9Xr.js → http-C302xXnO.js} +5 -4
- package/dist/{http-TeoaK9Xr.js.map → http-C302xXnO.js.map} +1 -1
- package/dist/http.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -454
- package/dist/index.js.map +1 -1
- package/dist/introspect-project-Peu2LC2T.js +490 -0
- package/dist/introspect-project-Peu2LC2T.js.map +1 -0
- package/dist/knowledge/index.d.ts +3 -1
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/mcp-docs.d.ts +1 -1
- package/dist/{knowledge-DYe5lCfq.js → knowledge-CvNlDTLz.js} +9 -6
- package/dist/knowledge-CvNlDTLz.js.map +1 -0
- package/dist/knowledge.js +2 -2
- package/dist/observation-CqE0tBgS.js +386 -0
- package/dist/observation-CqE0tBgS.js.map +1 -0
- package/dist/runtime-test-fixture.d.ts +3 -0
- package/dist/runtime-test-fixture.d.ts.map +1 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -1
- package/dist/runtime.js.map +1 -1
- package/dist/{tool-catalog-Zl-bcimC.js → tool-catalog-RHq7nyCm.js} +26 -2
- package/dist/tool-catalog-RHq7nyCm.js.map +1 -0
- package/dist/tool-catalog.d.ts +1 -1
- package/dist/tool-catalog.d.ts.map +1 -1
- package/dist/tools/introspect-project.d.ts.map +1 -1
- package/dist/tools/runtime/boot.d.ts.map +1 -1
- package/dist/tools/runtime/connection.d.ts +23 -3
- package/dist/tools/runtime/connection.d.ts.map +1 -1
- package/dist/tools/runtime/observation.d.ts +17 -12
- package/dist/tools/runtime/observation.d.ts.map +1 -1
- package/dist/tools/runtime/project-runtime.d.ts +16 -0
- package/dist/tools/runtime/project-runtime.d.ts.map +1 -0
- package/dist/tools/runtime/tools.d.ts +23 -3
- package/dist/tools/runtime/tools.d.ts.map +1 -1
- package/dist/tools-ma2JFPVY.js +568 -0
- package/dist/tools-ma2JFPVY.js.map +1 -0
- package/package.json +11 -9
- package/dist/knowledge-DYe5lCfq.js.map +0 -1
- package/dist/observation-VJgHaPps.js +0 -802
- package/dist/observation-VJgHaPps.js.map +0 -1
- 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-
|
|
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
|
-
- **
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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;
|
|
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
|
|
package/dist/dev-plane.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-plane.d.ts","sourceRoot":"","sources":["../src/dev-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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,
|
package/dist/dev-plane.js.map
CHANGED
|
@@ -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 {
|
|
2
|
-
import {
|
|
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-
|
|
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.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
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"}
|