Skip to content

Commit 15c15da

Browse files
committed
feat(devframe): ship @modelcontextprotocol/server as a regular dependency
1 parent ff88685 commit 15c15da

30 files changed

Lines changed: 71 additions & 155 deletions

File tree

‎docs/content/1.guide/15.agent-native.md‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ Every `ctx.rpc.sharedState` key is exposed as a `devframe://state/<key>` resourc
100100

101101
## Starting the MCP server
102102

103-
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 `@modelcontextprotocol/server` is installed - 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.
103+
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.
104104

105105
For a stdio server instead, via the CLI:
106106

@@ -120,8 +120,6 @@ const myDevframe = defineDevframe({ /* … */ })
120120
await createMcpServer(myDevframe, { transport: 'stdio' })
121121
```
122122

123-
`@modelcontextprotocol/server` is a peer dependency.
124-
125123
## Connecting Claude Desktop
126124

127125
In `claude_desktop_config.json`:

‎docs/content/1.guide/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ The mounted devframes share one RPC registry, state store, connection, auth gate
114114
pnpm add devframe
115115
```
116116

117-
`devframe` ships ESM-only, no Vite dependency. Adapters with optional peers (the MCP adapter needs `@modelcontextprotocol/server`) surface the requirement at import time.
117+
`devframe` ships ESM-only, no Vite dependency. The CLI adapter's optional peer (`cac`) surfaces its requirement at import time.
118118

119119
## Hello, Devframe
120120

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import myDevframe from './my-tool'
1414
await createMcpServer(myDevframe, { transport: 'stdio' })
1515
```
1616

17-
`@modelcontextprotocol/server` is a peer dependency; `createMcpServer` serves `stdio` through the SDK's `serveStdio`, pinning one server instance per connection.
17+
`createMcpServer` serves `stdio` through the MCP SDK's `serveStdio`, pinning one server instance per connection.
1818

1919
## Route-based server
2020

21-
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 `@modelcontextprotocol/server` is installed - flag your first function and the agent view is on. A devframe with nothing flagged mounts no route and loads no MCP code; a flagged surface whose peer is missing warns once ([`DF0077`](/errors/DF0077)) so you know what to install.
21+
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.
2222

23-
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 (a missing peer becomes a startup failure, [`DF0017`](/errors/DF0017)), `false` never mounts, an object customises the route:
23+
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:
2424

2525
```ts
2626
import { createCac } from 'devframe/adapters/cac'

‎docs/content/2.adapters/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: 'The lowest-level path is the standard handler, initDevframe(def, {
77

88
The lowest-level path is [the standard handler](/adapters/initiate), `initDevframe(def, { base })`: a Web Standard `(request: Request) => Promise<Response>` for any catch-all route. Every path below builds on it.
99

