Skip to content

Commit 423293a

Browse files
authored
fix: keep optional MCP imports out of bundles (#285)
1 parent 5f8a1d6 commit 423293a

13 files changed

Lines changed: 147 additions & 9 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"@unocss/reset": "catalog:frontend",
4949
"bumpp": "catalog:tooling",
5050
"crossws": "catalog:deps",
51+
"esbuild": "catalog:build",
5152
"eslint": "catalog:tooling",
5253
"h3": "catalog:deps",
5354
"knip": "catalog:tooling",

‎packages/devframe/src/adapters/cac.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { DevframeDefinition } from '../types/devframe'
1212
import process from 'node:process'
1313
import cac from 'cac'
1414
import { colors as c } from 'devframe/utils/colors'
15+
import { importRuntimeModule } from '../node/import-runtime-module'
1516
import { createBuild } from './build'
1617
import { createDevServer, resolveDevServerPort } from './dev'
1718
import { flagKeyToOption, isBooleanFlag, parseCliFlags } from './flags'
@@ -124,7 +125,7 @@ export function createCac(d: DevframeDefinition, options: CreateCacOptions = {})
124125
// MCP clients expect JSON-RPC on stdout — route welcome/logging
125126
// noise out of the way. Logs-SDK diagnostics land on stderr by
126127
// default, so nothing extra needed beyond not printing here.
127-
const { createMcpServer } = await import('./mcp')
128+
const { createMcpServer } = await importRuntimeModule<typeof import('./mcp')>('devframe/adapters/mcp')
128129
await createMcpServer(d, {
129130
transport: 'stdio',
130131
// Deliberately go to stderr: stdout is the MCP transport.

‎packages/devframe/src/adapters/initiate.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { resolveClientAssets } from '../define'
1919
import { createHostContext } from '../node/context'
2020
import { diagnostics } from '../node/diagnostics'
2121
import { createH3DevframeHost } from '../node/host-h3'
22+
import { importRuntimeModule } from '../node/import-runtime-module'
2223
import { createInstanceShell, resolveInstanceRegister } from '../node/instance-shell'
2324
import { normalizeBasePath } from './_shared'
2425
import { resolveDevServerPort, resolveMcpConnectionMeta } from './dev'
@@ -311,7 +312,7 @@ export function initDevframe(
311312
const mcpPath = joinURL(base, mcpMeta.path)
312313
let mountMcpHttp: typeof import('./mcp/http').mountMcpHttp
313314
try {
314-
;({ mountMcpHttp } = await import('./mcp/http'))
315+
;({ mountMcpHttp } = await importRuntimeModule<typeof import('./mcp')>('devframe/adapters/mcp'))
315316
}
316317
catch (error) {
317318
const reason = error instanceof Error ? error.message : String(error)

‎packages/devframe/src/internal/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export type { AgentArgsFallback } from '../node/agent-args'
3838
export { diagnostics } from '../node/diagnostics'
3939
export { DevframeAgentHost } from '../node/host-agent'
4040
export * from '../node/host-h3'
41+
export { importRuntimeModule } from '../node/import-runtime-module'
4142
export { listLiveDevframeInstances, registerDevframeInstance } from '../node/instance-registry'
4243
export type { DevframeInstanceRecord, DevframeInstanceRegistration } from '../node/instance-registry'
4344
export { createInstanceShell, resolveInstanceRegister, samePath } from '../node/instance-shell'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { importServicePackage } from './services-install'
2+
3+
/**
4+
* Resolve and import a package at runtime without adding it to a consumer's
5+
* bundle graph. First-party adapters use this for optional peers whose code
6+
* is needed only when the matching feature is enabled.
7+
*
8+
* @internal
9+
*/
10+
export async function importRuntimeModule<T = unknown>(specifier: string): Promise<T> {
11+
return await importServicePackage(specifier, [import.meta.url]) as T
12+
}

‎packages/hub/src/node/initiate.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { existsSync } from 'node:fs'
1212
import { readFile } from 'node:fs/promises'
1313
import process from 'node:process'
1414
import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_MCP_ROUTE, DEVFRAME_WS_ROUTE } from 'devframe/constants'
15-
import { createH3DevframeHost, createInstanceShell, resolveInstanceRegister } from 'devframe/internal'
15+
import { createH3DevframeHost, createInstanceShell, importRuntimeModule, resolveInstanceRegister } from 'devframe/internal'
1616
import { mountStaticHandler } from 'devframe/utils/serve-static'
1717
import { H3 } from 'h3'
1818
import { resolve } from 'pathe'
@@ -571,7 +571,7 @@ export function initHub(options: InitHubOptions): HubInstance {
571571
return { context: ctx }
572572

573573
const mcpRoute = withoutLeadingSlash(mcpConfig.path ?? DEVFRAME_MCP_ROUTE)
574-
const { mountMcpHttp } = await import('devframe/adapters/mcp')
574+
const { mountMcpHttp } = await importRuntimeModule<typeof import('devframe/adapters/mcp')>('devframe/adapters/mcp')
575575
const mounted = mountMcpHttp(app, ctx, joinURL(base, mcpRoute), {
576576
serverName: options.name ?? 'devframes-hub',
577577
serverVersion: options.version ?? '0.0.0',

‎packages/next/src/host.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ConnectionMeta, DevframeHost, DevframeNodeContext, DevframeStorageScope } from 'devframe'
22
import { DEVFRAME_CONNECTION_META_FILENAME } from 'devframe/constants'
3+
import { importRuntimeModule } from 'devframe/internal'
34
import { serveStaticHandler } from 'devframe/utils/serve-static'
45
import { H3 } from 'h3'
56

@@ -163,7 +164,7 @@ export function createDevframeNextHost(
163164
connectionMeta = meta
164165
},
165166
async mountMcp(ctx, path, mcpOptions = {}) {
166-
const { createMcpFetchHandler } = await import('devframe/adapters/mcp')
167+
const { createMcpFetchHandler } = await importRuntimeModule<typeof import('devframe/adapters/mcp')>('devframe/adapters/mcp')
167168
const handler = createMcpFetchHandler(ctx, {
168169
serverName: mcpOptions.serverName ?? 'devframe (next)',
169170
serverVersion: mcpOptions.serverVersion ?? '0.0.0',

‎pnpm-lock.yaml‎

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pnpm-workspace.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ catalogs:
9090
'@sveltejs/adapter-node': ^5.3.3
9191
'@sveltejs/kit': ^2.46.4
9292
'@vitejs/plugin-vue': ^6.0.8
93+
esbuild: ^0.28.0
9394
lightningcss: ^1.33.0
9495
magic-string: ^1.2.0
9596
mlly: ^1.8.2
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 Other
5-
export { createMcpFetchHandler }
6-
export { createMcpServer }
7-
export { mountMcpHttp }
4+
// #region Functions
5+
export function createMcpFetchHandler(_, _) {}
6+
export async function createMcpServer(_, _) {}
7+
export function mountMcpHttp(_, _, _, _) {}
88
// #endregion

0 commit comments

Comments
 (0)