Skip to content

Commit 436a4b2

Browse files
committed
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.
1 parent 490fc36 commit 436a4b2

8 files changed

Lines changed: 22 additions & 80 deletions

File tree

‎docs/content/2.adapters/7.mcp.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import myDevframe from './my-tool'
2020
await createMcpServer(myDevframe, { transport: 'stdio' })
2121
```
2222

23-
`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).
23+
`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.
2424

2525
## Route-based server
2626

‎docs/content/6.errors/DF0079.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ description: 'The mcp option is enabled, but the optional peer "@devframes/agent
99
1010
## Cause
1111

12-
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.
12+
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.
1313

1414
## Fix
1515

‎docs/content/7.migrations/1.migration-0.10.md‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,9 @@ Every surface reacts to the missing peer the same way:
2121
| `true` / route options object | always mounts | throws [DF0079](/errors/DF0079) |
2222
| `false` | never mounts, never probes | same |
2323

24-
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.
24+
This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits.
2525

26-
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:
27-
28-
```diff
29-
- const handler = createMcpFetchHandler(ctx, options)
30-
+ const handler = await createMcpFetchHandler(ctx, options)
31-
```
32-
33-
The `<your-app> mcp` stdio subcommand keeps working with the peer installed.
26+
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 `<your-app> mcp` stdio subcommand keeps working with the peer installed.
3427

3528
## `devframe connect` requires `@devframes/agentic`
3629

‎knip.jsonc‎

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,7 @@
7171
// `import()` carrying `webpackIgnore`/`turbopackIgnore` comments -
7272
// deliberately invisible to bundlers (and knip) so Next never inlines
7373
// their node-only code. They're genuinely used at runtime.
74-
// `@devframes/agentic` is install-only: devframe probes and loads it
75-
// through `importRuntimeModule` string specifiers to serve the hub's
76-
// aggregate MCP endpoint; it is never statically imported.
7774
"ignoreDependencies": [
78-
"@devframes/agentic",
7975
"@devframes/plugin-a11y",
8076
"@devframes/plugin-code-server",
8177
"@devframes/plugin-git",
@@ -107,10 +103,7 @@
107103
// dock's `action.importFrom` bare specifier, resolved by Vite through
108104
// the hub's `clientModuleResolution` template (`/@id/{specifier}`),
109105
// never a static import knip's graph can see.
110-
// `@devframes/agentic` is install-only: devframe probes and loads it
111-
// through `importRuntimeModule` string specifiers to serve the hub's
112-
// aggregate MCP endpoint; it is never statically imported.
113-
"ignoreDependencies": ["@devframes/agentic", "demo-dock-client"]
106+
"ignoreDependencies": ["demo-dock-client"]
114107
},
115108
// Multi-entry example package (see `packages/devframe` above for why
116109
// exports-mapped entries are listed explicitly): `src/index.ts` is the
@@ -133,12 +126,6 @@
133126
"entry": ["src/server.ts"]
134127
},
135128
"packages/devframe": {
136-
// `@devframes/agentic` (devframe's own optional peer) is loaded only
137-
// through `importRuntimeModule('@devframes/agentic/...')` string
138-
// specifiers and a `require.resolve` probe (`src/node/agentic.ts`),
139-
// never a static import knip's graph can see; the devDependency is
140-
// what lets tests exercise the installed-peer path.
141-
"ignoreDependencies": ["@devframes/agentic"],
142129
// Every `package.json#exports` subpath maps to one of these source
143130
// files (see `tsdown.config.ts`'s `serverEntries`/`clientEntries`).
144131
// Knip's package.json→dist→src source mapping needs `outDir` set on
Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
1-
// The user-facing MCP adapter entry. The implementation (and the MCP SDK)
2-
// lives in the optional `@devframes/agentic` peer, which is never imported
3-
// directly: each function lazy-loads it on first call (throwing a coded
4-
// DF0079 when the peer is not installed), so importing this entry stays
5-
// side-effect-free and loads no MCP code. Signatures are typed against
6-
// devframe's own contract in `types/mcp.ts`; `createMcpFetchHandler` and
7-
// `mountMcpHttp` are async here (they await the lazy load), unlike the
8-
// synchronous implementations behind them.
9-
import type { H3 } from 'h3'
10-
import type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic'
11-
import type { DevframeNodeContext } from '../types/context'
12-
import type { DevframeDefinition } from '../types/devframe'
13-
import type { CreateMcpFetchHandlerOptions, CreateMcpServerOptions, McpFetchHandler, McpServerHandle } from '../types/mcp'
14-
import { importAgenticMcp } from '../node/agentic'
15-
1+
// The user-facing MCP adapter entry: a re-export of the implementation in
2+
// the optional `@devframes/agentic` peer (which also carries the MCP SDK).
3+
// Like `devframe/adapters/cac` with its optional `cac` peer, importing it
4+
// without the peer throws module-not-found. First-party adapters lazy-load
5+
// the peer through `node/agentic.ts` (DF0078/DF0079) instead.
166
export type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic'
177
export type {
188
CreateMcpFetchHandlerOptions,
@@ -21,32 +11,4 @@ export type {
2111
McpFetchHandler,
2212
McpServerHandle,
2313
} from '../types/mcp'
24-
25-
/** Build an MCP server over the agent surface of a devframe definition (stdio). */
26-
export async function createMcpServer(
27-
definition: DevframeDefinition,
28-
options?: CreateMcpServerOptions,
29-
): Promise<McpServerHandle> {
30-
const mcp = await importAgenticMcp()
31-
return mcp.createMcpServer(definition, options)
32-
}
33-
34-
/** Build a framework-agnostic `Request → Response` MCP endpoint over a devframe context. */
35-
export async function createMcpFetchHandler(
36-
ctx: DevframeNodeContext,
37-
options: CreateMcpFetchHandlerOptions,
38-
): Promise<McpFetchHandler> {
39-
const mcp = await importAgenticMcp()
40-
return mcp.createMcpFetchHandler(ctx, options)
41-
}
42-
43-
/** Mount a stateless MCP endpoint on an h3 app at `path`. */
44-
export async function mountMcpHttp(
45-
app: H3,
46-
ctx: DevframeNodeContext,
47-
path: string,
48-
options: MountMcpHttpOptions,
49-
): Promise<MountedMcpHttp> {
50-
const mcp = await importAgenticMcp()
51-
return mcp.mountMcpHttp(app, ctx, path, options)
52-
}
14+
export { createMcpFetchHandler, createMcpServer, mountMcpHttp } from '@devframes/agentic/mcp'
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
/**
22
* Generated by tsnapi — public API snapshot of `devframe/adapters/mcp`
33
*/
4-
// #region Functions
5-
export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions): Promise<McpFetchHandler>;
6-
export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions): Promise<McpServerHandle>;
7-
export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions): Promise<MountedMcpHttp>;
8-
// #endregion
9-
104
// #region Other
5+
export { createMcpFetchHandler }
116
export { CreateMcpFetchHandlerOptions }
7+
export { createMcpServer }
128
export { CreateMcpServerOptions }
139
export { McpConnectionInfo }
1410
export { McpFetchHandler }
1511
export { McpServerHandle }
1612
export { MountedMcpHttp }
13+
export { mountMcpHttp }
1714
export { MountMcpHttpOptions }
1815
// #endregion
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
22
* Generated by tsnapi — public API snapshot of `devframe/adapters/mcp`
33
*/
4-
// #region Functions
5-
export async function createMcpFetchHandler(_, _) {}
6-
export async function createMcpServer(_, _) {}
7-
export async function mountMcpHttp(_, _, _, _) {}
4+
// #region Other
5+
export { createMcpFetchHandler }
6+
export { createMcpServer }
7+
export { mountMcpHttp }
88
// #endregion

‎tests/optional-mcp-bundles.test.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import { build } from 'esbuild'
66
import { afterEach, describe, expect, it } from 'vitest'
77

88
const root = fileURLToPath(new URL('..', import.meta.url))
9+
// `packages/devframe/dist/adapters/mcp.mjs` is deliberately absent: that
10+
// entry is a static re-export of `@devframes/agentic/mcp` (like
11+
// `adapters/cac` with its optional `cac` peer), so it resolves the peer by
12+
// design; only implicit paths must stay lazy.
913
const entries = [
1014
'packages/devframe/dist/adapters/cac.mjs',
1115
'packages/devframe/dist/adapters/initiate.mjs',
12-
'packages/devframe/dist/adapters/mcp.mjs',
1316
'packages/hub/dist/node/initiate.mjs',
1417
'packages/next/dist/hub.mjs',
1518
]

0 commit comments

Comments
 (0)