10-
Adapters wrap it as `createXxx(def, options?)` at `devframe/adapters/<name>`. `cac` and `mcp` need an optional peer ([`cac`](https://github.com/cacjs/cac), [`@modelcontextprotocol/server`](https://github.com/modelcontextprotocol/typescript-sdk)).
10+
Adapters wrap it as `createXxx(def, options?)` at `devframe/adapters/<name>`. `cac` needs an optional peer ([`cac`](https://github.com/cacjs/cac)).
1111

1212
## Comparison
1313

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ description: 'Failed to start MCP server ({transport}): {reason}'
1111

1212
The MCP server failed while initializing. Common reasons:
1313

14-
- The `@modelcontextprotocol/server` peer dependency is missing (the stdio and route-based transports both need it).
1514
- The stdio transport threw during `connect()` (e.g. stdin/stdout unavailable).
15+
- The MCP adapter module could not be loaded (e.g. a corrupted install missing `@modelcontextprotocol/server`).
1616

1717
## Fix
1818

19-
- **Missing SDK**: `pnpm add @modelcontextprotocol/server` in the package that imports `devframe/adapters/mcp` or enables the `mcp` setting.
2019
- **Transport failure**: inspect the underlying error attached as `cause`.
20+
- **Broken install**: reinstall dependencies so `@modelcontextprotocol/server` (a dependency of `devframe`) resolves.
2121

2222
## Source
2323

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
---
22
title: 'DF0046: Connector Requires the MCP SDK'
3-
description: 'devframe connect requires the optional peer dependency @modelcontextprotocol/server: {reason}'
3+
description: 'devframe connect requires the optional peer dependency @modelcontextprotocol/client: {reason}'
44
---
55

66
## Message
77

8-
> `devframe connect` requires the optional peer dependency @modelcontextprotocol/server: `{reason}`
8+
> `devframe connect` requires the optional peer dependency @modelcontextprotocol/client: `{reason}`
99
1010
## Cause
1111

12-
`devframe connect` was started but `@modelcontextprotocol/server` could not be imported. The SDK is an optional peer dependency of `devframe`, so it only needs to be installed where MCP features are used.
12+
`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.
1313

1414
## Fix
1515

1616
Install the SDK next to devframe and run the connector again:
1717

1818
```sh
19-
npm install @modelcontextprotocol/server
19+
npm install @modelcontextprotocol/client
2020
devframe connect
2121
```
2222

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

Lines changed: 0 additions & 35 deletions
This file was deleted.

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ Emitted by `devframe`: the framework-neutral host, RPC, streaming, assets, servi
8383
| [DF0074](/errors/DF0074) | error | JSON-Render Schema Is Asynchronous |
8484
| [DF0075](/errors/DF0075) | warn | No RPC Transport On This Runtime |
8585
| [DF0076](/errors/DF0076) | error | WebSocket Upgrade Unsupported On This Runtime |
86-
| [DF0077](/errors/DF0077) | warn | Agent Surface Without the MCP Peer |
8786

8887
## Hub: context & lifecycle (DF80xx)
8988

‎packages/devframe/package.json‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,21 +83,18 @@
8383
},
8484
"peerDependencies": {
8585
"@modelcontextprotocol/client": "^2.0.0",
86-
"@modelcontextprotocol/server": "^2.0.0",
8786
"cac": "^7.0.0"
8887
},
8988
"peerDependenciesMeta": {
9089
"@modelcontextprotocol/client": {
9190
"optional": true
9291
},
93-
"@modelcontextprotocol/server": {
94-
"optional": true
95-
},
9692
"cac": {
9793
"optional": true
9894
}
9995
},
10096
"dependencies": {
97+
"@modelcontextprotocol/server": "catalog:deps",
10198
"@standard-schema/spec": "catalog:deps",
10299
"birpc": "catalog:deps",
103100
"crossws": "catalog:deps",
@@ -109,7 +106,6 @@
109106
},
110107
"devDependencies": {
111108
"@modelcontextprotocol/client": "catalog:deps",
112-
"@modelcontextprotocol/server": "catalog:deps",
113109
"cac": "catalog:deps",
114110
"get-port-please": "catalog:deps",
115111
"immer": "catalog:deps",

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

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { DevframeDefinition, DevframeDeploymentKind, McpAuthorization, McpS
44
import { getPort } from 'get-port-please'
55
import { cleanDoubleSlashes, withLeadingSlash, withoutLeadingSlash, withTrailingSlash } from 'ufo'
66
import { DEVFRAME_MCP_ROUTE } from '../constants'
7-
import { diagnostics } from '../node/diagnostics'
87
import { importRuntimeModule } from '../node/import-runtime-module'
98

109
const DEFAULT_PORT = 9999
@@ -100,38 +99,23 @@ export function resolveMcpConfig(mcp: McpSetting | undefined): ResolvedMcpConfig
10099
}
101100
}
102101

103-
const autoMcpHinted = new Set<string>()
104-
105102
/**
106103
* Resolve the `mcp: 'auto'` default at mount time: import the MCP adapter
107104
* when the devframe's agent surface is non-empty, or return `undefined`
108105
* (mount nothing) when the surface is empty - the zero-cost path, loading
109-
* no MCP code at all. A non-empty surface whose optional peer
110-
* (`@modelcontextprotocol/server`) fails to import also returns `undefined`,
111-
* warning `DF0077` once per instance id so the author discovers the agent
112-
* view exists.
106+
* no MCP code at all. The adapter (and the MCP SDK behind it) loads through
107+
* `importRuntimeModule`, so it never enters a consumer's bundle graph.
113108
*
114109
* Generic like `importRuntimeModule`: the caller names the module type
115110
* (`typeof import('devframe/adapters/mcp')`) so this shared helper carries
116111
* no type-level dependency on the MCP adapter.
117112
*/
118113
export async function loadAutoMcpAdapter<T>(
119114
agent: Pick<DevframeAgentHost, 'hasSurface'>,
120-
id: string,
121115
): Promise<T | undefined> {
122116
if (!agent.hasSurface())
123117
return undefined
124-
try {
125-
return await importRuntimeModule<T>('devframe/adapters/mcp')
126-
}
127-
catch (error) {
128-
if (!autoMcpHinted.has(id)) {
129-
autoMcpHinted.add(id)
130-
const reason = error instanceof Error ? error.message : String(error)
131-
diagnostics.DF0077({ id, reason, cause: error })
132-
}
133-
return undefined
134-
}
118+
return await importRuntimeModule<T>('devframe/adapters/mcp')
135119
}
136120

137121
/**

0 commit comments

Comments
 (0)