From d85cd906a2699b45c14c09bbd3c8ce9e3b606d49 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 14 Sep 2026 10:43:25 +0000 Subject: [PATCH 1/6] feat!: move the MCP implementation into the optional @devframes/agentic peer The MCP implementation, the connect gateway, and the MCP SDK behind them move out of devframe into the new @devframes/agentic package, an optional peer of devframe and @devframes/hub. devframe stays slim; installing the peer is what turns the agent surface on. Users never import agentic: devframe/adapters/mcp stays the user-facing API and loads the peer lazily. - @devframes/agentic ships /mcp and /connect entries consumed by devframe's loaders; the bare root throws. Signatures are typed against devframe's own contract, so no SDK type leaks and the SDK stays swappable. - The mcp enable matrix: 'auto' mounts iff the agent surface is non-empty AND the peer resolves (missing peer: one DF0078 warning per process); an explicit setting - or importing devframe/adapters/mcp - throws DF0079 without the peer; false stays silent. - devframe/adapters/mcp keeps its exports unchanged, built in its own graph so its top-level await cannot reshape the server chunking. - The peer probe routes through resolveServicePackage (an opaque-parameter createRequire), not a literal createRequire(import.meta.url).resolve, which turbopack rewrites into a throwing stub inside a bundled Next hub. - Pure agent projections (to-json-schema, stringify) stay in devframe under src/agent/, shared by browser WebMCP and agentic via devframe/internal. - devframe drops @modelcontextprotocol/server (dep) and /client (optional peer); DF0046 now points at @devframes/agentic. The reference hubs install the peer, as any consumer wanting MCP now does. --- alias.ts | 5 +- docs/content/1.guide/15.agent-native.md | 2 +- docs/content/2.adapters/7.mcp.md | 14 +- docs/content/6.errors/DF0046.md | 14 +- docs/content/6.errors/DF0078.md | 28 +++ docs/content/6.errors/DF0079.md | 26 +++ docs/content/6.errors/index.md | 4 +- docs/content/7.migrations/1.migration-0.9.md | 15 ++ examples/custom-hub-next/package.json | 1 + examples/custom-hub-vite/package.json | 1 + knip.jsonc | 21 ++- packages/agentic/package.json | 55 ++++++ .../src/connect/__tests__}/connect.test.ts | 9 +- .../src/connect/index.ts} | 79 ++------- packages/agentic/src/index.ts | 14 ++ .../src}/mcp/__tests__/mcp-http.test.ts | 6 +- .../src}/mcp/__tests__/mcp-locality.test.ts | 4 +- .../src}/mcp/__tests__/mcp-server.test.ts | 2 +- .../src}/mcp/build-server.ts | 35 +--- .../src/adapters => agentic/src}/mcp/fetch.ts | 55 +----- .../src/adapters => agentic/src}/mcp/http.ts | 9 +- packages/agentic/src/mcp/index.ts | 23 +++ packages/agentic/tsconfig.json | 9 + packages/agentic/tsdown.config.ts | 14 ++ packages/agentic/vitest.config.ts | 10 ++ packages/devframe/package.json | 7 +- packages/devframe/src/adapters/_shared.ts | 28 +-- packages/devframe/src/adapters/cac.ts | 5 +- packages/devframe/src/adapters/initiate.ts | 30 ++-- packages/devframe/src/adapters/mcp.ts | 25 +++ packages/devframe/src/adapters/mcp/index.ts | 29 ---- .../mcp => agent}/__tests__/stringify.test.ts | 0 .../__tests__/to-json-schema.test.ts | 0 .../src/{adapters/mcp => agent}/stringify.ts | 0 .../{adapters/mcp => agent}/to-json-schema.ts | 0 packages/devframe/src/cli/main.ts | 40 ++++- packages/devframe/src/client/webmcp.ts | 7 +- packages/devframe/src/internal/index.ts | 15 +- packages/devframe/src/node/agentic.test.ts | 27 +++ packages/devframe/src/node/agentic.ts | 69 ++++++++ packages/devframe/src/node/diagnostics.ts | 13 +- .../src/node/import-runtime-module.ts | 12 +- .../devframe/src/node/services-install.ts | 23 +++ packages/devframe/src/types/devframe.ts | 15 +- packages/devframe/src/types/index.ts | 1 + packages/devframe/src/types/mcp.ts | 91 ++++++++++ packages/devframe/tsdown.config.ts | 18 +- packages/hub/package.json | 7 + packages/hub/src/node/initiate.ts | 17 +- packages/next/src/host.ts | 9 +- pnpm-lock.yaml | 159 ++++++++++++------ pnpm-workspace.yaml | 1 + .../@devframes/agentic/connect.snapshot.d.ts | 20 +++ .../@devframes/agentic/connect.snapshot.js | 8 + .../@devframes/agentic/index.snapshot.d.ts | 4 + .../@devframes/agentic/index.snapshot.js | 4 + .../@devframes/agentic/mcp.snapshot.d.ts | 18 ++ .../tsnapi/@devframes/agentic/mcp.snapshot.js | 8 + .../devframe/adapters/mcp.snapshot.d.ts | 47 ++---- .../tsnapi/devframe/adapters/mcp.snapshot.js | 8 +- .../tsnapi/devframe/index.snapshot.d.ts | 26 +++ .../tsnapi/devframe/internal.snapshot.d.ts | 21 ++- .../tsnapi/devframe/internal.snapshot.js | 9 + .../tsnapi/devframe/types.snapshot.d.ts | 5 + tests/optional-mcp-bundles.test.ts | 7 +- tsconfig.base.json | 11 +- turbo.json | 16 ++ vitest.config.ts | 1 + 68 files changed, 943 insertions(+), 373 deletions(-) create mode 100644 docs/content/6.errors/DF0078.md create mode 100644 docs/content/6.errors/DF0079.md create mode 100644 packages/agentic/package.json rename packages/{devframe/src/cli => agentic/src/connect/__tests__}/connect.test.ts (94%) rename packages/{devframe/src/cli/connect.ts => agentic/src/connect/index.ts} (79%) create mode 100644 packages/agentic/src/index.ts rename packages/{devframe/src/adapters => agentic/src}/mcp/__tests__/mcp-http.test.ts (97%) rename packages/{devframe/src/adapters => agentic/src}/mcp/__tests__/mcp-locality.test.ts (96%) rename packages/{devframe/src/adapters => agentic/src}/mcp/__tests__/mcp-server.test.ts (99%) rename packages/{devframe/src/adapters => agentic/src}/mcp/build-server.ts (91%) rename packages/{devframe/src/adapters => agentic/src}/mcp/fetch.ts (72%) rename packages/{devframe/src/adapters => agentic/src}/mcp/http.ts (88%) create mode 100644 packages/agentic/src/mcp/index.ts create mode 100644 packages/agentic/tsconfig.json create mode 100644 packages/agentic/tsdown.config.ts create mode 100644 packages/agentic/vitest.config.ts create mode 100644 packages/devframe/src/adapters/mcp.ts delete mode 100644 packages/devframe/src/adapters/mcp/index.ts rename packages/devframe/src/{adapters/mcp => agent}/__tests__/stringify.test.ts (100%) rename packages/devframe/src/{adapters/mcp => agent}/__tests__/to-json-schema.test.ts (100%) rename packages/devframe/src/{adapters/mcp => agent}/stringify.ts (100%) rename packages/devframe/src/{adapters/mcp => agent}/to-json-schema.ts (100%) create mode 100644 packages/devframe/src/node/agentic.test.ts create mode 100644 packages/devframe/src/node/agentic.ts create mode 100644 packages/devframe/src/types/mcp.ts create mode 100644 tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts create mode 100644 tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js create mode 100644 tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts create mode 100644 tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js create mode 100644 tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts create mode 100644 tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js diff --git a/alias.ts b/alias.ts index 6e2d67173..90c945255 100644 --- a/alias.ts +++ b/alias.ts @@ -47,7 +47,10 @@ export const alias = { 'devframe/adapters/build': r('devframe/src/adapters/build.ts'), 'devframe/adapters/embedded': r('devframe/src/adapters/embedded.ts'), 'devframe/initiate': r('devframe/src/adapters/initiate.ts'), - 'devframe/adapters/mcp': r('devframe/src/adapters/mcp/index.ts'), + 'devframe/adapters/mcp': r('devframe/src/adapters/mcp.ts'), + '@devframes/agentic/mcp': r('agentic/src/mcp/index.ts'), + '@devframes/agentic/connect': r('agentic/src/connect/index.ts'), + '@devframes/agentic': r('agentic/src/index.ts'), '@devframes/hub/build': r('hub/src/node/build.ts'), '@devframes/hub/client': r('hub/src/client/index.ts'), '@devframes/hub/constants': r('hub/src/constants.ts'), diff --git a/docs/content/1.guide/15.agent-native.md b/docs/content/1.guide/15.agent-native.md index ab89d47a3..4ed4357d5 100644 --- a/docs/content/1.guide/15.agent-native.md +++ b/docs/content/1.guide/15.agent-native.md @@ -100,7 +100,7 @@ Every `ctx.rpc.sharedState` key is exposed as a `devframe://state/` resourc ## Starting the MCP server -The dev server serves the agent surface over HTTP on its own: the `mcp: 'auto'` default mounts the route at `/__mcp` once anything above exists (an `agent`-flagged RPC, a registered tool or resource) - one flagged function is the whole setup. See the [MCP adapter](/adapters/mcp#route-based-server) for forcing it on or off and hardening the route. +The dev server serves the agent surface over HTTP on its own: the `mcp: 'auto'` default mounts the route at `/__mcp` once anything above exists (an `agent`-flagged RPC, a registered tool or resource) and the optional [`@devframes/agentic`](/adapters/mcp) peer is installed - one flagged function plus one install is the whole setup. See the [MCP adapter](/adapters/mcp#route-based-server) for forcing it on or off and hardening the route. For a stdio server instead, via the CLI: diff --git a/docs/content/2.adapters/7.mcp.md b/docs/content/2.adapters/7.mcp.md index a39cda8e2..1fc7ec806 100644 --- a/docs/content/2.adapters/7.mcp.md +++ b/docs/content/2.adapters/7.mcp.md @@ -7,6 +7,12 @@ description: 'Exposes a devframe''s agent-facing API as a Model Context Protocol Exposes a devframe's agent-facing API as a [Model Context Protocol](https://modelcontextprotocol.io) server: coding agents call flagged RPCs and read resources. +The implementation (and the MCP SDK behind it) lives in **`@devframes/agentic`**, an optional peer of `devframe`: install it to enable the agent surface, and keep importing everything from `devframe/adapters/mcp` - the peer is loaded for you, never imported directly. A devframe without an agent surface ships with neither the peer nor the SDK installed: + +```sh +npm install @devframes/agentic +``` + ```ts import { createMcpServer } from 'devframe/adapters/mcp' import myDevframe from './my-tool' @@ -14,11 +20,11 @@ import myDevframe from './my-tool' await createMcpServer(myDevframe, { transport: 'stdio' }) ``` -`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection. +`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection. Importing `devframe/adapters/mcp` without the peer installed throws [DF0079](/errors/DF0079). ## Route-based server -The dev server exposes the same MCP API over HTTP, live. The default setting is **`'auto'`**: the route mounts once the devframe exposes an agent surface (an `agent`-flagged RPC, a registered tool or resource) - flag your first function and the agent view is on. A devframe with nothing flagged mounts no route and loads no MCP code. +The dev server exposes the same MCP API over HTTP, live. The default setting is **`'auto'`**: the route mounts once the devframe exposes an agent surface (an `agent`-flagged RPC, a registered tool or resource) *and* `@devframes/agentic` is installed - flag your first function, install the peer, and the agent view is on. A devframe with nothing flagged mounts no route and loads no MCP code; an agent surface without the peer warns once ([DF0078](/errors/DF0078)) and mounts nothing, while an explicit `mcp` setting without the peer throws ([DF0079](/errors/DF0079)). `mcp: false` stays silent either way. Pin the behavior where you host the tool - it's a hosting decision, so pass `mcp` to `createCac` when you assemble the CLI (or to `createDevServer` / `initDevframe` / `initHub` when you host it programmatically): `true` always mounts, `false` never mounts, an object customises the route: @@ -103,6 +109,8 @@ Two gateway tools (`devframe:connect:*` ids; see [tool ids and wire names](/guid Discovery reads the **instance registry**: every `createDevServer` writes `~/.devframe/instances/-.json`, dialed with a loopback origin. In-process host frameworks register via `registerDevframeInstance` (`devframe/node`). `--port ` probes a port; `DEVFRAME_INSTANCES_DIR` relocates the registry, `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` opts out. -Most instances trust same-machine callers, so the connector reaches them with no credential. For an instance you *hardened* with a bearer, the connector reads `DEVFRAME_MCP_AUTH_TOKEN` and presents it (never a CLI flag, since command-line arguments are visible to other processes). Connect to a fleet with distinct credentials by driving `startConnectServer` with a per-instance `authToken` resolver. +The connector needs the same optional `@devframes/agentic` peer as the adapter; `devframe connect` without it throws [DF0046](/errors/DF0046). + +Most instances trust same-machine callers, so the connector reaches them with no credential. For an instance you *hardened* with a bearer, the connector reads `DEVFRAME_MCP_AUTH_TOKEN` and presents it (never a CLI flag, since command-line arguments are visible to other processes). See [Agent-Native](/guide/agent-native) for the API and safety model. diff --git a/docs/content/6.errors/DF0046.md b/docs/content/6.errors/DF0046.md index 6cfc61519..296da9158 100644 --- a/docs/content/6.errors/DF0046.md +++ b/docs/content/6.errors/DF0046.md @@ -1,25 +1,25 @@ --- -title: 'DF0046: Connector Requires the MCP SDK' -description: 'devframe connect requires the optional peer dependency @modelcontextprotocol/client: {reason}' +title: 'DF0046: Connector Requires @devframes/agentic' +description: 'devframe connect requires the optional peer dependency @devframes/agentic: {reason}' --- ## Message -> `devframe connect` requires the optional peer dependency @modelcontextprotocol/client: `{reason}` +> `devframe connect` requires the optional peer dependency @devframes/agentic: `{reason}` ## Cause -`devframe connect` was started but `@modelcontextprotocol/client` could not be imported. The client SDK is an optional peer dependency of `devframe`: only the connector dials other instances, so only it needs the package installed. +`devframe connect` was started but `@devframes/agentic/connect` could not be imported. The connector lives in `@devframes/agentic` (together with the MCP SDK), an optional peer dependency of `devframe`: only agent-facing features need the package installed. ## Fix -Install the SDK next to devframe and run the connector again: +Install the package next to devframe and run the connector again: ```sh -npm install @modelcontextprotocol/client +npm install @devframes/agentic devframe connect ``` ## Source -- [`packages/devframe/src/cli/connect.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/connect.ts): `startConnectServer()` throws this when the dynamic SDK import fails. +- [`packages/devframe/src/cli/main.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/main.ts): the `connect` subcommand throws this when the dynamic `@devframes/agentic/connect` import fails. diff --git a/docs/content/6.errors/DF0078.md b/docs/content/6.errors/DF0078.md new file mode 100644 index 000000000..7ed1352ca --- /dev/null +++ b/docs/content/6.errors/DF0078.md @@ -0,0 +1,28 @@ +--- +title: 'DF0078: Agent Surface Without @devframes/agentic' +description: 'This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer "@devframes/agentic" is not installed.' +--- + +## Message + +> This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer "@devframes/agentic" is not installed. + +## Cause + +The devframe (or hub) left a non-empty agent surface (RPC functions with an `agent` field, registered agent tools, resources, or providers) and the `mcp` setting is the omitted `'auto'` default, which would mount the MCP route. But `@devframes/agentic`, the optional peer carrying the MCP adapter and the MCP SDK, is not installed, so no route can be served. + +The warning is reported once per process; the instance keeps running without an MCP endpoint. + +## Fix + +Install the peer so the agent surface is served over MCP: + +```sh +npm install @devframes/agentic +``` + +Or, if the tools should deliberately stay unexposed, set `mcp: false` to opt out silently. + +## Source + +- [`packages/devframe/src/adapters/_shared.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/adapters/_shared.ts): `loadAutoMcpAdapter()` reports this (once) when the agent surface is non-empty but the peer probe fails. diff --git a/docs/content/6.errors/DF0079.md b/docs/content/6.errors/DF0079.md new file mode 100644 index 000000000..e737cf98e --- /dev/null +++ b/docs/content/6.errors/DF0079.md @@ -0,0 +1,26 @@ +--- +title: 'DF0079: MCP Enabled Without @devframes/agentic' +description: 'The mcp option is enabled, but the optional peer "@devframes/agentic" could not be loaded: {reason}' +--- + +## Message + +> The `mcp` option is enabled, but the optional peer "@devframes/agentic" could not be loaded: `{reason}` + +## Cause + +An explicit `mcp` setting (`true`, a route options object, the `--mcp` flag, or the `mcp` CLI subcommand) asked for an MCP surface - or `devframe/adapters/mcp` was imported directly - but the implementation could not be loaded from the optional `@devframes/agentic` peer, typically because it is not installed. Unlike the omitted `'auto'` default (which degrades to a one-time [DF0078](/errors/DF0078) warning), an explicit opt-in fails fast rather than silently running without MCP. + +## Fix + +Install the peer next to devframe: + +```sh +npm install @devframes/agentic +``` + +Or remove the explicit `mcp` setting (or pass `mcp: false`) if the endpoint isn't wanted. The underlying import error is attached as `cause`. + +## Source + +- [`packages/devframe/src/node/agentic.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/agentic.ts): `importAgenticMcp()` maps a failed load of `@devframes/agentic/mcp` to this error. diff --git a/docs/content/6.errors/index.md b/docs/content/6.errors/index.md index 3bd55a16f..b70aabb01 100644 --- a/docs/content/6.errors/index.md +++ b/docs/content/6.errors/index.md @@ -54,7 +54,7 @@ Emitted by `devframe`: the framework-neutral host, RPC, streaming, assets, servi | [DF0043](/errors/DF0043) | error | Invalid RPC Argument | | [DF0044](/errors/DF0044) | error | Invalid RPC Return Value | | [DF0045](/errors/DF0045) | warn | Instance Registry Update Failed | -| [DF0046](/errors/DF0046) | error | Connector Requires the MCP SDK | +| [DF0046](/errors/DF0046) | error | Connector Requires @devframes/agentic | | [DF0047](/errors/DF0047) | warn | Agent Tool Wire-Name Collision | | [DF0048](/errors/DF0048) | error | Unknown Shared-State Key | | [DF0049](/errors/DF0049) | error | Connector Call Requires Port and Tool | @@ -84,6 +84,8 @@ Emitted by `devframe`: the framework-neutral host, RPC, streaming, assets, servi | [DF0075](/errors/DF0075) | warn | No RPC Transport On This Runtime | | [DF0076](/errors/DF0076) | error | WebSocket Upgrade Unsupported On This Runtime | | [DF0077](/errors/DF0077) | error | In-Page Channel Function Not Registered | +| [DF0078](/errors/DF0078) | warn | Agent Surface Without @devframes/agentic | +| [DF0079](/errors/DF0079) | error | MCP Enabled Without @devframes/agentic | ## Hub: context & lifecycle (DF80xx) diff --git a/docs/content/7.migrations/1.migration-0.9.md b/docs/content/7.migrations/1.migration-0.9.md index ad5d2f0dd..9d923a448 100644 --- a/docs/content/7.migrations/1.migration-0.9.md +++ b/docs/content/7.migrations/1.migration-0.9.md @@ -5,6 +5,21 @@ description: '0.9 removes the compatibility shims deprecated across the 0.7 seri 0.9 removes the compatibility shims deprecated across the 0.7 series and trims the public API of `devframe` and `@devframes/hub`. Each change has a drop-in replacement. It also moves the [MCP](/adapters/mcp) surface to the stateless [MCP 2026-07-28 protocol](https://modelcontextprotocol.io/specification/2026-07-28). The devframe API is unchanged; see [The MCP endpoints are stateless](#the-mcp-endpoints-are-stateless). +Late in the 0.9 series the MCP implementation moved into the new optional peer `@devframes/agentic`; see [MCP requires `@devframes/agentic`](#mcp-requires-devframesagentic). + +## MCP requires `@devframes/agentic` + +The MCP implementation and the MCP SDK moved out of `devframe` into `@devframes/agentic`, an **optional peer**: install it to serve agent tools over MCP, skip it for a slimmer install without an MCP surface. Your imports do not change - `devframe/adapters/mcp` stays the user-facing API and loads the peer for you; `@devframes/agentic` is never imported directly. + +> [!WARNING] +> This is a behavior change within the 0.9 series: `devframe` no longer depends on the MCP SDK, so MCP now requires `@devframes/agentic` to be installed. Without it, the omitted `'auto'` default warns once ([DF0078](/errors/DF0078)) and mounts nothing; an explicit `mcp` setting - and importing `devframe/adapters/mcp` itself - throws ([DF0079](/errors/DF0079)); `mcp: false` stays silent. + +```sh +npm install @devframes/agentic +``` + +The `devframe/adapters/mcp` exports are unchanged: `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`, and their option types (the types are also importable from `devframe/types`). `devframe connect` needs the peer too and throws [DF0046](/errors/DF0046) without it, replacing the former `@modelcontextprotocol/client` optional peer. + ## `devframe/adapters/cli` is removed The `devframe/adapters/cli` entry is gone; import from `devframe/adapters/cac`: diff --git a/examples/custom-hub-next/package.json b/examples/custom-hub-next/package.json index be4b3a8b3..53a78af41 100644 --- a/examples/custom-hub-next/package.json +++ b/examples/custom-hub-next/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@antfu/design": "catalog:frontend", + "@devframes/agentic": "workspace:*", "@devframes/hub": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", diff --git a/examples/custom-hub-vite/package.json b/examples/custom-hub-vite/package.json index eb21efdd0..004439771 100644 --- a/examples/custom-hub-vite/package.json +++ b/examples/custom-hub-vite/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@antfu/design": "catalog:frontend", + "@devframes/agentic": "workspace:*", "@devframes/hub": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", diff --git a/knip.jsonc b/knip.jsonc index b664838e7..27db3ad69 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -71,7 +71,11 @@ // `import()` carrying `webpackIgnore`/`turbopackIgnore` comments - // deliberately invisible to bundlers (and knip) so Next never inlines // their node-only code. They're genuinely used at runtime. + // `@devframes/agentic` is install-only: devframe probes and loads it + // through `importRuntimeModule` string specifiers to serve the hub's + // aggregate MCP endpoint; it is never statically imported. "ignoreDependencies": [ + "@devframes/agentic", "@devframes/plugin-a11y", "@devframes/plugin-code-server", "@devframes/plugin-git", @@ -103,7 +107,10 @@ // dock's `action.importFrom` bare specifier, resolved by Vite through // the hub's `clientModuleResolution` template (`/@id/{specifier}`), // never a static import knip's graph can see. - "ignoreDependencies": ["demo-dock-client"] + // `@devframes/agentic` is install-only: devframe probes and loads it + // through `importRuntimeModule` string specifiers to serve the hub's + // aggregate MCP endpoint; it is never statically imported. + "ignoreDependencies": ["@devframes/agentic", "demo-dock-client"] }, // Multi-entry example package (see `packages/devframe` above for why // exports-mapped entries are listed explicitly): `src/index.ts` is the @@ -126,6 +133,12 @@ "entry": ["src/server.ts"] }, "packages/devframe": { + // `@devframes/agentic` (devframe's own optional peer) is loaded only + // through `importRuntimeModule('@devframes/agentic/...')` string + // specifiers and a `require.resolve` probe (`src/node/agentic.ts`), + // never a static import knip's graph can see; the devDependency is + // what lets tests exercise the installed-peer path. + "ignoreDependencies": ["@devframes/agentic"], // Every `package.json#exports` subpath maps to one of these source // files (see `tsdown.config.ts`'s `serverEntries`/`clientEntries`). // Knip's package.json→dist→src source mapping needs `outDir` set on @@ -136,8 +149,7 @@ // listed explicitly instead. Keep this in sync with the `exports` map. "entry": [ "src/{index,constants}.ts", - "src/adapters/{build,cac,dev,embedded,initiate}.ts", - "src/adapters/mcp/index.ts", + "src/adapters/{build,cac,dev,embedded,initiate,mcp}.ts", "src/client/index.ts", "src/in-page-channel/index.ts", "src/internal/index.ts", @@ -151,6 +163,9 @@ "src/utils/*.ts" ] }, + "packages/agentic": { + "entry": ["src/{mcp,connect}/index.ts"] + }, "packages/hub": { "entry": ["src/{index,constants}.ts", "src/{client,node,types}/index.ts", "src/node/{index,initiate,build}.ts"] }, diff --git a/packages/agentic/package.json b/packages/agentic/package.json new file mode 100644 index 000000000..b7334ec5e --- /dev/null +++ b/packages/agentic/package.json @@ -0,0 +1,55 @@ +{ + "name": "@devframes/agentic", + "type": "module", + "version": "0.9.19", + "description": "Agent-surface implementation for devframe: installing it next to devframe enables the MCP adapter (devframe/adapters/mcp) and the devframe connect gateway.", + "author": "Anthony Fu ", + "license": "MIT", + "homepage": "https://github.com/devframes/devframe#readme", + "repository": { + "directory": "packages/agentic", + "type": "git", + "url": "git+https://github.com/devframes/devframe.git" + }, + "bugs": "https://github.com/devframes/devframe/issues", + "keywords": [ + "devtools", + "devframe", + "mcp", + "agent" + ], + "sideEffects": false, + "exports": { + ".": "./dist/index.mjs", + "./mcp": "./dist/mcp/index.mjs", + "./connect": "./dist/connect/index.mjs", + "./package.json": "./package.json" + }, + "types": "./dist/index.d.mts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsdown", + "watch": "tsdown --watch", + "typecheck": "tsc --noEmit", + "prepack": "turbo run build --filter=@devframes/agentic" + }, + "peerDependencies": { + "devframe": "workspace:*" + }, + "dependencies": { + "@modelcontextprotocol/client": "catalog:deps", + "@modelcontextprotocol/server": "catalog:deps", + "@standard-schema/spec": "catalog:deps", + "h3": "catalog:deps", + "pathe": "catalog:deps", + "ufo": "catalog:deps" + }, + "devDependencies": { + "@types/node": "catalog:types", + "devframe": "workspace:*", + "tsdown": "catalog:build", + "valibot": "catalog:deps" + } +} diff --git a/packages/devframe/src/cli/connect.test.ts b/packages/agentic/src/connect/__tests__/connect.test.ts similarity index 94% rename from packages/devframe/src/cli/connect.test.ts rename to packages/agentic/src/connect/__tests__/connect.test.ts index 9644247d0..6eadea7ca 100644 --- a/packages/devframe/src/cli/connect.test.ts +++ b/packages/agentic/src/connect/__tests__/connect.test.ts @@ -1,10 +1,9 @@ -import type { DevframeInstanceRecord } from '../node/instance-registry' -import type { StartedServer } from '../node/instance-shell' -import type { DevframeDefinition } from '../types/devframe' +import type { DevframeInstanceRecord, StartedServer } from 'devframe/internal' +import type { DevframeDefinition } from 'devframe/types' import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client' +import { createDevServer } from 'devframe/adapters/dev' import { afterEach, describe, expect, it } from 'vitest' -import { createDevServer } from '../adapters/dev' -import { buildInstanceRequestHeaders, resolveAuthToken } from './connect' +import { buildInstanceRequestHeaders, resolveAuthToken } from '../index' const TOKEN = 'a-high-entropy-connect-test-token' diff --git a/packages/devframe/src/cli/connect.ts b/packages/agentic/src/connect/index.ts similarity index 79% rename from packages/devframe/src/cli/connect.ts rename to packages/agentic/src/connect/index.ts index f79370299..c27bf0026 100644 --- a/packages/devframe/src/cli/connect.ts +++ b/packages/agentic/src/connect/index.ts @@ -1,11 +1,12 @@ import type { Tool } from '@modelcontextprotocol/server' -import type { DevframeInstanceRecord } from '../node/instance-registry' -import process from 'node:process' +import type { DevframeInstanceRecord } from 'devframe/internal' +import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client' +import { Server } from '@modelcontextprotocol/server' +import { StdioServerTransport } from '@modelcontextprotocol/server/stdio' +import { diagnostics, listLiveDevframeInstances, probeDevframeOrigin } from 'devframe/internal' import { toAgentToolName } from 'devframe/utils/agent-tool-name' import { Diagnostic } from 'devframe/utils/nostics' import { joinURL } from 'ufo' -import { diagnostics } from '../node/diagnostics' -import { listLiveDevframeInstances, probeDevframeOrigin } from '../node/instance-registry' export interface ConnectServerOptions { /** @@ -75,14 +76,6 @@ interface IndexedInstance extends Omit { hint?: string } -/** The lazily imported MCP SDK surface `devframe connect` needs. */ -interface ConnectSdk { - Server: typeof import('@modelcontextprotocol/server').Server - StdioServerTransport: typeof import('@modelcontextprotocol/server/stdio').StdioServerTransport - Client: typeof import('@modelcontextprotocol/client').Client - StreamableHTTPClientTransport: typeof import('@modelcontextprotocol/client').StreamableHTTPClientTransport -} - // Gateway tool ids follow the `devframe::` convention; the wire // names are their sanitized forms (`devframe_connect_list-instances`, …). const INDEX_TOOL = toAgentToolName('devframe:connect:list-instances') @@ -127,9 +120,7 @@ const GATEWAY_TOOLS: Tool[] = [ * Streamable-HTTP endpoint), and holds no domain knowledge of its own. */ export async function startConnectServer(options: ConnectServerOptions = {}): Promise { - const sdk = await importSdk() - - const server = new sdk.Server( + const server = new Server( { name: 'devframe-connect', version: '0.0.0' }, { capabilities: { tools: {} } }, ) @@ -140,9 +131,9 @@ export async function startConnectServer(options: ConnectServerOptions = {}): Pr const { name, arguments: args } = request.params try { if (name === INDEX_TOOL) - return textResult(await index(sdk, options)) + return textResult(await index(options)) if (name === CALL_TOOL) - return textResult(await call(sdk, options, args ?? {})) + return textResult(await call(options, args ?? {})) return errorResult({ message: `unknown tool "${name}"`, fix: `Call ${INDEX_TOOL} or ${CALL_TOOL}.` }) } catch (error) { @@ -150,7 +141,7 @@ export async function startConnectServer(options: ConnectServerOptions = {}): Pr } }) - const transport = new sdk.StdioServerTransport() + const transport = new StdioServerTransport() await server.connect(transport) return { @@ -160,28 +151,8 @@ export async function startConnectServer(options: ConnectServerOptions = {}): Pr } } -async function importSdk(): Promise { - try { - const [serverMod, stdioMod, clientMod] = await Promise.all([ - import('@modelcontextprotocol/server'), - import('@modelcontextprotocol/server/stdio'), - import('@modelcontextprotocol/client'), - ]) - return { - Server: serverMod.Server, - StdioServerTransport: stdioMod.StdioServerTransport, - Client: clientMod.Client, - StreamableHTTPClientTransport: clientMod.StreamableHTTPClientTransport, - } - } - catch (error) { - const reason = error instanceof Error ? error.message : String(error) - throw diagnostics.DF0046({ reason, cause: error }) - } -} - /** Discover instances: registry (prune-on-read) + explicit port probes. */ -async function index(sdk: ConnectSdk, options: ConnectServerOptions): Promise { +async function index(options: ConnectServerOptions): Promise { const { live } = await listLiveDevframeInstances({ instancesDir: options.instancesDir, timeoutMs: options.timeoutMs, @@ -205,7 +176,7 @@ async function index(sdk: ConnectSdk, options: ConnectServerOptions): Promise { - return withInstanceClient(sdk, url, token, async (client) => { +async function listInstanceTools(url: string, token: string | undefined): Promise<{ name: string, description?: string }[]> { + return withInstanceClient(url, token, async (client) => { const listed = await client.listTools() return listed.tools.map((tool: { name: string, description?: string }) => ({ name: tool.name, @@ -254,7 +225,6 @@ async function listInstanceTools(sdk: ConnectSdk, url: string, token: string | u } async function call( - sdk: ConnectSdk, options: ConnectServerOptions, args: { port?: number, tool?: string, args?: Record }, ): Promise { @@ -272,7 +242,7 @@ async function call( throw diagnostics.DF0051({ port: args.port }) const url = `${record.origin}${record.mcp.path}` - return withInstanceClient(sdk, url, resolveAuthToken(options.authToken, record), async (client) => { + return withInstanceClient(url, resolveAuthToken(options.authToken, record), async (client) => { const result = await client.callTool({ name: args.tool!, arguments: args.args ?? {} }) return { instance: { id: record.id, port: record.port }, @@ -285,16 +255,15 @@ async function call( } async function withInstanceClient( - sdk: ConnectSdk, url: string, token: string | undefined, - fn: (client: InstanceType) => Promise, + fn: (client: Client) => Promise, ): Promise { // The instance's own (loopback) origin (so the route's origin gate, which // rejects `Origin`-less requests, accepts this native client) plus the // bearer (when configured), the `Authorization` header being the only place // the credential ever appears. - const transport = new sdk.StreamableHTTPClientTransport( + const transport = new StreamableHTTPClientTransport( new URL(url), { requestInit: { headers: buildInstanceRequestHeaders(url, token) } }, ) @@ -302,7 +271,7 @@ async function withInstanceClient( // `initialize` handshake for a 2025-only instance. Devframe's own route is // stateless 2026-07-28, but a mixed fleet (older instances, third-party // MCP servers reached by port) may still be 2025-era. - const client = new sdk.Client( + const client = new Client( { name: 'devframe-connect', version: '0.0.0' }, { versionNegotiation: { mode: 'auto' } }, ) @@ -355,17 +324,3 @@ function errorResult(error: ConnectErrorPayload): { content: [{ type: 'text', text: JSON.stringify({ error }, null, 2) }], } } - -/** Parse the repeatable `--port` flag value(s) from cac into numbers. */ -export function parsePortsFlag(value: unknown): number[] { - const values = Array.isArray(value) ? value : value === undefined ? [] : [value] - return values - .map(v => Number(v)) - .filter(n => Number.isInteger(n) && n > 0 && n < 65536) -} - -/** Keep the connector process alive until the stdio transport closes it. */ -export function keepAlive(): void { - // stdin stays open while the MCP client holds the pipe; nothing else to do. - process.stdin.resume() -} diff --git a/packages/agentic/src/index.ts b/packages/agentic/src/index.ts new file mode 100644 index 000000000..caa76c68e --- /dev/null +++ b/packages/agentic/src/index.ts @@ -0,0 +1,14 @@ +// `@devframes/agentic` is not imported by users: it carries the MCP +// implementation (and the MCP SDK) that `devframe` loads through its own +// `devframe/adapters/mcp` entry and the `devframe connect` command. Installing +// the package next to devframe is what enables those surfaces; the `/mcp` and +// `/connect` subpaths exist for devframe's loaders. +// +// Importing the bare package is always a mistake, so it throws with a pointer +// at the user-facing API instead of silently resolving to nothing. (devframe's +// probe for this optional peer resolves `package.json`, never this entry.) +throw new Error( + '[@devframes/agentic] is not imported directly; installing it enables devframe\'s agent surfaces.\n' + + ' • import from "devframe/adapters/mcp" to serve a devframe over MCP\n' + + ' • run "devframe connect" for the stdio discovery gateway\n', +) diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-http.test.ts b/packages/agentic/src/mcp/__tests__/mcp-http.test.ts similarity index 97% rename from packages/devframe/src/adapters/mcp/__tests__/mcp-http.test.ts rename to packages/agentic/src/mcp/__tests__/mcp-http.test.ts index 4a41495ce..709109b90 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/mcp-http.test.ts +++ b/packages/agentic/src/mcp/__tests__/mcp-http.test.ts @@ -1,8 +1,8 @@ -import type { StartedServer } from '../../../node/instance-shell' -import type { DevframeDefinition, McpRouteOptions } from '../../../types/devframe' +import type { StartedServer } from 'devframe/internal' +import type { DevframeDefinition, McpRouteOptions } from 'devframe/types' import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client' +import { createDevServer } from 'devframe/adapters/dev' import { afterEach, describe, expect, it } from 'vitest' -import { createDevServer } from '../../dev' const TOKEN = 'a-high-entropy-test-bearer-token' diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-locality.test.ts b/packages/agentic/src/mcp/__tests__/mcp-locality.test.ts similarity index 96% rename from packages/devframe/src/adapters/mcp/__tests__/mcp-locality.test.ts rename to packages/agentic/src/mcp/__tests__/mcp-locality.test.ts index 183e61f52..02456d659 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/mcp-locality.test.ts +++ b/packages/agentic/src/mcp/__tests__/mcp-locality.test.ts @@ -1,6 +1,4 @@ -import type { McpAuthorization } from '../../../types/devframe' -import type { DevframeHost } from '../../../types/host' -import type { McpConnectionInfo } from '../fetch' +import type { DevframeHost, McpAuthorization, McpConnectionInfo } from 'devframe/types' import { createHostContext } from 'devframe/node' import { isLoopbackAddress } from 'devframe/utils/origin' import { afterEach, describe, expect, it } from 'vitest' diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts b/packages/agentic/src/mcp/__tests__/mcp-server.test.ts similarity index 99% rename from packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts rename to packages/agentic/src/mcp/__tests__/mcp-server.test.ts index 9c95c3fac..ba1efe25d 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts +++ b/packages/agentic/src/mcp/__tests__/mcp-server.test.ts @@ -1,4 +1,4 @@ -import type { DevframeHost } from '../../../types/host' +import type { DevframeHost } from 'devframe/types' import { Client, InMemoryTransport } from '@modelcontextprotocol/client' import { createHostContext } from 'devframe/node' import { describe, expect, it } from 'vitest' diff --git a/packages/devframe/src/adapters/mcp/build-server.ts b/packages/agentic/src/mcp/build-server.ts similarity index 91% rename from packages/devframe/src/adapters/mcp/build-server.ts rename to packages/agentic/src/mcp/build-server.ts index 2ca735d80..0860f5da1 100644 --- a/packages/devframe/src/adapters/mcp/build-server.ts +++ b/packages/agentic/src/mcp/build-server.ts @@ -1,44 +1,15 @@ import type { Tool } from '@modelcontextprotocol/server' import type { StandardSchemaV1 } from '@standard-schema/spec' import type { RpcFunctionDefinitionAnyWithContext } from 'devframe/rpc' -import type { AgentTool, DevframeDefinition, DevframeHost, DevframeNodeContext } from 'devframe/types' +import type { AgentTool, CreateMcpServerOptions, DevframeDefinition, DevframeHost, DevframeNodeContext, McpServerHandle } from 'devframe/types' import { homedir } from 'node:os' import process from 'node:process' import { Server } from '@modelcontextprotocol/server' +import { DEVFRAME_EVENTS } from 'devframe/constants' +import { argsToJsonSchema, diagnostics, formatMcpError, returnToJsonSchema, stringifyForMcp } from 'devframe/internal' import { createHostContext } from 'devframe/node' import { toAgentToolName } from 'devframe/utils/agent-tool-name' import { join } from 'pathe' -import { DEVFRAME_EVENTS } from '../../events' -import { diagnostics } from '../../node/diagnostics' -import { formatMcpError, stringifyForMcp } from './stringify' -import { argsToJsonSchema, returnToJsonSchema } from './to-json-schema' - -export interface CreateMcpServerOptions { - /** - * Transport to use. `createMcpServer` itself runs `'stdio'` (a standalone - * process with its own host context); the Streamable-HTTP transport is - * served route-based by the dev server instead; see `mountMcpHttp` and - * the `mcp` option on `createDevServer` / `createCac`'s `--mcp` flag. - */ - transport?: 'stdio' - /** - * Expose shared-state keys as MCP resources. - * - `true` (default): every key the host publishes - * - `false`: none - * - `(key) => boolean`: filter - */ - exposeSharedState?: boolean | ((key: string) => boolean) - /** Override the name reported in the MCP handshake. */ - serverName?: string - /** Override the version reported in the MCP handshake. Defaults to `definition.version ?? '0.0.0'`. */ - serverVersion?: string - /** Called once the transport is connected. */ - onReady?: (info: { transport: 'stdio' }) => void -} - -export interface McpServerHandle { - stop: () => Promise -} export interface BuildMcpServerOptions { serverName: string diff --git a/packages/devframe/src/adapters/mcp/fetch.ts b/packages/agentic/src/mcp/fetch.ts similarity index 72% rename from packages/devframe/src/adapters/mcp/fetch.ts rename to packages/agentic/src/mcp/fetch.ts index 8ff63c9c8..f8378dd05 100644 --- a/packages/devframe/src/adapters/mcp/fetch.ts +++ b/packages/agentic/src/mcp/fetch.ts @@ -1,37 +1,9 @@ -import type { DevframeNodeContext, McpAuthorization } from 'devframe/types' +import type { CreateMcpFetchHandlerOptions, DevframeNodeContext, McpAuthorization, McpConnectionInfo, McpFetchHandler } from 'devframe/types' import { createMcpHandler } from '@modelcontextprotocol/server' import { timingSafeEqual } from 'devframe/utils/crypto-token' import { isAllowedOrigin, isLoopbackAddress } from 'devframe/utils/origin' import { bridgeListChanged, buildMcpServerFromContext } from './build-server' -export interface CreateMcpFetchHandlerOptions { - /** Name reported in the MCP handshake. */ - serverName: string - /** Version reported in the MCP handshake. */ - serverVersion: string - /** Expose shared-state keys as MCP resources; see `buildMcpServerFromContext`. */ - exposeSharedState: boolean | ((key: string) => boolean) - /** - * Optional identity check, layered on top of the origin gate and checked - * **after** it: a bearer token string (matched in constant time against - * `Authorization: Bearer `), a `(request) => boolean` callback, or - * `false` (the default) for origin-only, trusting same-machine callers. A - * callback governs identity only and cannot relax the origin gate. See - * {@link McpAuthorization}. - */ - authorization?: McpAuthorization - /** - * Origin allow-list beyond the loopback default. `false` disables the - * origin gate entirely. Default: loopback-only. - * - * Unlike the WS transport, the MCP route does **not** allow `Origin`-less - * requests: a route-based endpoint is reachable by any local process, so a - * request must carry an `Origin` that passes the gate. Native clients - * (e.g. `devframe connect`) send their loopback origin explicitly. - */ - allowedOrigins?: readonly string[] | false -} - /** * Parse exactly one `Authorization: Bearer ` credential, returning the * token or `undefined` for a missing, malformed, empty, or multi-credential @@ -62,31 +34,6 @@ async function isAuthorized(req: Request, authorization: McpAuthorization): Prom return timingSafeEqual(token, authorization) } -/** Connection facts a host knows about a request beyond the `Request` itself. */ -export interface McpConnectionInfo { - /** - * The connecting peer's remote address (a node socket's `remoteAddress`), - * used to prove a same-machine caller when the endpoint relies on the - * loopback origin default with no identity check. A host that can resolve a - * trustworthy peer address (the h3/node mount) supplies it; when it's - * omitted the origin gate stays the only locality signal. - */ - remoteAddress?: string -} - -export interface McpFetchHandler { - /** - * WHATWG-`fetch` handler for the MCP endpoint. Hand every method - * (POST/GET/DELETE) on the endpoint's path to it; routing by path is the - * host's job. Pass {@link McpConnectionInfo} when the host can resolve the - * peer address so the default trust boundary can enforce same-machine - * locality. - */ - fetch: (request: Request, connection?: McpConnectionInfo) => Promise - /** Tear down the handler (aborts in-flight exchanges, drops the change bridge). */ - dispose: () => Promise -} - /** * Build a framework-agnostic MCP endpoint over a devframe context: a * web-standard `Request → Response` handler any host can mount: h3 (see diff --git a/packages/devframe/src/adapters/mcp/http.ts b/packages/agentic/src/mcp/http.ts similarity index 88% rename from packages/devframe/src/adapters/mcp/http.ts rename to packages/agentic/src/mcp/http.ts index e6e150026..821a5ef2b 100644 --- a/packages/devframe/src/adapters/mcp/http.ts +++ b/packages/agentic/src/mcp/http.ts @@ -1,16 +1,9 @@ +import type { MountedMcpHttp, MountMcpHttpOptions } from 'devframe/internal' import type { DevframeNodeContext } from 'devframe/types' import type { H3, H3Event } from 'h3' -import type { CreateMcpFetchHandlerOptions } from './fetch' import { defineHandler, getRequestIP } from 'h3' import { createMcpFetchHandler } from './fetch' -export interface MountMcpHttpOptions extends CreateMcpFetchHandlerOptions {} - -export interface MountedMcpHttp { - /** Tear down the MCP handler (aborts in-flight exchanges, drops the change bridge). */ - dispose: () => Promise -} - /** * Mount a stateless MCP endpoint on an h3 app at `path`: the h3 binding over * {@link createMcpFetchHandler}, which owns the per-request serving, the diff --git a/packages/agentic/src/mcp/index.ts b/packages/agentic/src/mcp/index.ts new file mode 100644 index 000000000..04a3c898d --- /dev/null +++ b/packages/agentic/src/mcp/index.ts @@ -0,0 +1,23 @@ +// The devframe MCP adapter implementation: translates the agent-host surface +// of a DevframeDefinition into an MCP server. Users import it through +// `devframe/adapters/mcp` (which lazy-loads this entry); this subpath exists +// for devframe's loaders, not for direct consumption. +// +// The MCP SDK behind it is a regular dependency of `@devframes/agentic`, an +// optional peer of `devframe`; first-party adapters load this entry lazily +// (`importRuntimeModule`) so neither enters a consumer bundle graph. The +// exported signatures are typed against devframe's own `types/mcp.ts` +// contract - no SDK type leaks, so the SDK stays swappable. + +export { createMcpServer } from './build-server' +export { createMcpFetchHandler } from './fetch' +export { mountMcpHttp } from './http' + +export type { MountedMcpHttp, MountMcpHttpOptions } from 'devframe/internal' +export type { + CreateMcpFetchHandlerOptions, + CreateMcpServerOptions, + McpConnectionInfo, + McpFetchHandler, + McpServerHandle, +} from 'devframe/types' diff --git a/packages/agentic/tsconfig.json b/packages/agentic/tsconfig.json new file mode 100644 index 000000000..6646b7b4e --- /dev/null +++ b/packages/agentic/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["esnext", "dom"], + "types": ["node"] + }, + "include": ["src", "tsdown.config.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/agentic/tsdown.config.ts b/packages/agentic/tsdown.config.ts new file mode 100644 index 000000000..f1cdda1ad --- /dev/null +++ b/packages/agentic/tsdown.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: { + 'index': 'src/index.ts', + 'mcp/index': 'src/mcp/index.ts', + 'connect/index': 'src/connect/index.ts', + }, + outExtensions: () => ({ js: '.mjs', dts: '.d.mts' }), + clean: true, + tsconfig: '../../tsconfig.base.json', + dts: true, + platform: 'node', +}) diff --git a/packages/agentic/vitest.config.ts b/packages/agentic/vitest.config.ts new file mode 100644 index 000000000..729f6dbe9 --- /dev/null +++ b/packages/agentic/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' +import { alias } from '../../alias' + +export default defineConfig({ + resolve: { alias }, + test: { + name: 'agentic', + testTimeout: 10_000, + }, +}) diff --git a/packages/devframe/package.json b/packages/devframe/package.json index fd760c556..5d77dea61 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -82,11 +82,11 @@ "prepack": "turbo run build --filter=devframe && mkdir -p ./skills && cp -r ../../skills/devframe ./skills/devframe" }, "peerDependencies": { - "@modelcontextprotocol/client": "^2.0.0", + "@devframes/agentic": "workspace:*", "cac": "^7.0.0" }, "peerDependenciesMeta": { - "@modelcontextprotocol/client": { + "@devframes/agentic": { "optional": true }, "cac": { @@ -94,7 +94,6 @@ } }, "dependencies": { - "@modelcontextprotocol/server": "catalog:deps", "@standard-schema/spec": "catalog:deps", "birpc": "catalog:deps", "crossws": "catalog:deps", @@ -105,7 +104,7 @@ "ufo": "catalog:deps" }, "devDependencies": { - "@modelcontextprotocol/client": "catalog:deps", + "@devframes/agentic": "workspace:*", "cac": "catalog:deps", "get-port-please": "catalog:deps", "immer": "catalog:deps", diff --git a/packages/devframe/src/adapters/_shared.ts b/packages/devframe/src/adapters/_shared.ts index c68d6f8ae..7c22a84ff 100644 --- a/packages/devframe/src/adapters/_shared.ts +++ b/packages/devframe/src/adapters/_shared.ts @@ -1,10 +1,11 @@ +import type { AgenticMcpModule } from '../node/agentic' import type { DevframeAgentHost } from '../types/agent' import type { ConnectionMeta } from '../types/context' import type { DevframeDefinition, DevframeDeploymentKind, McpAuthorization, McpSetting } from '../types/devframe' import { getPort } from 'get-port-please' import { cleanDoubleSlashes, withLeadingSlash, withoutLeadingSlash, withTrailingSlash } from 'ufo' import { DEVFRAME_MCP_ROUTE } from '../constants' -import { importRuntimeModule } from '../node/import-runtime-module' +import { importAgenticMcp, isAgenticInstalled, warnAgenticMcpMissingOnce } from '../node/agentic' const DEFAULT_PORT = 9999 @@ -101,21 +102,24 @@ export function resolveMcpConfig(mcp: McpSetting | undefined): ResolvedMcpConfig /** * Resolve the `mcp: 'auto'` default at mount time: import the MCP adapter - * when the devframe's agent surface is non-empty, or return `undefined` - * (mount nothing) when the surface is empty - the zero-cost path, loading - * no MCP code at all. The adapter (and the MCP SDK behind it) loads through - * `importRuntimeModule`, so it never enters a consumer's bundle graph. - * - * Generic like `importRuntimeModule`: the caller names the module type - * (`typeof import('devframe/adapters/mcp')`) so this shared helper carries - * no type-level dependency on the MCP adapter. + * from the optional `@devframes/agentic` peer when the devframe's agent + * surface is non-empty, or return `undefined` (mount nothing) when the + * surface is empty - the zero-cost path, loading no MCP code at all. A + * non-empty surface with the peer absent also mounts nothing, reporting a + * one-time DF0078 warning instead. The adapter (and the MCP SDK behind it) + * loads through `importRuntimeModule`, so it never enters a consumer's + * bundle graph. */ -export async function loadAutoMcpAdapter( +export async function loadAutoMcpAdapter( agent: Pick, -): Promise { +): Promise { if (!agent.hasSurface()) return undefined - return await importRuntimeModule('devframe/adapters/mcp') + if (!isAgenticInstalled()) { + warnAgenticMcpMissingOnce() + return undefined + } + return await importAgenticMcp() } /** diff --git a/packages/devframe/src/adapters/cac.ts b/packages/devframe/src/adapters/cac.ts index 225f5136e..82379df8b 100644 --- a/packages/devframe/src/adapters/cac.ts +++ b/packages/devframe/src/adapters/cac.ts @@ -12,7 +12,7 @@ import type { DevframeDefinition, McpSetting } from '../types/devframe' import process from 'node:process' import cac from 'cac' import { colors as c } from 'devframe/utils/colors' -import { importRuntimeModule } from '../node/import-runtime-module' +import { importAgenticMcp } from '../node/agentic' import { createBuild } from './build' import { createDevServer, resolveDevServerPort } from './dev' import { flagKeyToOption, isBooleanFlag, parseCliFlags } from './flags' @@ -138,7 +138,8 @@ export function createCac(d: DevframeDefinition, options: CreateCacOptions = {}) // MCP clients expect JSON-RPC on stdout, so route welcome/logging // noise out of the way. Logs-SDK diagnostics land on stderr by // default, so nothing extra needed beyond not printing here. - const { createMcpServer } = await importRuntimeModule('devframe/adapters/mcp') + // Explicit invocation: a missing `@devframes/agentic` throws DF0079. + const { createMcpServer } = await importAgenticMcp() await createMcpServer(d, { transport: 'stdio', /** Deliberately go to stderr: stdout is the MCP transport. */ diff --git a/packages/devframe/src/adapters/initiate.ts b/packages/devframe/src/adapters/initiate.ts index 6b9549c71..dd00e68be 100644 --- a/packages/devframe/src/adapters/initiate.ts +++ b/packages/devframe/src/adapters/initiate.ts @@ -3,6 +3,7 @@ import type { ConnectionMeta, DevframeNodeContext, DevframeNodeRpcSession, Devfr import type { Buffer } from 'node:buffer' import type { IncomingMessage, Server as NodeHttpServer, ServerResponse } from 'node:http' import type { Duplex } from 'node:stream' +import type { AgenticMcpModule } from '../node/agentic' import type { DevframeAuthHandler } from '../node/auth/handler' import type { DevframeInstanceRecord } from '../node/instance-registry' import type { InstanceShellInternals, StartedServer } from '../node/instance-shell' @@ -17,10 +18,10 @@ import { resolve } from 'pathe' import { joinURL, withoutLeadingSlash } from 'ufo' import { resolveClientAssets } from '../client-assets' import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_MCP_ROUTE } from '../constants' +import { importAgenticMcp } from '../node/agentic' import { createHostContext } from '../node/context' import { diagnostics } from '../node/diagnostics' import { createH3DevframeHost } from '../node/host-h3' -import { importRuntimeModule } from '../node/import-runtime-module' import { createInstanceShell, resolveInstanceRegister } from '../node/instance-shell' import { loadAutoMcpAdapter, normalizeBasePath, resolveMcpConfig } from './_shared' import { resolveDevServerPort } from './dev' @@ -345,11 +346,14 @@ export function initDevframe( /** * Mount the route-based MCP server at `__mcp`, before the SPA static - * catch-all so the exact route wins. The MCP SDK is pulled in dynamically - * only when the route mounts, keeping it out of consumer bundles: `'auto'` - * mounts once `setup()` left a non-empty agent surface (an empty surface - * loads no MCP code); an explicit setting mounts unconditionally. The - * resolved config is origin-only unless it opts into a bearer/callback. + * catch-all so the exact route wins. The adapter (from the optional + * `@devframes/agentic` peer) is pulled in dynamically only when the route + * mounts, keeping it and the MCP SDK out of consumer bundles: `'auto'` + * mounts once `setup()` left a non-empty agent surface AND the peer is + * installed (an empty surface loads no MCP code; a non-empty one without the + * peer warns once and mounts nothing); an explicit setting mounts + * unconditionally, throwing DF0079 when the peer is absent. The resolved + * config is origin-only unless it opts into a bearer/callback. */ async function mountMcpRoute( app: H3, @@ -358,10 +362,10 @@ async function mountMcpRoute( base: string, setting: McpSetting, ): Promise<{ meta: ConnectionMeta['mcp'], dispose: () => Promise } | undefined> { - let module: typeof import('./mcp') | undefined + let module: AgenticMcpModule | undefined let config: ResolvedMcpConfig | undefined if (setting === 'auto') { - module = await loadAutoMcpAdapter(context.agent) + module = await loadAutoMcpAdapter(context.agent) if (module) config = { authorization: false } } @@ -372,15 +376,7 @@ async function mountMcpRoute( return undefined const route = withoutLeadingSlash(config.path ?? DEVFRAME_MCP_ROUTE) - if (!module) { - try { - module = await importRuntimeModule('devframe/adapters/mcp') - } - catch (error) { - const reason = error instanceof Error ? error.message : String(error) - throw diagnostics.DF0017({ transport: 'http', reason, cause: error }) - } - } + module ??= await importAgenticMcp() const mounted = module.mountMcpHttp(app, context, joinURL(base, route), { serverName: `${def.id} (devframe)`, serverVersion: def.version ?? '0.0.0', diff --git a/packages/devframe/src/adapters/mcp.ts b/packages/devframe/src/adapters/mcp.ts new file mode 100644 index 000000000..ff4e3ee77 --- /dev/null +++ b/packages/devframe/src/adapters/mcp.ts @@ -0,0 +1,25 @@ +// The user-facing MCP adapter entry. The implementation (and the MCP SDK) +// lives in the optional `@devframes/agentic` peer, which is never imported +// directly: this entry lazy-loads it (throwing a coded DF0079 when the peer +// is not installed) and re-exports the surface, typed against devframe's own +// contract in `types/mcp.ts`. +import { importAgenticMcp } from '../node/agentic' + +export type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic' +export type { + CreateMcpFetchHandlerOptions, + CreateMcpServerOptions, + McpConnectionInfo, + McpFetchHandler, + McpServerHandle, +} from '../types/mcp' + +// The alias must resolve its exports at module evaluation (they are consumed +// as plain named imports), so the lazy load is a deliberate top-level await; +// it builds in its own graph (see tsdown.config.ts) to keep TLA contained. +// eslint-disable-next-line antfu/no-top-level-await +const mcp = await importAgenticMcp() + +export const createMcpServer = mcp.createMcpServer +export const createMcpFetchHandler = mcp.createMcpFetchHandler +export const mountMcpHttp = mcp.mountMcpHttp diff --git a/packages/devframe/src/adapters/mcp/index.ts b/packages/devframe/src/adapters/mcp/index.ts deleted file mode 100644 index 0482cf1e8..000000000 --- a/packages/devframe/src/adapters/mcp/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Public entry for the devframe MCP adapter. Translates the agent-host -// surface of a DevframeDefinition into an MCP server. -// -// Usage: -// import { createMcpServer } from 'devframe/adapters/mcp' -// await createMcpServer(definition, { transport: 'stdio' }) -// -// The MCP SDK behind it is a regular dependency of `devframe`; first-party -// adapters still load this entry lazily (`importRuntimeModule`) so the SDK -// stays out of consumer bundle graphs. - -export { - createMcpServer, - type CreateMcpServerOptions, - type McpServerHandle, -} from './build-server' - -export { - createMcpFetchHandler, - type CreateMcpFetchHandlerOptions, - type McpConnectionInfo, - type McpFetchHandler, -} from './fetch' - -export { - type MountedMcpHttp, - mountMcpHttp, - type MountMcpHttpOptions, -} from './http' diff --git a/packages/devframe/src/adapters/mcp/__tests__/stringify.test.ts b/packages/devframe/src/agent/__tests__/stringify.test.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/__tests__/stringify.test.ts rename to packages/devframe/src/agent/__tests__/stringify.test.ts diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/agent/__tests__/to-json-schema.test.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts rename to packages/devframe/src/agent/__tests__/to-json-schema.test.ts diff --git a/packages/devframe/src/adapters/mcp/stringify.ts b/packages/devframe/src/agent/stringify.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/stringify.ts rename to packages/devframe/src/agent/stringify.ts diff --git a/packages/devframe/src/adapters/mcp/to-json-schema.ts b/packages/devframe/src/agent/to-json-schema.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/to-json-schema.ts rename to packages/devframe/src/agent/to-json-schema.ts diff --git a/packages/devframe/src/cli/main.ts b/packages/devframe/src/cli/main.ts index 95243c7a3..984758ef9 100644 --- a/packages/devframe/src/cli/main.ts +++ b/packages/devframe/src/cli/main.ts @@ -1,6 +1,39 @@ import process from 'node:process' import { cac } from 'cac' -import { keepAlive, parsePortsFlag, startConnectServer } from './connect' +import { diagnostics } from '../node/diagnostics' +import { importRuntimeModule } from '../node/import-runtime-module' + +/** The surface `devframe connect` consumes from `@devframes/agentic/connect`. */ +interface AgenticConnectModule { + startConnectServer: (options: { + ports?: number[] + instancesDir?: string + timeoutMs?: number + authToken?: string + }) => Promise<{ stop: () => Promise }> +} + +/** Parse the repeatable `--port` flag value(s) from cac into numbers. */ +function parsePortsFlag(value: unknown): number[] { + const values = Array.isArray(value) ? value : value === undefined ? [] : [value] + return values + .map(v => Number(v)) + .filter(n => Number.isInteger(n) && n > 0 && n < 65536) +} + +/** + * Load the connector from the optional `@devframes/agentic` peer, mapping a + * failed load (typically: the peer is not installed) to a thrown `DF0046`. + */ +async function importConnect(): Promise { + try { + return await importRuntimeModule('@devframes/agentic/connect') + } + catch (error) { + const reason = error instanceof Error ? error.message : String(error) + throw diagnostics.DF0046({ reason, cause: error }) + } +} /** * The `devframe` bin is the framework's own CLI, distinct from the per-app @@ -16,6 +49,7 @@ export async function runDevframeCli(argv: string[] = process.argv): Promise', 'Override the instance registry directory (default: ~/.devframe/instances, or $DEVFRAME_INSTANCES_DIR)') .option('--timeout ', 'Probe timeout per instance in milliseconds', { default: 1000 }) .action(async (options: { port?: unknown, instancesDir?: string, timeout?: number }) => { + const { startConnectServer } = await importConnect() await startConnectServer({ ports: parsePortsFlag(options.port), instancesDir: options.instancesDir, @@ -27,7 +61,9 @@ export async function runDevframeCli(argv: string[] = process.argv): Promise { + it('resolves the installed optional peer without importing it', () => { + expect(isAgenticInstalled()).toBe(true) + }) + + it('is false for an unresolvable package', () => { + expect(isAgenticInstalled('@devframes/definitely-not-installed')).toBe(false) + }) +}) + +describe('importAgenticMcp', () => { + it('maps a failed load to a thrown DF0079', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + try { + const error = await importAgenticMcp('@devframes/definitely-not-installed/mcp').catch(e => e) + expect(error).toBeInstanceOf(Diagnostic) + expect((error as Diagnostic).code).toBe('DF0079') + } + finally { + warn.mockRestore() + } + }) +}) diff --git a/packages/devframe/src/node/agentic.ts b/packages/devframe/src/node/agentic.ts new file mode 100644 index 000000000..926887ba5 --- /dev/null +++ b/packages/devframe/src/node/agentic.ts @@ -0,0 +1,69 @@ +import type { H3 } from 'h3' +import type { DevframeNodeContext } from '../types/context' +import type { DevframeDefinition } from '../types/devframe' +import type { CreateMcpFetchHandlerOptions, CreateMcpServerOptions, McpFetchHandler, McpServerHandle } from '../types/mcp' +import { diagnostics } from './diagnostics' +import { importRuntimeModule, isRuntimeModuleResolvable } from './import-runtime-module' + +/** + * The h3-bound half of the MCP adapter contract (the SDK-neutral half lives + * in `types/mcp.ts`, which must stay lib-neutral; h3's declarations are not). + */ +export interface MountMcpHttpOptions extends CreateMcpFetchHandlerOptions {} + +export interface MountedMcpHttp { + /** Tear down the MCP handler (aborts in-flight exchanges, drops the change bridge). */ + dispose: () => Promise +} + +/** The surface `@devframes/agentic/mcp` exports, as devframe's loaders consume it. */ +export interface AgenticMcpModule { + /** Build an MCP server over the agent surface of a devframe definition (stdio). */ + createMcpServer: (definition: DevframeDefinition, options?: CreateMcpServerOptions) => Promise + /** Build a framework-agnostic `Request → Response` MCP endpoint over a devframe context. */ + createMcpFetchHandler: (ctx: DevframeNodeContext, options: CreateMcpFetchHandlerOptions) => McpFetchHandler + /** Mount a stateless MCP endpoint on an h3 app at `path`. */ + mountMcpHttp: (app: H3, ctx: DevframeNodeContext, path: string, options: MountMcpHttpOptions) => MountedMcpHttp +} + +// `@devframes/agentic` is devframe's optional peer carrying the MCP adapter +// and the MCP SDK. These helpers implement the enable matrix around it: +// probe (never import) to decide, warn once under `'auto'` when the peer is +// absent, throw a coded error when an explicit `mcp` setting needs it. + +const AGENTIC_PACKAGE = '@devframes/agentic' + +/** + * Whether `@devframes/agentic` is resolvable from devframe's own location + * (where its optional peer is linked). A pure `require.resolve` probe: no + * module is loaded, so the `'auto'` miss path stays zero-cost. + */ +export function isAgenticInstalled(pkg: string = AGENTIC_PACKAGE): boolean { + return isRuntimeModuleResolvable(`${pkg}/package.json`) +} + +let warnedMissing = false + +/** Report DF0078 (agent surface without `@devframes/agentic`) once per process. */ +export function warnAgenticMcpMissingOnce(): void { + if (warnedMissing) + return + warnedMissing = true + diagnostics.DF0078() +} + +/** + * Load the MCP adapter from `@devframes/agentic/mcp`, mapping a failed load + * (typically: the optional peer is not installed) to a thrown `DF0079`. + * Loads through `importRuntimeModule`, so the adapter and the MCP SDK behind + * it never enter a consumer's bundle graph. + */ +export async function importAgenticMcp(specifier: string = `${AGENTIC_PACKAGE}/mcp`): Promise { + try { + return await importRuntimeModule(specifier) + } + catch (error) { + const reason = error instanceof Error ? error.message : String(error) + throw diagnostics.DF0079({ reason, cause: error }) + } +} diff --git a/packages/devframe/src/node/diagnostics.ts b/packages/devframe/src/node/diagnostics.ts index a075e3d47..ef2c8dea6 100644 --- a/packages/devframe/src/node/diagnostics.ts +++ b/packages/devframe/src/node/diagnostics.ts @@ -88,8 +88,8 @@ export const diagnostics = defineDiagnostics({ fix: 'Discovery tooling (`devframe connect`) will not see this instance. Check that the registry directory is writable, point `DEVFRAME_INSTANCES_DIR` at a writable directory, or set `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` to opt out of registration.', }, DF0046: { - why: (p: { reason: string }) => `\`devframe connect\` requires the optional peer dependency @modelcontextprotocol/client: ${p.reason}`, - fix: 'Install it next to devframe (e.g. `npm install @modelcontextprotocol/client`) and run `devframe connect` again.', + why: (p: { reason: string }) => `\`devframe connect\` requires the optional peer dependency @devframes/agentic: ${p.reason}`, + fix: 'Install it next to devframe (e.g. `npm install @devframes/agentic`) and run `devframe connect` again.', }, DF0047: { why: (p: { name: string, id: string, existing: string }) => @@ -211,5 +211,14 @@ export const diagnostics = defineDiagnostics({ `\`attach\` / \`handleUpgrade\` drive a raw \`node:http\` upgrade into crossws's Node adapter, which refuses to run on ${p.runtime}.`, fix: 'On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno example), or connect over the SSE endpoint instead.', }, + DF0078: { + why: 'This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer "@devframes/agentic" is not installed.', + fix: 'Install `@devframes/agentic` next to devframe to serve the MCP endpoint, or set `mcp: false` to opt out silently.', + }, + DF0079: { + why: (p: { reason: string }) => + `The \`mcp\` option is enabled, but the optional peer "@devframes/agentic" could not be loaded: ${p.reason}`, + fix: 'Install `@devframes/agentic` next to devframe (the MCP adapter and the MCP SDK live there), or remove the explicit `mcp` setting.', + }, }, }) diff --git a/packages/devframe/src/node/import-runtime-module.ts b/packages/devframe/src/node/import-runtime-module.ts index 1f056d69b..bc4ed34c6 100644 --- a/packages/devframe/src/node/import-runtime-module.ts +++ b/packages/devframe/src/node/import-runtime-module.ts @@ -1,4 +1,4 @@ -import { importServicePackage } from './services-install' +import { importServicePackage, resolveServicePackage } from './services-install' /** * Resolve and import a package at runtime without adding it to a consumer's @@ -11,3 +11,13 @@ import { importServicePackage } from './services-install' export async function importRuntimeModule(specifier: string): Promise { return await importServicePackage(specifier, [import.meta.url]) as T } + +/** + * Whether {@link importRuntimeModule} would resolve `specifier`, without + * importing anything. The zero-cost probe behind optional-peer decisions. + * + * @internal + */ +export function isRuntimeModuleResolvable(specifier: string): boolean { + return resolveServicePackage(specifier, [import.meta.url]) !== undefined +} diff --git a/packages/devframe/src/node/services-install.ts b/packages/devframe/src/node/services-install.ts index d661ddb1c..83fa5154d 100644 --- a/packages/devframe/src/node/services-install.ts +++ b/packages/devframe/src/node/services-install.ts @@ -41,6 +41,29 @@ export function expandResolveFrom(resolveFrom: string, cwd: string): string | un return undefined } +/** + * Resolve a package specifier against each `resolveFrom` candidate in order, + * returning the resolved absolute path or `undefined`. A pure probe: nothing + * is imported. The resolution base arrives as a parameter (not a literal + * `createRequire(import.meta.url)`), which keeps bundlers that special-case + * that pattern (turbopack rewrites its `.resolve()` of a dynamic specifier + * into a throwing stub) from touching the runtime resolution. + */ +export function resolveServicePackage( + pkg: string, + resolveFroms: readonly (string | null | undefined)[], +): string | undefined { + for (const from of resolveFroms) { + if (typeof from !== 'string' || from.length === 0) + continue + try { + return createRequire(toRequireBase(from)).resolve(pkg) + } + catch {} + } + return undefined +} + /** * Import a service package's module, trying each `resolveFrom` candidate in * order (so a plugin-declared service resolves against the plugin's own diff --git a/packages/devframe/src/types/devframe.ts b/packages/devframe/src/types/devframe.ts index 6e39f18d5..97323eadb 100644 --- a/packages/devframe/src/types/devframe.ts +++ b/packages/devframe/src/types/devframe.ts @@ -111,12 +111,15 @@ export type McpAuthorization * * - `'auto'`, the default: mount the route when the devframe exposes an * agent surface (an `agent`-flagged RPC function, or a tool / resource / - * provider registered on `ctx.agent`). An empty agent surface mounts - * nothing and loads no MCP code. - * - `true`: always mount at the default `__mcp` route. - * - `false`: never mount. - * - {@link McpRouteOptions}: always mount, with a custom route path, origin - * allow-list, or {@link McpAuthorization} identity check. + * provider registered on `ctx.agent`) AND the optional `@devframes/agentic` + * peer (the MCP adapter and SDK) is installed. An empty agent surface + * mounts nothing and loads no MCP code; a non-empty one without the peer + * warns once (DF0078) and mounts nothing. + * - `true`: always mount at the default `__mcp` route; a missing + * `@devframes/agentic` throws DF0079. + * - `false`: never mount, and never probe or warn. + * - {@link McpRouteOptions}: always mount (like `true`), with a custom route + * path, origin allow-list, or {@link McpAuthorization} identity check. * * A mounted route trusts same-machine callers by default (the loopback * origin gate), exactly like `mcp: true`. diff --git a/packages/devframe/src/types/index.ts b/packages/devframe/src/types/index.ts index ce4024d10..a406a920d 100644 --- a/packages/devframe/src/types/index.ts +++ b/packages/devframe/src/types/index.ts @@ -4,6 +4,7 @@ export * from './devframe' export * from './diagnostics' export * from './events' export * from './host' +export * from './mcp' export * from './remote-assets' export * from './rpc' export * from './rpc-augments' diff --git a/packages/devframe/src/types/mcp.ts b/packages/devframe/src/types/mcp.ts new file mode 100644 index 000000000..cdb9e003a --- /dev/null +++ b/packages/devframe/src/types/mcp.ts @@ -0,0 +1,91 @@ +import type { McpAuthorization } from './devframe' + +/** + * The MCP adapter surface, typed here so `devframe` (which probes for and + * lazily loads `@devframes/agentic`) and `@devframes/agentic/mcp` (which + * implements it) share one contract without a type-level dependency cycle. + * None of these shapes reference the MCP SDK: the SDK is an implementation + * detail of `@devframes/agentic`, freely swappable behind this surface. + * The h3-bound `mountMcpHttp` shapes live in `node/agentic.ts` (exported via + * `devframe/internal`) instead: `devframe/types` must stay lib-neutral, and + * h3's declarations are not. + */ +export interface CreateMcpServerOptions { + /** + * Transport to use. `createMcpServer` itself runs `'stdio'` (a standalone + * process with its own host context); the Streamable-HTTP transport is + * served route-based by the dev server instead; see `mountMcpHttp` and + * the `mcp` option on `createDevServer` / `createCac`'s `--mcp` flag. + */ + transport?: 'stdio' + /** + * Expose shared-state keys as MCP resources. + * - `true` (default): every key the host publishes + * - `false`: none + * - `(key) => boolean`: filter + */ + exposeSharedState?: boolean | ((key: string) => boolean) + /** Override the name reported in the MCP handshake. */ + serverName?: string + /** Override the version reported in the MCP handshake. Defaults to `definition.version ?? '0.0.0'`. */ + serverVersion?: string + /** Called once the transport is connected. */ + onReady?: (info: { transport: 'stdio' }) => void +} + +export interface McpServerHandle { + stop: () => Promise +} + +export interface CreateMcpFetchHandlerOptions { + /** Name reported in the MCP handshake. */ + serverName: string + /** Version reported in the MCP handshake. */ + serverVersion: string + /** Expose shared-state keys as MCP resources; see {@link CreateMcpServerOptions.exposeSharedState}. */ + exposeSharedState: boolean | ((key: string) => boolean) + /** + * Optional identity check, layered on top of the origin gate and checked + * **after** it: a bearer token string (matched in constant time against + * `Authorization: Bearer `), a `(request) => boolean` callback, or + * `false` (the default) for origin-only, trusting same-machine callers. A + * callback governs identity only and cannot relax the origin gate. See + * {@link McpAuthorization}. + */ + authorization?: McpAuthorization + /** + * Origin allow-list beyond the loopback default. `false` disables the + * origin gate entirely. Default: loopback-only. + * + * Unlike the WS transport, the MCP route does **not** allow `Origin`-less + * requests: a route-based endpoint is reachable by any local process, so a + * request must carry an `Origin` that passes the gate. Native clients + * (e.g. `devframe connect`) send their loopback origin explicitly. + */ + allowedOrigins?: readonly string[] | false +} + +/** Connection facts a host knows about a request beyond the `Request` itself. */ +export interface McpConnectionInfo { + /** + * The connecting peer's remote address (a node socket's `remoteAddress`), + * used to prove a same-machine caller when the endpoint relies on the + * loopback origin default with no identity check. A host that can resolve a + * trustworthy peer address (the h3/node mount) supplies it; when it's + * omitted the origin gate stays the only locality signal. + */ + remoteAddress?: string +} + +export interface McpFetchHandler { + /** + * WHATWG-`fetch` handler for the MCP endpoint. Hand every method + * (POST/GET/DELETE) on the endpoint's path to it; routing by path is the + * host's job. Pass {@link McpConnectionInfo} when the host can resolve the + * peer address so the default trust boundary can enforce same-machine + * locality. + */ + fetch: (request: Request, connection?: McpConnectionInfo) => Promise + /** Tear down the handler (aborts in-flight exchanges, drops the change bridge). */ + dispose: () => Promise +} diff --git a/packages/devframe/tsdown.config.ts b/packages/devframe/tsdown.config.ts index f41216f41..880b263e4 100644 --- a/packages/devframe/tsdown.config.ts +++ b/packages/devframe/tsdown.config.ts @@ -116,14 +116,13 @@ const serverEntries = { 'adapters/build': 'src/adapters/build.ts', 'adapters/embedded': 'src/adapters/embedded.ts', 'adapters/initiate': 'src/adapters/initiate.ts', - 'adapters/mcp': 'src/adapters/mcp/index.ts', 'cli/main': 'src/cli/main.ts', 'recipes/common-rpc-functions': 'src/recipes/common-rpc-functions.ts', 'recipes/interactive-auth': 'src/recipes/interactive-auth.ts', } /** - * Three configs: + * Four configs: * * 1. Runtime client/agnostic build (`dts: false`). Independent rolldown * chunk graph so server-only imports like `devframe/rpc/transports/ws-server` @@ -137,6 +136,11 @@ const serverEntries = { * `src/types/rpc-augments.ts`, produce exactly one declaration site. * This is what lets consumer `declare module 'devframe'` augmentations * propagate across every import chain. + * 4. The `adapters/mcp` alias, alone. Its top-level `await` (lazy-loading + * `@devframes/agentic/mcp` at module evaluation) would otherwise reshape + * the server graph's chunking - every sibling entry turns into async-safe + * re-export facades. An isolated graph keeps the TLA contained; the few + * helpers it inlines are duplicated only here. */ export default defineConfig([ { @@ -191,6 +195,14 @@ export default defineConfig([ deps, dts: { emitDtsOnly: true }, outExtensions: () => ({ dts: '.d.mts' }), - entry: { ...clientEntries, ...serverEntries }, + entry: { ...clientEntries, ...serverEntries, 'adapters/mcp': 'src/adapters/mcp.ts' }, + }, + { + clean: false, + platform: 'node', + tsconfig, + deps: nodeDeps, + dts: false, + entry: { 'adapters/mcp': 'src/adapters/mcp.ts' }, }, ]) diff --git a/packages/hub/package.json b/packages/hub/package.json index 621510ca3..cda7d690e 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -41,8 +41,14 @@ "prepack": "turbo run build --filter=@devframes/hub" }, "peerDependencies": { + "@devframes/agentic": "workspace:*", "devframe": "workspace:*" }, + "peerDependenciesMeta": { + "@devframes/agentic": { + "optional": true + } + }, "dependencies": { "@standard-schema/spec": "catalog:deps", "destr": "catalog:deps", @@ -54,6 +60,7 @@ "zigpty": "catalog:deps" }, "devDependencies": { + "@devframes/agentic": "workspace:*", "@types/node": "catalog:types", "devframe": "workspace:*", "get-port-please": "catalog:deps", diff --git a/packages/hub/src/node/initiate.ts b/packages/hub/src/node/initiate.ts index 78519220b..c43626958 100644 --- a/packages/hub/src/node/initiate.ts +++ b/packages/hub/src/node/initiate.ts @@ -1,4 +1,4 @@ -import type { DevframeInstanceRecord, InstanceShellApi, ResolvedMcpConfig } from 'devframe/internal' +import type { AgenticMcpModule, DevframeInstanceRecord, InstanceShellApi, ResolvedMcpConfig } from 'devframe/internal' import type { DevframeAuthHandler } from 'devframe/node/auth' import type { AuthBannerFunction } from 'devframe/recipes/interactive-auth' import type { WsOriginRegistry } from 'devframe/rpc/transports/ws-server' @@ -12,7 +12,7 @@ import type { InstallDevframeOptions } from './install-devframe' import { readFile } from 'node:fs/promises' import process from 'node:process' import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_MCP_ROUTE } from 'devframe/constants' -import { createH3DevframeHost, createInstanceShell, importRuntimeModule, loadAutoMcpAdapter, resolveInstanceRegister, resolveMcpConfig } from 'devframe/internal' +import { createH3DevframeHost, createInstanceShell, importAgenticMcp, loadAutoMcpAdapter, resolveInstanceRegister, resolveMcpConfig } from 'devframe/internal' import { createInteractiveAuth } from 'devframe/recipes/interactive-auth' import { mountStaticHandler } from 'devframe/utils/serve-static' import { H3 } from 'h3' @@ -500,14 +500,15 @@ export function initHub(options: InitHubOptions): HubInstance { // Aggregate MCP: one Streamable-HTTP endpoint over the shared // context's whole registry. The omitted `'auto'` default mounts when - // the devframes (or an agent-flagged hub command) left a non-empty - // agent surface; an empty surface loads no MCP code. Origin-only - // unless the config opts into a bearer/callback. + // the agent surface is non-empty AND `@devframes/agentic` is installed + // (missing peer: one DF0078 warning); an explicit setting throws + // DF0079 without the peer. Origin-only unless the config opts into a + // bearer/callback. const mcpSetting = options.mcp ?? 'auto' - let mcpModule: typeof import('devframe/adapters/mcp') | undefined + let mcpModule: AgenticMcpModule | undefined let mcpConfig: ResolvedMcpConfig | undefined if (mcpSetting === 'auto') { - mcpModule = await loadAutoMcpAdapter(ctx.agent) + mcpModule = await loadAutoMcpAdapter(ctx.agent) if (mcpModule) mcpConfig = { authorization: false } } @@ -518,7 +519,7 @@ export function initHub(options: InitHubOptions): HubInstance { return { context: ctx } const mcpRoute = withoutLeadingSlash(mcpConfig.path ?? DEVFRAME_MCP_ROUTE) - mcpModule ??= await importRuntimeModule('devframe/adapters/mcp') + mcpModule ??= await importAgenticMcp() const mounted = mcpModule.mountMcpHttp(app, ctx, joinURL(base, mcpRoute), { serverName: options.name ?? 'devframes-hub', serverVersion: options.version ?? '0.0.0', diff --git a/packages/next/src/host.ts b/packages/next/src/host.ts index 62107e9e6..cdc506b35 100644 --- a/packages/next/src/host.ts +++ b/packages/next/src/host.ts @@ -1,6 +1,6 @@ import type { ConnectionMeta, DevframeHost, DevframeNodeContext, DevframeStorageScope, McpAuthorization } from 'devframe' import { DEVFRAME_CONNECTION_META_FILENAME } from 'devframe/constants' -import { importRuntimeModule } from 'devframe/internal' +import { importAgenticMcp } from 'devframe/internal' import { serveStaticHandler } from 'devframe/utils/serve-static' import { H3 } from 'h3' @@ -81,8 +81,9 @@ export interface DevframeNextHost { * Serve an MCP Streamable-HTTP endpoint at `path` **in-process**, on the * Next app's own origin, through the same catch-all route as the SPAs (the * `/_next/mcp` shape). Built on `createMcpFetchHandler` from - * `devframe/adapters/mcp` (imported lazily, so the MCP SDK stays out of - * the app's bundle graph). Advertise the path in the connection meta + * `@devframes/agentic/mcp` (imported lazily through the optional peer, so + * neither it nor the MCP SDK enters the app's bundle graph; a missing peer + * throws DF0079). Advertise the path in the connection meta * (`mcp: { path }`, same origin, no port) and register the instance via * `registerDevframeInstance` so `devframe connect` can discover it. */ @@ -174,7 +175,7 @@ export function createDevframeNextHost( connectionMeta = meta }, async mountMcp(ctx, path, mcpOptions = {}) { - const { createMcpFetchHandler } = await importRuntimeModule('devframe/adapters/mcp') + const { createMcpFetchHandler } = await importAgenticMcp() const handler = createMcpFetchHandler(ctx, { serverName: mcpOptions.serverName ?? 'devframe (next)', serverVersion: mcpOptions.serverVersion ?? '0.0.0', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e399b34cb..de406d783 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -555,6 +555,7 @@ catalogs: version: 8.18.1 overrides: + '@devframes/agentic': workspace:* '@devframes/hub': workspace:* '@devframes/hub-ui': workspace:* '@devframes/json-render': workspace:* @@ -596,7 +597,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@antfu/eslint-config': specifier: catalog:tooling version: 9.5.1(@typescript-eslint/typescript-estree@8.69.0(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.69.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.42)(eslint-plugin-slop@0.1.3(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-sonarjs@4.2.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@5.0.0(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) @@ -722,7 +723,7 @@ importers: version: 4.4.3(react@19.2.8)(shiki@4.4.3)(solid-js@1.9.15)(svelte@5.57.0(@typescript-eslint/types@8.69.0))(vue@3.5.42(typescript@6.0.3)) comark-docs: specifier: catalog:docs - version: https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(c0fd9af7c481da357e4beac4fc989fc5) + version: https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(be895fb79464905e24531015ffa32a57) d3-shape: specifier: catalog:frontend version: 3.2.0 @@ -744,7 +745,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -775,7 +776,10 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + '@devframes/agentic': + specifier: workspace:* + version: link:../../packages/agentic '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -875,7 +879,10 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + '@devframes/agentic': + specifier: workspace:* + version: link:../../packages/agentic '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -961,7 +968,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) cac: specifier: catalog:deps version: 7.0.0 @@ -1441,7 +1448,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/next': specifier: workspace:* version: link:../../packages/next @@ -1496,7 +1503,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) devframe: specifier: workspace:* version: link:../../packages/devframe @@ -1521,7 +1528,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) cac: specifier: catalog:deps version: 7.0.0 @@ -1560,11 +1567,42 @@ importers: specifier: catalog:deps version: 8.21.3 - packages/devframe: + packages/agentic: dependencies: + '@modelcontextprotocol/client': + specifier: catalog:deps + version: 2.0.0 '@modelcontextprotocol/server': specifier: catalog:deps version: 2.0.0 + '@standard-schema/spec': + specifier: catalog:deps + version: 1.1.0 + h3: + specifier: catalog:deps + version: 2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)) + pathe: + specifier: catalog:deps + version: 2.0.3 + ufo: + specifier: catalog:deps + version: 1.6.4 + devDependencies: + '@types/node': + specifier: catalog:types + version: 26.5.1 + devframe: + specifier: workspace:* + version: link:../devframe + tsdown: + specifier: catalog:build + version: 0.23.0(@volar/typescript@2.4.28(typescript@6.0.3))(oxc-resolver@11.24.2)(tsx@4.23.13)(typescript@6.0.3) + valibot: + specifier: catalog:deps + version: 1.5.0(typescript@6.0.3) + + packages/devframe: + dependencies: '@standard-schema/spec': specifier: catalog:deps version: 1.1.0 @@ -1590,9 +1628,9 @@ importers: specifier: catalog:deps version: 1.6.4 devDependencies: - '@modelcontextprotocol/client': - specifier: catalog:deps - version: 2.0.0 + '@devframes/agentic': + specifier: workspace:* + version: link:../agentic cac: specifier: catalog:deps version: 7.0.0 @@ -1672,6 +1710,9 @@ importers: specifier: catalog:deps version: 0.2.1 devDependencies: + '@devframes/agentic': + specifier: workspace:* + version: link:../agentic '@types/node': specifier: catalog:types version: 26.5.1 @@ -1695,7 +1736,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../hub @@ -1786,7 +1827,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../hub @@ -1946,7 +1987,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-a11y--assets': specifier: workspace:* version: link:assets-pkg @@ -2024,7 +2065,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-assets--assets': specifier: workspace:* version: link:assets-pkg @@ -2060,7 +2101,7 @@ importers: version: link:../../packages/devframe floating-vue: specifier: catalog:frontend - version: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) + version: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) h3: specifier: catalog:deps version: 2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)) @@ -2099,7 +2140,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-code-server--assets': specifier: workspace:* version: link:assets-pkg @@ -2159,7 +2200,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-data-inspector--assets': specifier: workspace:* version: link:assets-pkg @@ -2249,7 +2290,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-git--assets': specifier: workspace:* version: link:assets-pkg @@ -2336,7 +2377,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -2414,7 +2455,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -2492,7 +2533,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-og--assets': specifier: workspace:* version: link:assets-pkg @@ -2558,7 +2599,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-terminals--assets': specifier: workspace:* version: link:assets-pkg @@ -2722,7 +2763,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../packages/hub @@ -13008,7 +13049,7 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13017,6 +13058,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.6.1 dompurify: 3.4.14 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -13024,7 +13066,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13033,6 +13075,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.6.1 dompurify: 3.4.15 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -13040,7 +13083,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13049,6 +13092,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.6.1 dompurify: 3.4.15 floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) @@ -13056,7 +13100,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13065,6 +13109,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.14 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -13072,7 +13117,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13081,14 +13126,15 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.14 - floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) + floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) playwright: 1.62.1 reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13097,14 +13143,15 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.14 - floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) + floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) playwright: 1.62.1 reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13113,6 +13160,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.15 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -14361,7 +14409,7 @@ snapshots: '@modelcontextprotocol/server@2.0.0': dependencies: '@modelcontextprotocol/core': 2.0.0 - zod: 4.5.4 + zod: 4.6.5 '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: @@ -14877,7 +14925,7 @@ snapshots: - rolldown - unplugin - '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': + '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.4) consola: 3.4.2 @@ -14897,7 +14945,7 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 - unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.3.2 transitivePeerDependencies: @@ -14908,7 +14956,7 @@ snapshots: - unplugin optional: true - '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': + '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.4) consola: 3.4.2 @@ -14928,7 +14976,7 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 - unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.3.2 transitivePeerDependencies: @@ -14939,7 +14987,7 @@ snapshots: - unplugin optional: true - '@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': + '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.4) consola: 3.4.2 @@ -14959,7 +15007,7 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 - unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.3.2 transitivePeerDependencies: @@ -15443,12 +15491,12 @@ snapshots: - rolldown - unplugin - '@nuxtjs/mcp-toolkit@0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5)': + '@nuxtjs/mcp-toolkit@0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5)': dependencies: '@modelcontextprotocol/sdk': 1.30.0(supports-color@10.2.2)(zod@4.6.5) '@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) '@vitejs/plugin-vue': 6.0.8(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)) - h3: 2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)) + h3: 2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)) tinyglobby: 0.2.17 vite-plugin-singlefile: 2.3.3(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) zod: 4.6.5 @@ -18785,7 +18833,7 @@ snapshots: - srvx - uploadthing - comark-docs@https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(c0fd9af7c481da357e4beac4fc989fc5): + comark-docs@https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(be895fb79464905e24531015ffa32a57): dependencies: '@ai-sdk/gateway': 4.0.69(zod@4.6.5) '@ai-sdk/vue': 4.0.85(vue@3.5.42(typescript@6.0.3))(zod@4.6.5) @@ -18796,7 +18844,7 @@ snapshots: '@iconify/vue': 5.0.1(vue@3.5.42(typescript@6.0.3)) '@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) '@nuxt/ui': 4.11.0(2f33558f9603787933b49f7068ea6162) - '@nuxtjs/mcp-toolkit': 0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5) + '@nuxtjs/mcp-toolkit': 0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5) '@nuxtjs/robots': 6.2.0(990ae1d11bf7f0267e37b5cee9a36fdb) '@nuxtjs/sitemap': 8.5.0(990ae1d11bf7f0267e37b5cee9a36fdb) '@octokit/webhooks-methods': 6.0.0 @@ -19991,26 +20039,26 @@ snapshots: vue-resize: 2.0.0-alpha.1(vue@3.5.42(typescript@6.0.3)) optional: true - floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): + floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.8.0 vue: 3.5.42(typescript@6.0.3) optionalDependencies: - '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) - floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): + floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.8.0 vue: 3.5.42(typescript@6.0.3) optionalDependencies: - '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) - floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): + floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.8.0 vue: 3.5.42(typescript@6.0.3) optionalDependencies: - '@nuxt/kit': 4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) fnv1a-64@0.1.2: {} @@ -20248,6 +20296,13 @@ snapshots: optionalDependencies: crossws: 0.4.12(srvx@0.11.22) + h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)): + dependencies: + rou3: 0.9.2 + srvx: 1.0.3 + optionalDependencies: + crossws: 0.4.12(srvx@0.11.22) + h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)): dependencies: rou3: 0.9.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0e25b04fc..11d061986 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -48,6 +48,7 @@ packages: - docs overrides: + '@devframes/agentic': 'workspace:*' '@devframes/hub': 'workspace:*' '@devframes/hub-ui': 'workspace:*' '@devframes/json-render': 'workspace:*' diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts new file mode 100644 index 000000000..72bbef30a --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts @@ -0,0 +1,20 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/connect` + */ +// #region Interfaces +export interface ConnectServerHandle { + stop: () => Promise; +} +export interface ConnectServerOptions { + ports?: number[]; + instancesDir?: string; + timeoutMs?: number; + authToken?: string | ((_: DevframeInstanceRecord) => string | undefined); +} +// #endregion + +// #region Functions +export declare function buildInstanceRequestHeaders(_: string, _: string | undefined): Record; +export declare function resolveAuthToken(_: ConnectServerOptions['authToken'], _: DevframeInstanceRecord): string | undefined; +export declare function startConnectServer(_?: ConnectServerOptions): Promise; +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js new file mode 100644 index 000000000..d08928240 --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js @@ -0,0 +1,8 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/connect` + */ +// #region Functions +export function buildInstanceRequestHeaders(_, _) {} +export function resolveAuthToken(_, _) {} +export async function startConnectServer(_) {} +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts new file mode 100644 index 000000000..3813681bc --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts @@ -0,0 +1,4 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic` + */ +/* no exports */ \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js new file mode 100644 index 000000000..3813681bc --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js @@ -0,0 +1,4 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic` + */ +/* no exports */ \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts new file mode 100644 index 000000000..35be0c19d --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts @@ -0,0 +1,18 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/mcp` + */ +// #region Functions +export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions$1): McpFetchHandler$1; +export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions$1): Promise; +export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions$1): MountedMcpHttp$1; +// #endregion + +// #region Other +export { CreateMcpFetchHandlerOptions } +export { CreateMcpServerOptions } +export { McpConnectionInfo } +export { McpFetchHandler } +export { McpServerHandle } +export { MountedMcpHttp } +export { MountMcpHttpOptions } +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js new file mode 100644 index 000000000..61a4e375e --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js @@ -0,0 +1,8 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/mcp` + */ +// #region Functions +export function createMcpFetchHandler(_, _) {} +export async function createMcpServer(_, _) {} +export function mountMcpHttp(_, _, _, _) {} +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts index a69263e97..c7f9c3fa2 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts @@ -1,41 +1,18 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Interfaces -export interface CreateMcpFetchHandlerOptions { - serverName: string; - serverVersion: string; - exposeSharedState: boolean | ((_: string) => boolean); - authorization?: McpAuthorization; - allowedOrigins?: readonly string[] | false; -} -export interface CreateMcpServerOptions { - transport?: 'stdio'; - exposeSharedState?: boolean | ((_: string) => boolean); - serverName?: string; - serverVersion?: string; - onReady?: (_: { - transport: 'stdio'; - }) => void; -} -export interface McpConnectionInfo { - remoteAddress?: string; -} -export interface McpFetchHandler { - fetch: (_: Request, _?: McpConnectionInfo) => Promise; - dispose: () => Promise; -} -export interface McpServerHandle { - stop: () => Promise; -} -export interface MountedMcpHttp { - dispose: () => Promise; -} -export interface MountMcpHttpOptions extends CreateMcpFetchHandlerOptions {} +// #region Variables +export declare const createMcpFetchHandler: (ctx: DevframeNodeContext, options: CreateMcpFetchHandlerOptions) => McpFetchHandler; +export declare const createMcpServer: (definition: DevframeDefinition, options?: CreateMcpServerOptions) => Promise; +export declare const mountMcpHttp: (app: import("h3").H3, ctx: DevframeNodeContext, path: string, options: MountMcpHttpOptions) => MountedMcpHttp; // #endregion -// #region Functions -export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions): McpFetchHandler; -export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions): Promise; -export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions): MountedMcpHttp; +// #region Other +export { CreateMcpFetchHandlerOptions } +export { CreateMcpServerOptions } +export { McpConnectionInfo } +export { McpFetchHandler } +export { McpServerHandle } +export { MountedMcpHttp } +export { MountMcpHttpOptions } // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js index bd0cf3670..c0bfa5916 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js @@ -1,8 +1,8 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Functions -export function createMcpFetchHandler(_, _) {} -export async function createMcpServer(_, _) {} -export function mountMcpHttp(_, _, _, _) {} +// #region Variables +export var createMcpFetchHandler /* const */ +export var createMcpServer /* const */ +export var mountMcpHttp /* const */ // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts index 7dbdabed2..0f8c1490c 100644 --- a/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts @@ -87,6 +87,22 @@ export interface ConnectionMetaWebsocket { port?: number; host?: string; } +export interface CreateMcpFetchHandlerOptions { + serverName: string; + serverVersion: string; + exposeSharedState: boolean | ((_: string) => boolean); + authorization?: McpAuthorization; + allowedOrigins?: readonly string[] | false; +} +export interface CreateMcpServerOptions { + transport?: 'stdio'; + exposeSharedState?: boolean | ((_: string) => boolean); + serverName?: string; + serverVersion?: string; + onReady?: (_: { + transport: 'stdio'; + }) => void; +} export interface DevframeAgentHost { readonly events: EventEmitter; registerTool: (_: AgentToolInput) => AgentHandle; @@ -415,11 +431,21 @@ export interface EventsMap { export interface EventUnsubscribe { (): void; } +export interface McpConnectionInfo { + remoteAddress?: string; +} +export interface McpFetchHandler { + fetch: (_: Request, _?: McpConnectionInfo) => Promise; + dispose: () => Promise; +} export interface McpRouteOptions { path?: string; authorization?: McpAuthorization; allowedOrigins?: readonly string[] | false; } +export interface McpServerHandle { + stop: () => Promise; +} export interface RemoteAssets { package: string; version: string; diff --git a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts index 4e01745e3..00cc7f772 100644 --- a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts @@ -49,10 +49,12 @@ export declare class DevframeAgentHost implements DevframeAgentHost$1 { // #endregion // #region Functions +export declare function argsToJsonSchema(_: readonly StandardSchemaV1[] | undefined): unknown; /** @deprecated */ export declare function coerceAgentPositionalArgs(_: unknown, _: readonly unknown[] | undefined, _?: AgentArgsFallback): unknown[]; export declare function createH3DevframeHost(_: CreateH3DevframeHostOptions): DevframeHost; export declare function createRpcWireCodec(_?: ReadonlyMap>): RpcWireCodec; +export declare function formatMcpError(_: unknown): string; export declare function importRuntimeModule(_: string): Promise; export declare function normalizeHttpServerUrl(_: string, _: number | string): string; export declare function peekRpcWireFrame(_: string): { @@ -60,6 +62,8 @@ export declare function peekRpcWireFrame(_: string): { i?: string; }; export declare function resolveClientAssets(_: DevframeDefinition): StaticAssetsSource | undefined; +export declare function returnToJsonSchema(_: StandardSchemaV1 | undefined): unknown; +export declare function stringifyForMcp(_: unknown): string; export declare function toolInputToCommandArgs(_: unknown, _?: number): unknown[]; // #endregion @@ -188,7 +192,7 @@ export declare const diagnostics: import("nostics").Diagnostics<{ readonly why: (p: { reason: string; }) => string; - readonly fix: "Install it next to devframe (e.g. `npm install @modelcontextprotocol/client`) and run `devframe connect` again."; + readonly fix: "Install it next to devframe (e.g. `npm install @devframes/agentic`) and run `devframe connect` again."; }; readonly DF0047: { readonly why: (p: { @@ -365,12 +369,23 @@ export declare const diagnostics: import("nostics").Diagnostics<{ }) => string; readonly fix: "On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno example), or connect over the SSE endpoint instead."; }; + readonly DF0078: { + readonly why: "This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer \"@devframes/agentic\" is not installed."; + readonly fix: "Install `@devframes/agentic` next to devframe to serve the MCP endpoint, or set `mcp: false` to opt out silently."; + }; + readonly DF0079: { + readonly why: (p: { + reason: string; + }) => string; + readonly fix: "Install `@devframes/agentic` next to devframe (the MCP adapter and the MCP SDK live there), or remove the explicit `mcp` setting."; + }; }, readonly [(d: import("nostics").Diagnostic, { method }?: { method?: "log" | "warn" | "error"; }) => void]>; // #endregion // #region Other +export { AgenticMcpModule } export { ContextRpcServer } export { createContextRpcServer } export { CreateContextRpcServerOptions } @@ -378,6 +393,7 @@ export { createInstanceShell } export { CreateInstanceShellOptions } export { DevframeInstanceRecord } export { DevframeInstanceRegistration } +export { importAgenticMcp } export { InstanceRegisterConfig } export { InstanceShell } export { InstanceShellApi } @@ -386,7 +402,10 @@ export { InstanceShellInternals } export { InstanceWsTier } export { listLiveDevframeInstances } export { loadAutoMcpAdapter } +export { MountedMcpHttp } +export { MountMcpHttpOptions } export { normalizeBasePath } +export { probeDevframeOrigin } export { registerDevframeInstance } export { resolveBasePath } export { ResolvedMcpConfig } diff --git a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js index 1dc5bcb58..a4f777409 100644 --- a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js @@ -1,6 +1,13 @@ /** * Generated by tsnapi — public API snapshot of `devframe/internal` */ +// #region Functions +export function argsToJsonSchema(_) {} +export function formatMcpError(_) {} +export function returnToJsonSchema(_) {} +export function stringifyForMcp(_) {} +// #endregion + // #region Other export { coerceAgentPositionalArgs } export { createContextRpcServer } @@ -9,12 +16,14 @@ export { createInstanceShell } export { createRpcWireCodec } export { DevframeAgentHost } export { diagnostics } +export { importAgenticMcp } export { importRuntimeModule } export { listLiveDevframeInstances } export { loadAutoMcpAdapter } export { normalizeBasePath } export { normalizeHttpServerUrl } export { peekRpcWireFrame } +export { probeDevframeOrigin } export { registerDevframeInstance } export { resolveBasePath } export { resolveClientAssets } diff --git a/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts index c062d1213..2604d37fd 100644 --- a/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts @@ -14,6 +14,8 @@ export { AgentToolProviderHandle } export { ConnectionMeta } export { ConnectionMetaSse } export { ConnectionMetaWebsocket } +export { CreateMcpFetchHandlerOptions } +export { CreateMcpServerOptions } export { DevframeAgentHost } export { DevframeAgentHostEvents } export { DevframeCapabilities } @@ -66,7 +68,10 @@ export { EventEmitter } export { EventsMap } export { EventUnsubscribe } export { McpAuthorization } +export { McpConnectionInfo } +export { McpFetchHandler } export { McpRouteOptions } +export { McpServerHandle } export { McpSetting } export { RemoteAssets } export { RemoteAssetsErrorMessage } diff --git a/tests/optional-mcp-bundles.test.ts b/tests/optional-mcp-bundles.test.ts index 352f1a48a..3959e821e 100644 --- a/tests/optional-mcp-bundles.test.ts +++ b/tests/optional-mcp-bundles.test.ts @@ -23,12 +23,15 @@ afterEach(() => { describe('the MCP SDK stays out of consumer bundles', () => { it.each(entries)('bundles %s without resolving the MCP SDK', async (entry) => { const resolvedMcpImports: string[] = [] + // Both the SDK and `@devframes/agentic` (the optional peer wrapping it) + // must stay behind `importRuntimeModule`, never statically resolvable + // from a consumer entry. const rejectMcpSdk: Plugin = { name: 'reject-mcp-sdk', setup(context) { - context.onResolve({ filter: /^@modelcontextprotocol\// }, (args) => { + context.onResolve({ filter: /^(?:@modelcontextprotocol|@devframes\/agentic)(?:\/|$)/ }, (args) => { resolvedMcpImports.push(args.path) - return { errors: [{ text: `Unexpected MCP SDK import: ${args.path}` }] } + return { errors: [{ text: `Unexpected MCP import: ${args.path}` }] } }) }, } diff --git a/tsconfig.base.json b/tsconfig.base.json index 1d9e90c58..f273c2b41 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -125,7 +125,16 @@ "./packages/devframe/src/adapters/initiate.ts" ], "devframe/adapters/mcp": [ - "./packages/devframe/src/adapters/mcp/index.ts" + "./packages/devframe/src/adapters/mcp.ts" + ], + "@devframes/agentic/mcp": [ + "./packages/agentic/src/mcp/index.ts" + ], + "@devframes/agentic/connect": [ + "./packages/agentic/src/connect/index.ts" + ], + "@devframes/agentic": [ + "./packages/agentic/src/index.ts" ], "@devframes/hub/build": [ "./packages/hub/src/node/build.ts" diff --git a/turbo.json b/turbo.json index e3c5c8990..52908e9e1 100644 --- a/turbo.json +++ b/turbo.json @@ -5,6 +5,17 @@ "typecheck": { "dependsOn": ["^typecheck", "@devframes/hub-ui#build:css", "@devframes/json-render-ui#build:css"] }, + // `devframe` ⇄ `@devframes/agentic` are circular in the package graph + // (agentic peers on devframe; devframe devDeps agentic so tests can + // exercise the installed-peer path), which `^typecheck` would turn into + // a task cycle. Both typecheck from source via `paths` aliases, so + // explicit dependency-free tasks break the cycle. + "devframe#typecheck": { + "dependsOn": [] + }, + "@devframes/agentic#typecheck": { + "dependsOn": [] + }, "@devframes/hub-ui#build:css": { "outputLogs": "new-only", "outputs": ["src/client/.generated/**"] @@ -17,6 +28,11 @@ "outputLogs": "new-only", "outputs": ["dist/**"] }, + "@devframes/agentic#build": { + "outputLogs": "new-only", + "dependsOn": ["devframe#build"], + "outputs": ["dist/**"] + }, "@devframes/hub#build": { "outputLogs": "new-only", "dependsOn": ["devframe#build"], diff --git a/vitest.config.ts b/vitest.config.ts index c16bb3b7c..8d4eaad52 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -13,6 +13,7 @@ export default defineConfig({ }, test: { projects: [ + 'packages/agentic', 'packages/devframe', 'packages/hub', 'packages/hub-ui', From b0880ec12b23af6c5b5a7ba21d496d2a9d5fca1f Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 14 Sep 2026 22:45:30 +0000 Subject: [PATCH 2/6] docs: move the @devframes/agentic migration into a 0.10 upgrade guide --- docs/content/7.migrations/1.migration-0.10.md | 45 +++++++++++++++++++ ...{1.migration-0.9.md => 2.migration-0.9.md} | 15 ------- ...{2.migration-0.8.md => 3.migration-0.8.md} | 0 ...{3.migration-0.7.md => 4.migration-0.7.md} | 0 ...{4.migration-0.6.md => 5.migration-0.6.md} | 0 docs/content/7.migrations/index.md | 1 + 6 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 docs/content/7.migrations/1.migration-0.10.md rename docs/content/7.migrations/{1.migration-0.9.md => 2.migration-0.9.md} (91%) rename docs/content/7.migrations/{2.migration-0.8.md => 3.migration-0.8.md} (100%) rename docs/content/7.migrations/{3.migration-0.7.md => 4.migration-0.7.md} (100%) rename docs/content/7.migrations/{4.migration-0.6.md => 5.migration-0.6.md} (100%) diff --git a/docs/content/7.migrations/1.migration-0.10.md b/docs/content/7.migrations/1.migration-0.10.md new file mode 100644 index 000000000..363ba65fd --- /dev/null +++ b/docs/content/7.migrations/1.migration-0.10.md @@ -0,0 +1,45 @@ +--- +title: 'Migrating to 0.10' +description: '0.10 moves the MCP implementation and the MCP SDK into @devframes/agentic, a new optional peer: install it to serve agent tools over MCP; imports are unchanged.' +--- + +0.10 moves the MCP implementation and the MCP SDK out of `devframe` into [`@devframes/agentic`](/adapters/mcp), a new **optional peer**. A devframe without an agent surface ships slimmer (no MCP SDK installed at all); one that exposes agent tools adds a single install. Your imports do not change. + +## MCP requires `@devframes/agentic` + +`devframe/adapters/mcp` stays the user-facing API and now lazy-loads its implementation from the `@devframes/agentic` peer; the peer itself is never imported directly. Install it wherever an MCP surface should be served: + +```sh +npm install @devframes/agentic +``` + +Every surface reacts to the missing peer the same way: + +| `mcp` setting | peer installed | peer missing | +| ------------- | -------------- | ------------ | +| omitted / `'auto'` | mounts once the agent surface is non-empty | non-empty surface: warns once ([DF0078](/errors/DF0078)), mounts nothing; empty surface: silent, no MCP code loads | +| `true` / route options object | always mounts | throws [DF0079](/errors/DF0079) | +| `false` | never mounts, never probes | same | + +Importing `devframe/adapters/mcp` itself without the peer also throws [DF0079](/errors/DF0079). This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits. + +The exports are unchanged: `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`, and their option types (the option and handle types are also importable from `devframe/types`). The ` mcp` stdio subcommand keeps working with the peer installed. + +## `devframe connect` requires `@devframes/agentic` + +The connector's gateway moved into the same peer, replacing the former `@modelcontextprotocol/client` optional peer. `devframe connect` without it throws [DF0046](/errors/DF0046): + +| 0.9.x | 0.10 | +|-------|------| +| `npm install @modelcontextprotocol/client` (optional peer for `devframe connect`) | `npm install @devframes/agentic` | + +## `devframe` no longer depends on the MCP SDK + +`@modelcontextprotocol/server` left `devframe`'s dependencies and `@modelcontextprotocol/client` left its optional peers; both are now regular dependencies of `@devframes/agentic`, so installing the peer is the whole story. If you had installed `@modelcontextprotocol/client` only for `devframe connect`, you can drop it: + +```sh +npm uninstall @modelcontextprotocol/client +npm install @devframes/agentic +``` + +Code that imported SDK types directly for devframe's MCP options no longer needs to: the full option surface is typed on `devframe/adapters/mcp` and `devframe/types` without any SDK types. diff --git a/docs/content/7.migrations/1.migration-0.9.md b/docs/content/7.migrations/2.migration-0.9.md similarity index 91% rename from docs/content/7.migrations/1.migration-0.9.md rename to docs/content/7.migrations/2.migration-0.9.md index 9d923a448..ad5d2f0dd 100644 --- a/docs/content/7.migrations/1.migration-0.9.md +++ b/docs/content/7.migrations/2.migration-0.9.md @@ -5,21 +5,6 @@ description: '0.9 removes the compatibility shims deprecated across the 0.7 seri 0.9 removes the compatibility shims deprecated across the 0.7 series and trims the public API of `devframe` and `@devframes/hub`. Each change has a drop-in replacement. It also moves the [MCP](/adapters/mcp) surface to the stateless [MCP 2026-07-28 protocol](https://modelcontextprotocol.io/specification/2026-07-28). The devframe API is unchanged; see [The MCP endpoints are stateless](#the-mcp-endpoints-are-stateless). -Late in the 0.9 series the MCP implementation moved into the new optional peer `@devframes/agentic`; see [MCP requires `@devframes/agentic`](#mcp-requires-devframesagentic). - -## MCP requires `@devframes/agentic` - -The MCP implementation and the MCP SDK moved out of `devframe` into `@devframes/agentic`, an **optional peer**: install it to serve agent tools over MCP, skip it for a slimmer install without an MCP surface. Your imports do not change - `devframe/adapters/mcp` stays the user-facing API and loads the peer for you; `@devframes/agentic` is never imported directly. - -> [!WARNING] -> This is a behavior change within the 0.9 series: `devframe` no longer depends on the MCP SDK, so MCP now requires `@devframes/agentic` to be installed. Without it, the omitted `'auto'` default warns once ([DF0078](/errors/DF0078)) and mounts nothing; an explicit `mcp` setting - and importing `devframe/adapters/mcp` itself - throws ([DF0079](/errors/DF0079)); `mcp: false` stays silent. - -```sh -npm install @devframes/agentic -``` - -The `devframe/adapters/mcp` exports are unchanged: `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`, and their option types (the types are also importable from `devframe/types`). `devframe connect` needs the peer too and throws [DF0046](/errors/DF0046) without it, replacing the former `@modelcontextprotocol/client` optional peer. - ## `devframe/adapters/cli` is removed The `devframe/adapters/cli` entry is gone; import from `devframe/adapters/cac`: diff --git a/docs/content/7.migrations/2.migration-0.8.md b/docs/content/7.migrations/3.migration-0.8.md similarity index 100% rename from docs/content/7.migrations/2.migration-0.8.md rename to docs/content/7.migrations/3.migration-0.8.md diff --git a/docs/content/7.migrations/3.migration-0.7.md b/docs/content/7.migrations/4.migration-0.7.md similarity index 100% rename from docs/content/7.migrations/3.migration-0.7.md rename to docs/content/7.migrations/4.migration-0.7.md diff --git a/docs/content/7.migrations/4.migration-0.6.md b/docs/content/7.migrations/5.migration-0.6.md similarity index 100% rename from docs/content/7.migrations/4.migration-0.6.md rename to docs/content/7.migrations/5.migration-0.6.md diff --git a/docs/content/7.migrations/index.md b/docs/content/7.migrations/index.md index bcee572e0..b0ffcb050 100644 --- a/docs/content/7.migrations/index.md +++ b/docs/content/7.migrations/index.md @@ -7,6 +7,7 @@ Upgrade guides for devframe and `@devframes/hub`, newest first. Each one lists e | Version | What changed | | ------- | ------------ | +| [Migrating to 0.10](/migrations/migration-0.10) | Moves the MCP implementation and the MCP SDK into `@devframes/agentic`, a new optional peer; imports are unchanged. | | [Migrating to 0.9](/migrations/migration-0.9) | Removes the compatibility shims deprecated across the 0.7 series, trims the public API, and moves the MCP surface to the stateless MCP 2026-07-28 protocol. | | [Migrating to 0.8](/migrations/migration-0.8) | Makes RPC schemas validator-neutral and runtime-validated, and adds the agent-native MCP API. | | [Migrating to 0.7](/migrations/migration-0.7) | Makes `cac` an optional peer and moves json-render into an opt-in package. | From 490fc3617905d6e28a1f50d7ccf28f39806cb089 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 14 Sep 2026 23:07:23 +0000 Subject: [PATCH 3/6] refactor: lazy-load agentic inside the devframe/adapters/mcp functions Replaces the alias's top-level await with a per-function lazy import: importing the entry is now side-effect-free (DF0079 moves to call time), and without TLA the entry rejoins the main server build graph, dropping the isolated tsdown config. createMcpFetchHandler and mountMcpHttp become async through this entry; the migration guide covers the added await. --- docs/content/6.errors/DF0079.md | 2 +- docs/content/7.migrations/1.migration-0.10.md | 11 ++++- packages/devframe/src/adapters/mcp.ts | 49 ++++++++++++++----- packages/devframe/tsdown.config.ts | 18 ++----- .../devframe/adapters/mcp.snapshot.d.ts | 8 +-- .../tsnapi/devframe/adapters/mcp.snapshot.js | 8 +-- tests/optional-mcp-bundles.test.ts | 1 + 7 files changed, 60 insertions(+), 37 deletions(-) diff --git a/docs/content/6.errors/DF0079.md b/docs/content/6.errors/DF0079.md index e737cf98e..a4305e389 100644 --- a/docs/content/6.errors/DF0079.md +++ b/docs/content/6.errors/DF0079.md @@ -9,7 +9,7 @@ description: 'The mcp option is enabled, but the optional peer "@devframes/agent ## Cause -An explicit `mcp` setting (`true`, a route options object, the `--mcp` flag, or the `mcp` CLI subcommand) asked for an MCP surface - or `devframe/adapters/mcp` was imported directly - but the implementation could not be loaded from the optional `@devframes/agentic` peer, typically because it is not installed. Unlike the omitted `'auto'` default (which degrades to a one-time [DF0078](/errors/DF0078) warning), an explicit opt-in fails fast rather than silently running without MCP. +An explicit `mcp` setting (`true`, a route options object, the `--mcp` flag, or the `mcp` CLI subcommand) asked for an MCP surface - or a `devframe/adapters/mcp` function was called - but the implementation could not be loaded from the optional `@devframes/agentic` peer, typically because it is not installed. Unlike the omitted `'auto'` default (which degrades to a one-time [DF0078](/errors/DF0078) warning), an explicit opt-in fails fast rather than silently running without MCP. ## Fix diff --git a/docs/content/7.migrations/1.migration-0.10.md b/docs/content/7.migrations/1.migration-0.10.md index 363ba65fd..e76a6ccda 100644 --- a/docs/content/7.migrations/1.migration-0.10.md +++ b/docs/content/7.migrations/1.migration-0.10.md @@ -21,9 +21,16 @@ Every surface reacts to the missing peer the same way: | `true` / route options object | always mounts | throws [DF0079](/errors/DF0079) | | `false` | never mounts, never probes | same | -Importing `devframe/adapters/mcp` itself without the peer also throws [DF0079](/errors/DF0079). This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits. +Calling a `devframe/adapters/mcp` function without the peer also throws [DF0079](/errors/DF0079); the import itself stays side-effect-free and loads no MCP code. This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits. -The exports are unchanged: `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`, and their option types (the option and handle types are also importable from `devframe/types`). The ` mcp` stdio subcommand keeps working with the peer installed. +The exports keep their names and option types (also importable from `devframe/types`): `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`. The latter two are now **async** - they await the lazy peer load - so add an `await` where you called them synchronously: + +```diff +- const handler = createMcpFetchHandler(ctx, options) ++ const handler = await createMcpFetchHandler(ctx, options) +``` + +The ` mcp` stdio subcommand keeps working with the peer installed. ## `devframe connect` requires `@devframes/agentic` diff --git a/packages/devframe/src/adapters/mcp.ts b/packages/devframe/src/adapters/mcp.ts index ff4e3ee77..28d7cec8e 100644 --- a/packages/devframe/src/adapters/mcp.ts +++ b/packages/devframe/src/adapters/mcp.ts @@ -1,8 +1,16 @@ // The user-facing MCP adapter entry. The implementation (and the MCP SDK) // lives in the optional `@devframes/agentic` peer, which is never imported -// directly: this entry lazy-loads it (throwing a coded DF0079 when the peer -// is not installed) and re-exports the surface, typed against devframe's own -// contract in `types/mcp.ts`. +// directly: each function lazy-loads it on first call (throwing a coded +// DF0079 when the peer is not installed), so importing this entry stays +// side-effect-free and loads no MCP code. Signatures are typed against +// devframe's own contract in `types/mcp.ts`; `createMcpFetchHandler` and +// `mountMcpHttp` are async here (they await the lazy load), unlike the +// synchronous implementations behind them. +import type { H3 } from 'h3' +import type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic' +import type { DevframeNodeContext } from '../types/context' +import type { DevframeDefinition } from '../types/devframe' +import type { CreateMcpFetchHandlerOptions, CreateMcpServerOptions, McpFetchHandler, McpServerHandle } from '../types/mcp' import { importAgenticMcp } from '../node/agentic' export type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic' @@ -14,12 +22,31 @@ export type { McpServerHandle, } from '../types/mcp' -// The alias must resolve its exports at module evaluation (they are consumed -// as plain named imports), so the lazy load is a deliberate top-level await; -// it builds in its own graph (see tsdown.config.ts) to keep TLA contained. -// eslint-disable-next-line antfu/no-top-level-await -const mcp = await importAgenticMcp() +/** Build an MCP server over the agent surface of a devframe definition (stdio). */ +export async function createMcpServer( + definition: DevframeDefinition, + options?: CreateMcpServerOptions, +): Promise { + const mcp = await importAgenticMcp() + return mcp.createMcpServer(definition, options) +} -export const createMcpServer = mcp.createMcpServer -export const createMcpFetchHandler = mcp.createMcpFetchHandler -export const mountMcpHttp = mcp.mountMcpHttp +/** Build a framework-agnostic `Request → Response` MCP endpoint over a devframe context. */ +export async function createMcpFetchHandler( + ctx: DevframeNodeContext, + options: CreateMcpFetchHandlerOptions, +): Promise { + const mcp = await importAgenticMcp() + return mcp.createMcpFetchHandler(ctx, options) +} + +/** Mount a stateless MCP endpoint on an h3 app at `path`. */ +export async function mountMcpHttp( + app: H3, + ctx: DevframeNodeContext, + path: string, + options: MountMcpHttpOptions, +): Promise { + const mcp = await importAgenticMcp() + return mcp.mountMcpHttp(app, ctx, path, options) +} diff --git a/packages/devframe/tsdown.config.ts b/packages/devframe/tsdown.config.ts index 880b263e4..4a87c2500 100644 --- a/packages/devframe/tsdown.config.ts +++ b/packages/devframe/tsdown.config.ts @@ -116,13 +116,14 @@ const serverEntries = { 'adapters/build': 'src/adapters/build.ts', 'adapters/embedded': 'src/adapters/embedded.ts', 'adapters/initiate': 'src/adapters/initiate.ts', + 'adapters/mcp': 'src/adapters/mcp.ts', 'cli/main': 'src/cli/main.ts', 'recipes/common-rpc-functions': 'src/recipes/common-rpc-functions.ts', 'recipes/interactive-auth': 'src/recipes/interactive-auth.ts', } /** - * Four configs: + * Three configs: * * 1. Runtime client/agnostic build (`dts: false`). Independent rolldown * chunk graph so server-only imports like `devframe/rpc/transports/ws-server` @@ -136,11 +137,6 @@ const serverEntries = { * `src/types/rpc-augments.ts`, produce exactly one declaration site. * This is what lets consumer `declare module 'devframe'` augmentations * propagate across every import chain. - * 4. The `adapters/mcp` alias, alone. Its top-level `await` (lazy-loading - * `@devframes/agentic/mcp` at module evaluation) would otherwise reshape - * the server graph's chunking - every sibling entry turns into async-safe - * re-export facades. An isolated graph keeps the TLA contained; the few - * helpers it inlines are duplicated only here. */ export default defineConfig([ { @@ -195,14 +191,6 @@ export default defineConfig([ deps, dts: { emitDtsOnly: true }, outExtensions: () => ({ dts: '.d.mts' }), - entry: { ...clientEntries, ...serverEntries, 'adapters/mcp': 'src/adapters/mcp.ts' }, - }, - { - clean: false, - platform: 'node', - tsconfig, - deps: nodeDeps, - dts: false, - entry: { 'adapters/mcp': 'src/adapters/mcp.ts' }, + entry: { ...clientEntries, ...serverEntries }, }, ]) diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts index c7f9c3fa2..70b707435 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts @@ -1,10 +1,10 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Variables -export declare const createMcpFetchHandler: (ctx: DevframeNodeContext, options: CreateMcpFetchHandlerOptions) => McpFetchHandler; -export declare const createMcpServer: (definition: DevframeDefinition, options?: CreateMcpServerOptions) => Promise; -export declare const mountMcpHttp: (app: import("h3").H3, ctx: DevframeNodeContext, path: string, options: MountMcpHttpOptions) => MountedMcpHttp; +// #region Functions +export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions): Promise; +export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions): Promise; +export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions): Promise; // #endregion // #region Other diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js index c0bfa5916..754209d2c 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js @@ -1,8 +1,8 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Variables -export var createMcpFetchHandler /* const */ -export var createMcpServer /* const */ -export var mountMcpHttp /* const */ +// #region Functions +export async function createMcpFetchHandler(_, _) {} +export async function createMcpServer(_, _) {} +export async function mountMcpHttp(_, _, _, _) {} // #endregion \ No newline at end of file diff --git a/tests/optional-mcp-bundles.test.ts b/tests/optional-mcp-bundles.test.ts index 3959e821e..9a49a0aa8 100644 --- a/tests/optional-mcp-bundles.test.ts +++ b/tests/optional-mcp-bundles.test.ts @@ -9,6 +9,7 @@ const root = fileURLToPath(new URL('..', import.meta.url)) const entries = [ 'packages/devframe/dist/adapters/cac.mjs', 'packages/devframe/dist/adapters/initiate.mjs', + 'packages/devframe/dist/adapters/mcp.mjs', 'packages/hub/dist/node/initiate.mjs', 'packages/next/dist/hub.mjs', ] From 436a4b262673522dd83a4bee3a014f195033115f Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 14 Sep 2026 23:51:27 +0000 Subject: [PATCH 4/6] refactor: make devframe/adapters/mcp a static re-export of the agentic peer Replaces the per-function lazy import with a plain re-export of @devframes/agentic/mcp, keeping the implementation's synchronous signatures. Importing the entry without the peer now throws module-not-found, matching the devframe/adapters/cac precedent for optional peers; first-party adapters keep the coded DF0078/DF0079 lazy-load path. --- docs/content/2.adapters/7.mcp.md | 2 +- docs/content/6.errors/DF0079.md | 2 +- docs/content/7.migrations/1.migration-0.10.md | 11 +--- knip.jsonc | 15 +----- packages/devframe/src/adapters/mcp.ts | 50 +++---------------- .../devframe/adapters/mcp.snapshot.d.ts | 9 ++-- .../tsnapi/devframe/adapters/mcp.snapshot.js | 8 +-- tests/optional-mcp-bundles.test.ts | 5 +- 8 files changed, 22 insertions(+), 80 deletions(-) diff --git a/docs/content/2.adapters/7.mcp.md b/docs/content/2.adapters/7.mcp.md index 1fc7ec806..b4e6ed9e0 100644 --- a/docs/content/2.adapters/7.mcp.md +++ b/docs/content/2.adapters/7.mcp.md @@ -20,7 +20,7 @@ import myDevframe from './my-tool' await createMcpServer(myDevframe, { transport: 'stdio' }) ``` -`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection. Importing `devframe/adapters/mcp` without the peer installed throws [DF0079](/errors/DF0079). +`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection. Importing `devframe/adapters/mcp` without the peer installed throws the usual module-not-found error, exactly like `devframe/adapters/cac` with its optional `cac` peer. ## Route-based server diff --git a/docs/content/6.errors/DF0079.md b/docs/content/6.errors/DF0079.md index a4305e389..21ff658ac 100644 --- a/docs/content/6.errors/DF0079.md +++ b/docs/content/6.errors/DF0079.md @@ -9,7 +9,7 @@ description: 'The mcp option is enabled, but the optional peer "@devframes/agent ## Cause -An explicit `mcp` setting (`true`, a route options object, the `--mcp` flag, or the `mcp` CLI subcommand) asked for an MCP surface - or a `devframe/adapters/mcp` function was called - but the implementation could not be loaded from the optional `@devframes/agentic` peer, typically because it is not installed. Unlike the omitted `'auto'` default (which degrades to a one-time [DF0078](/errors/DF0078) warning), an explicit opt-in fails fast rather than silently running without MCP. +An explicit `mcp` setting (`true`, a route options object, the `--mcp` flag, or the `mcp` CLI subcommand) asked for an MCP surface, but the implementation could not be loaded from the optional `@devframes/agentic` peer, typically because it is not installed. Unlike the omitted `'auto'` default (which degrades to a one-time [DF0078](/errors/DF0078) warning), an explicit opt-in fails fast rather than silently running without MCP. ## Fix diff --git a/docs/content/7.migrations/1.migration-0.10.md b/docs/content/7.migrations/1.migration-0.10.md index e76a6ccda..4ef2aca3a 100644 --- a/docs/content/7.migrations/1.migration-0.10.md +++ b/docs/content/7.migrations/1.migration-0.10.md @@ -21,16 +21,9 @@ Every surface reacts to the missing peer the same way: | `true` / route options object | always mounts | throws [DF0079](/errors/DF0079) | | `false` | never mounts, never probes | same | -Calling a `devframe/adapters/mcp` function without the peer also throws [DF0079](/errors/DF0079); the import itself stays side-effect-free and loads no MCP code. This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits. +This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits. -The exports keep their names and option types (also importable from `devframe/types`): `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`. The latter two are now **async** - they await the lazy peer load - so add an `await` where you called them synchronously: - -```diff -- const handler = createMcpFetchHandler(ctx, options) -+ const handler = await createMcpFetchHandler(ctx, options) -``` - -The ` mcp` stdio subcommand keeps working with the peer installed. +The exports are unchanged - `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`, and their option types (also importable from `devframe/types`). Importing `devframe/adapters/mcp` without the peer installed throws the usual module-not-found error, exactly like `devframe/adapters/cac` with its optional `cac` peer. The ` mcp` stdio subcommand keeps working with the peer installed. ## `devframe connect` requires `@devframes/agentic` diff --git a/knip.jsonc b/knip.jsonc index 27db3ad69..44782507e 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -71,11 +71,7 @@ // `import()` carrying `webpackIgnore`/`turbopackIgnore` comments - // deliberately invisible to bundlers (and knip) so Next never inlines // their node-only code. They're genuinely used at runtime. - // `@devframes/agentic` is install-only: devframe probes and loads it - // through `importRuntimeModule` string specifiers to serve the hub's - // aggregate MCP endpoint; it is never statically imported. "ignoreDependencies": [ - "@devframes/agentic", "@devframes/plugin-a11y", "@devframes/plugin-code-server", "@devframes/plugin-git", @@ -107,10 +103,7 @@ // dock's `action.importFrom` bare specifier, resolved by Vite through // the hub's `clientModuleResolution` template (`/@id/{specifier}`), // never a static import knip's graph can see. - // `@devframes/agentic` is install-only: devframe probes and loads it - // through `importRuntimeModule` string specifiers to serve the hub's - // aggregate MCP endpoint; it is never statically imported. - "ignoreDependencies": ["@devframes/agentic", "demo-dock-client"] + "ignoreDependencies": ["demo-dock-client"] }, // Multi-entry example package (see `packages/devframe` above for why // exports-mapped entries are listed explicitly): `src/index.ts` is the @@ -133,12 +126,6 @@ "entry": ["src/server.ts"] }, "packages/devframe": { - // `@devframes/agentic` (devframe's own optional peer) is loaded only - // through `importRuntimeModule('@devframes/agentic/...')` string - // specifiers and a `require.resolve` probe (`src/node/agentic.ts`), - // never a static import knip's graph can see; the devDependency is - // what lets tests exercise the installed-peer path. - "ignoreDependencies": ["@devframes/agentic"], // Every `package.json#exports` subpath maps to one of these source // files (see `tsdown.config.ts`'s `serverEntries`/`clientEntries`). // Knip's package.json→dist→src source mapping needs `outDir` set on diff --git a/packages/devframe/src/adapters/mcp.ts b/packages/devframe/src/adapters/mcp.ts index 28d7cec8e..437f5b843 100644 --- a/packages/devframe/src/adapters/mcp.ts +++ b/packages/devframe/src/adapters/mcp.ts @@ -1,18 +1,8 @@ -// The user-facing MCP adapter entry. The implementation (and the MCP SDK) -// lives in the optional `@devframes/agentic` peer, which is never imported -// directly: each function lazy-loads it on first call (throwing a coded -// DF0079 when the peer is not installed), so importing this entry stays -// side-effect-free and loads no MCP code. Signatures are typed against -// devframe's own contract in `types/mcp.ts`; `createMcpFetchHandler` and -// `mountMcpHttp` are async here (they await the lazy load), unlike the -// synchronous implementations behind them. -import type { H3 } from 'h3' -import type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic' -import type { DevframeNodeContext } from '../types/context' -import type { DevframeDefinition } from '../types/devframe' -import type { CreateMcpFetchHandlerOptions, CreateMcpServerOptions, McpFetchHandler, McpServerHandle } from '../types/mcp' -import { importAgenticMcp } from '../node/agentic' - +// The user-facing MCP adapter entry: a re-export of the implementation in +// the optional `@devframes/agentic` peer (which also carries the MCP SDK). +// Like `devframe/adapters/cac` with its optional `cac` peer, importing it +// without the peer throws module-not-found. First-party adapters lazy-load +// the peer through `node/agentic.ts` (DF0078/DF0079) instead. export type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic' export type { CreateMcpFetchHandlerOptions, @@ -21,32 +11,4 @@ export type { McpFetchHandler, McpServerHandle, } from '../types/mcp' - -/** Build an MCP server over the agent surface of a devframe definition (stdio). */ -export async function createMcpServer( - definition: DevframeDefinition, - options?: CreateMcpServerOptions, -): Promise { - const mcp = await importAgenticMcp() - return mcp.createMcpServer(definition, options) -} - -/** Build a framework-agnostic `Request → Response` MCP endpoint over a devframe context. */ -export async function createMcpFetchHandler( - ctx: DevframeNodeContext, - options: CreateMcpFetchHandlerOptions, -): Promise { - const mcp = await importAgenticMcp() - return mcp.createMcpFetchHandler(ctx, options) -} - -/** Mount a stateless MCP endpoint on an h3 app at `path`. */ -export async function mountMcpHttp( - app: H3, - ctx: DevframeNodeContext, - path: string, - options: MountMcpHttpOptions, -): Promise { - const mcp = await importAgenticMcp() - return mcp.mountMcpHttp(app, ctx, path, options) -} +export { createMcpFetchHandler, createMcpServer, mountMcpHttp } from '@devframes/agentic/mcp' diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts index 70b707435..6ab424823 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts @@ -1,18 +1,15 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Functions -export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions): Promise; -export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions): Promise; -export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions): Promise; -// #endregion - // #region Other +export { createMcpFetchHandler } export { CreateMcpFetchHandlerOptions } +export { createMcpServer } export { CreateMcpServerOptions } export { McpConnectionInfo } export { McpFetchHandler } export { McpServerHandle } export { MountedMcpHttp } +export { mountMcpHttp } export { MountMcpHttpOptions } // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js index 754209d2c..6ad22c4cd 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js @@ -1,8 +1,8 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Functions -export async function createMcpFetchHandler(_, _) {} -export async function createMcpServer(_, _) {} -export async function mountMcpHttp(_, _, _, _) {} +// #region Other +export { createMcpFetchHandler } +export { createMcpServer } +export { mountMcpHttp } // #endregion \ No newline at end of file diff --git a/tests/optional-mcp-bundles.test.ts b/tests/optional-mcp-bundles.test.ts index 9a49a0aa8..ba39241c1 100644 --- a/tests/optional-mcp-bundles.test.ts +++ b/tests/optional-mcp-bundles.test.ts @@ -6,10 +6,13 @@ import { build } from 'esbuild' import { afterEach, describe, expect, it } from 'vitest' const root = fileURLToPath(new URL('..', import.meta.url)) +// `packages/devframe/dist/adapters/mcp.mjs` is deliberately absent: that +// entry is a static re-export of `@devframes/agentic/mcp` (like +// `adapters/cac` with its optional `cac` peer), so it resolves the peer by +// design; only implicit paths must stay lazy. const entries = [ 'packages/devframe/dist/adapters/cac.mjs', 'packages/devframe/dist/adapters/initiate.mjs', - 'packages/devframe/dist/adapters/mcp.mjs', 'packages/hub/dist/node/initiate.mjs', 'packages/next/dist/hub.mjs', ] From 1f900c5909c2a0d1cea953537fc9d2aa29df128e Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 15 Sep 2026 09:24:20 +0900 Subject: [PATCH 5/6] chore: update --- docs/content/2.adapters/7.mcp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/2.adapters/7.mcp.md b/docs/content/2.adapters/7.mcp.md index b4e6ed9e0..b45adf132 100644 --- a/docs/content/2.adapters/7.mcp.md +++ b/docs/content/2.adapters/7.mcp.md @@ -20,7 +20,7 @@ import myDevframe from './my-tool' await createMcpServer(myDevframe, { transport: 'stdio' }) ``` -`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection. Importing `devframe/adapters/mcp` without the peer installed throws the usual module-not-found error, exactly like `devframe/adapters/cac` with its optional `cac` peer. +`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection. ## Route-based server From 496b769a6ac1c106546bc0210d50da3af5d24579 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Tue, 15 Sep 2026 09:25:50 +0900 Subject: [PATCH 6/6] chore: update --- packages/devframe/src/node/services-install.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devframe/src/node/services-install.ts b/packages/devframe/src/node/services-install.ts index 83fa5154d..e69aef570 100644 --- a/packages/devframe/src/node/services-install.ts +++ b/packages/devframe/src/node/services-install.ts @@ -51,9 +51,9 @@ export function expandResolveFrom(resolveFrom: string, cwd: string): string | un */ export function resolveServicePackage( pkg: string, - resolveFroms: readonly (string | null | undefined)[], + resolveFrom: readonly (string | null | undefined)[], ): string | undefined { - for (const from of resolveFroms) { + for (const from of resolveFrom) { if (typeof from !== 'string' || from.length === 0) continue try {