@faapi/mcp 0.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/index.d.ts +600 -0
- package/dist/index.js +1414 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 faapi contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @faapi/mcp
|
|
2
|
+
|
|
3
|
+
> MCP Server SDK for faapi — 不依赖 @modelcontextprotocol/sdk,纯手写 MCP 协议
|
|
4
|
+
|
|
5
|
+
`@faapi/mcp` 实现 MCP(Model Context Protocol)服务端,通过 Streamable HTTP transport 暴露 tool 给 AI 助手(如 Claude、Cursor)。用 zod 声明 tool 输入参数,自动转为 JSON Schema。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @faapi/mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
要求 Node.js >= 24。
|
|
14
|
+
|
|
15
|
+
## 快速开始
|
|
16
|
+
|
|
17
|
+
在 faapi 应用中创建 MCP 端点:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// api/mcp/handler.ts
|
|
21
|
+
import { createMcpServer, createMcpHandler } from '@faapi/mcp';
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
|
|
24
|
+
const mcp = createMcpServer({ name: 'my-app', version: '1.0.0' });
|
|
25
|
+
|
|
26
|
+
mcp.tool('hello', {
|
|
27
|
+
description: 'Say hello',
|
|
28
|
+
input: { name: z.string().describe('Name to greet') },
|
|
29
|
+
handler: async ({ name }) => ({
|
|
30
|
+
content: [{ type: 'text', text: `Hello, ${name}!` }],
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const { POST, GET, DELETE } = createMcpHandler(mcp);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
启动后,MCP 端点在 `/api/mcp` 可用,AI 助手通过 Streamable HTTP 连接。
|
|
38
|
+
|
|
39
|
+
## MCP 方法
|
|
40
|
+
|
|
41
|
+
| 方法 | 行为 |
|
|
42
|
+
|------|------|
|
|
43
|
+
| `initialize` | 协议握手,返回 serverInfo + capabilities |
|
|
44
|
+
| `tools/list` | 列出所有 tool(含 JSON Schema) |
|
|
45
|
+
| `tools/call` | 调用 tool,返回结果 |
|
|
46
|
+
| `ping` | 心跳 |
|
|
47
|
+
|
|
48
|
+
## Transport
|
|
49
|
+
|
|
50
|
+
仅支持 Streamable HTTP(MCP 2025-06-18 规范):
|
|
51
|
+
|
|
52
|
+
- **POST**:发送 JSON-RPC 消息,返回 JSON 响应
|
|
53
|
+
- **GET**:v1 返回 405(不支持独立 SSE 流)
|
|
54
|
+
- **DELETE**:按 `Mcp-Session-Id` 销毁会话
|
|
55
|
+
|
|
56
|
+
## 与 @modelcontextprotocol/sdk 的区别
|
|
57
|
+
|
|
58
|
+
| 方面 | @modelcontextprotocol/sdk | @faapi/mcp |
|
|
59
|
+
|------|--------------------------|------------|
|
|
60
|
+
| Transport | stdio + Streamable HTTP + 旧版 SSE | 仅 Streamable HTTP |
|
|
61
|
+
| 依赖 | @modelcontextprotocol/sdk + zod-to-json-schema | 仅 zod(v4 内置 toJSONSchema) |
|
|
62
|
+
| 集成 | 独立进程 | faapi 路由(函数即接口) |
|
|
63
|
+
| Tool 定义 | `server.tool(name, schema, cb)` | `mcp.tool(name, { input, handler })` |
|
|
64
|
+
|
|
65
|
+
## 许可证
|
|
66
|
+
|
|
67
|
+
[MIT](https://github.com/faapi/faapi/blob/main/LICENSE)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* JSON-RPC 2.0 协议消息类型、解析和响应构建
|
|
6
|
+
*
|
|
7
|
+
* MCP 协议基于 JSON-RPC 2.0,所有通信都是 JSON-RPC 消息。
|
|
8
|
+
* 本模块提供类型定义、消息判定函数和响应构建工具。
|
|
9
|
+
*/
|
|
10
|
+
interface JsonRpcRequest {
|
|
11
|
+
jsonrpc: '2.0';
|
|
12
|
+
id: string | number;
|
|
13
|
+
method: string;
|
|
14
|
+
params?: unknown;
|
|
15
|
+
}
|
|
16
|
+
interface JsonRpcNotification {
|
|
17
|
+
jsonrpc: '2.0';
|
|
18
|
+
method: string;
|
|
19
|
+
params?: unknown;
|
|
20
|
+
}
|
|
21
|
+
interface JsonRpcResultResponse {
|
|
22
|
+
jsonrpc: '2.0';
|
|
23
|
+
id: string | number;
|
|
24
|
+
result: unknown;
|
|
25
|
+
}
|
|
26
|
+
interface JsonRpcErrorResponse {
|
|
27
|
+
jsonrpc: '2.0';
|
|
28
|
+
id: string | number | null;
|
|
29
|
+
error: JsonRpcError;
|
|
30
|
+
}
|
|
31
|
+
interface JsonRpcError {
|
|
32
|
+
code: number;
|
|
33
|
+
message: string;
|
|
34
|
+
data?: unknown;
|
|
35
|
+
}
|
|
36
|
+
type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResultResponse | JsonRpcErrorResponse;
|
|
37
|
+
declare const ErrorCode: {
|
|
38
|
+
readonly ParseError: -32700;
|
|
39
|
+
readonly InvalidRequest: -32600;
|
|
40
|
+
readonly MethodNotFound: -32601;
|
|
41
|
+
readonly InvalidParams: -32602;
|
|
42
|
+
readonly InternalError: -32603;
|
|
43
|
+
readonly ConnectionClosed: -32000;
|
|
44
|
+
readonly RequestTimeout: -32001;
|
|
45
|
+
};
|
|
46
|
+
declare function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest;
|
|
47
|
+
declare function isNotification(msg: JsonRpcMessage): msg is JsonRpcNotification;
|
|
48
|
+
declare function isResultResponse(msg: JsonRpcMessage): msg is JsonRpcResultResponse;
|
|
49
|
+
declare function isErrorResponse(msg: JsonRpcMessage): msg is JsonRpcErrorResponse;
|
|
50
|
+
declare function createResultResponse(id: string | number, result: unknown): JsonRpcResultResponse;
|
|
51
|
+
declare function createErrorResponse(id: string | number | null, code: number, message: string, data?: unknown): JsonRpcErrorResponse;
|
|
52
|
+
/**
|
|
53
|
+
* 解析 JSON-RPC 消息(单条或批量)
|
|
54
|
+
*
|
|
55
|
+
* @throws ParseError(返回 Error 对象,不抛异常)
|
|
56
|
+
* @returns 消息数组(单条也包装为数组)
|
|
57
|
+
*/
|
|
58
|
+
declare function parseJsonRpcMessage(data: unknown): JsonRpcMessage[];
|
|
59
|
+
declare class JsonRpcParseError extends Error {
|
|
60
|
+
constructor(message: string);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* MCP 会话管理
|
|
65
|
+
*
|
|
66
|
+
* Streamable HTTP transport 通过 Mcp-Session-Id header 维持会话。
|
|
67
|
+
* 会话在 initialize 请求时创建,在 DELETE 请求时销毁。
|
|
68
|
+
*
|
|
69
|
+
* 内置 TTL 机制:超过空闲时间的会话自动过期,防止内存泄漏。
|
|
70
|
+
* 惰性清理:在 get/create 时检查过期会话,无需定时器。
|
|
71
|
+
*
|
|
72
|
+
* 每个 session 维护 SSE subscriber 集合,用于服务端主动推送
|
|
73
|
+
* (logging/message, resources/updated, progress 等通知)。
|
|
74
|
+
*/
|
|
75
|
+
/** 日志级别(syslog 严重度,从低到高) */
|
|
76
|
+
type LoggingLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency';
|
|
77
|
+
/** SSE 流订阅者(controller + 关联的 session id) */
|
|
78
|
+
interface SseSubscriber {
|
|
79
|
+
/** SSE 流控制器 */
|
|
80
|
+
controller: ReadableStreamDefaultController<Uint8Array>;
|
|
81
|
+
/** 关联的 session id */
|
|
82
|
+
sessionId: string;
|
|
83
|
+
}
|
|
84
|
+
interface McpSession {
|
|
85
|
+
/** 会话 ID(cryptographically secure UUID) */
|
|
86
|
+
id: string;
|
|
87
|
+
/** 是否已完成 initialize 握手 */
|
|
88
|
+
initialized: boolean;
|
|
89
|
+
/** 协商的协议版本 */
|
|
90
|
+
protocolVersion: string;
|
|
91
|
+
/** 客户端信息(来自 initialize 请求) */
|
|
92
|
+
clientInfo?: {
|
|
93
|
+
name: string;
|
|
94
|
+
version: string;
|
|
95
|
+
};
|
|
96
|
+
/** 创建时间戳 */
|
|
97
|
+
createdAt: number;
|
|
98
|
+
/** 最后活动时间戳(每次 get 时更新) */
|
|
99
|
+
lastActivity: number;
|
|
100
|
+
/** 当前日志级别(低于此级别的日志不推送),默认 info */
|
|
101
|
+
loggingLevel: LoggingLevel;
|
|
102
|
+
/** SSE 流订阅者集合(GET 流注册,用于服务端主动推送) */
|
|
103
|
+
subscribers: Set<SseSubscriber>;
|
|
104
|
+
/** 已订阅的资源 URI 集合(resources/subscribe 注册) */
|
|
105
|
+
subscribedResources: Set<string>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 内存会话管理器
|
|
109
|
+
*
|
|
110
|
+
* 生产环境如需多实例共享会话,可替换为 Redis 等外部存储实现。
|
|
111
|
+
*
|
|
112
|
+
* @param ttl 会话空闲超时(毫秒),超过此时间未活动的会话自动过期。默认 30 分钟。设为 0 表示永不过期。
|
|
113
|
+
*/
|
|
114
|
+
declare class SessionManager {
|
|
115
|
+
private sessions;
|
|
116
|
+
private readonly ttl;
|
|
117
|
+
constructor(ttl?: number);
|
|
118
|
+
/** 是否启用 TTL 过期检查(ttl > 0) */
|
|
119
|
+
private get ttlEnabled();
|
|
120
|
+
/** 创建新会话,返回 session 对象 */
|
|
121
|
+
create(): McpSession;
|
|
122
|
+
/** 按 ID 获取会话(更新最后活动时间,过期返回 undefined) */
|
|
123
|
+
get(id: string): McpSession | undefined;
|
|
124
|
+
/** 是否存在(不更新活动时间,过期会话返回 false) */
|
|
125
|
+
has(id: string): boolean;
|
|
126
|
+
/** 销毁会话(关闭所有 SSE 订阅者) */
|
|
127
|
+
delete(id: string): boolean;
|
|
128
|
+
/** 当前会话数(含可能尚未清理的过期会话) */
|
|
129
|
+
get size(): number;
|
|
130
|
+
/** 获取所有 session ID 列表(用于全局广播通知) */
|
|
131
|
+
allSessionIds(): string[];
|
|
132
|
+
/** 清空所有会话 */
|
|
133
|
+
clear(): void;
|
|
134
|
+
/** 注册 SSE 订阅者到 session */
|
|
135
|
+
addSubscriber(sessionId: string, controller: ReadableStreamDefaultController<Uint8Array>): SseSubscriber | undefined;
|
|
136
|
+
/** 注销 SSE 订阅者 */
|
|
137
|
+
removeSubscriber(subscriber: SseSubscriber): void;
|
|
138
|
+
/** 向 session 的所有订阅者推送 SSE 数据 */
|
|
139
|
+
broadcastToSession(sessionId: string, data: string): void;
|
|
140
|
+
/** 判断指定级别的日志是否应该推送(>= session.loggingLevel) */
|
|
141
|
+
shouldLog(sessionId: string, level: LoggingLevel): boolean;
|
|
142
|
+
/** 添加资源订阅(将 uri 加入 session.subscribedResources) */
|
|
143
|
+
subscribeResource(sessionId: string, uri: string): boolean;
|
|
144
|
+
/** 取消资源订阅(从 session.subscribedResources 移除 uri) */
|
|
145
|
+
unsubscribeResource(sessionId: string, uri: string): boolean;
|
|
146
|
+
/** 找出所有订阅了指定 URI 的 session id 列表 */
|
|
147
|
+
findSubscribersOfUri(uri: string): string[];
|
|
148
|
+
/** 关闭 session 的所有订阅者(用于 session 销毁/过期) */
|
|
149
|
+
private closeSubscribers;
|
|
150
|
+
/** 清理所有过期会话 */
|
|
151
|
+
private cleanupExpired;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* MCP Server 核心:tool/resource/prompt 注册 + JSON-RPC 方法分发
|
|
156
|
+
*
|
|
157
|
+
* 实现 MCP 协议的核心方法:
|
|
158
|
+
* - initialize:握手,返回 serverInfo + capabilities(根据注册情况动态生成)
|
|
159
|
+
* - notifications/initialized:通知,无响应
|
|
160
|
+
* - tools/list、tools/call:tool 列表与调用
|
|
161
|
+
* - resources/list、resources/read:资源列表与读取
|
|
162
|
+
* - prompts/list、prompts/get:提示列表与获取
|
|
163
|
+
* - ping:心跳
|
|
164
|
+
*
|
|
165
|
+
* Tool 输入参数用 zod schema 声明,通过 zod v4 内置 toJSONSchema 转为 JSON Schema。
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
/** MCP 协议版本(当前实现遵循 2025-06-18 规范) */
|
|
169
|
+
declare const PROTOCOL_VERSION = "2025-06-18";
|
|
170
|
+
/** 支持的协议版本列表 */
|
|
171
|
+
declare const SUPPORTED_PROTOCOL_VERSIONS: readonly ["2025-06-18", "2025-03-26"];
|
|
172
|
+
interface McpServerOptions {
|
|
173
|
+
/** Server 名称 */
|
|
174
|
+
name: string;
|
|
175
|
+
/** Server 版本 */
|
|
176
|
+
version: string;
|
|
177
|
+
/** 可选显示名称 */
|
|
178
|
+
title?: string;
|
|
179
|
+
/** 可选给客户端的指引说明 */
|
|
180
|
+
instructions?: string;
|
|
181
|
+
/** 会话空闲超时(毫秒),默认 30 分钟;设为 0 表示永不过期 */
|
|
182
|
+
sessionTtl?: number;
|
|
183
|
+
/** GET SSE 流心跳间隔(毫秒),默认 30 秒 */
|
|
184
|
+
sseHeartbeatMs?: number;
|
|
185
|
+
/** list 方法默认每页项数,默认 100 */
|
|
186
|
+
defaultPageSize?: number;
|
|
187
|
+
/** tools 列表是否可变(声明 listChanged: true + removeTool 时推送通知)。默认 false */
|
|
188
|
+
toolsListChanged?: boolean;
|
|
189
|
+
/** resources 列表是否可变(声明 listChanged: true + removeResource 时推送通知)。默认 false */
|
|
190
|
+
resourcesListChanged?: boolean;
|
|
191
|
+
/** prompts 列表是否可变(声明 listChanged: true + removePrompt 时推送通知)。默认 false */
|
|
192
|
+
promptsListChanged?: boolean;
|
|
193
|
+
}
|
|
194
|
+
interface McpToolResult {
|
|
195
|
+
/** 内容数组(text/image/audio/resource_link/resource) */
|
|
196
|
+
content: Array<Record<string, unknown>>;
|
|
197
|
+
/** 是否为 tool 执行错误(区别于协议错误) */
|
|
198
|
+
isError?: boolean;
|
|
199
|
+
/** 结构化输出(配合 outputSchema 使用) */
|
|
200
|
+
structuredContent?: unknown;
|
|
201
|
+
}
|
|
202
|
+
/** sendLogging 函数类型——推送日志到 session 的 SSE 订阅者 */
|
|
203
|
+
type SendLoggingFn = (level: LoggingLevel, data: unknown, logger?: string) => void;
|
|
204
|
+
/** sendProgress 函数类型——推送进度到 session 的 SSE 订阅者 */
|
|
205
|
+
type SendProgressFn = (progress: number, total?: number) => void;
|
|
206
|
+
/** 自定义方法的请求 extra(与 tool/resource/prompt handler extra 共享 send* 能力) */
|
|
207
|
+
interface RequestExtra {
|
|
208
|
+
/** 会话 ID */
|
|
209
|
+
sessionId: string;
|
|
210
|
+
/** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */
|
|
211
|
+
sendLogging: SendLoggingFn;
|
|
212
|
+
/** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */
|
|
213
|
+
sendProgress: SendProgressFn;
|
|
214
|
+
}
|
|
215
|
+
/** 自定义方法 handler 返回值——对象作为 result,JsonRpcErrorResponse 作为错误 */
|
|
216
|
+
type MethodHandlerResult = Record<string, unknown> | JsonRpcErrorResponse;
|
|
217
|
+
/** 自定义 JSON-RPC 方法 handler 类型 */
|
|
218
|
+
type MethodHandler = (params: unknown, session: McpSession | undefined, extra: RequestExtra) => Promise<MethodHandlerResult> | MethodHandlerResult;
|
|
219
|
+
interface ToolCallExtra {
|
|
220
|
+
/** 会话 ID */
|
|
221
|
+
sessionId: string;
|
|
222
|
+
/** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */
|
|
223
|
+
sendLogging: SendLoggingFn;
|
|
224
|
+
/** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */
|
|
225
|
+
sendProgress: SendProgressFn;
|
|
226
|
+
}
|
|
227
|
+
interface McpToolDefinition {
|
|
228
|
+
/** 工具描述 */
|
|
229
|
+
description?: string;
|
|
230
|
+
/** 输入参数 schema(zod raw shape,如 { name: z.string() }) */
|
|
231
|
+
input?: Record<string, z.ZodType>;
|
|
232
|
+
/** 工具注解(如 readOnlyHint、destructiveHint 等) */
|
|
233
|
+
annotations?: Record<string, unknown>;
|
|
234
|
+
/** 处理函数 */
|
|
235
|
+
handler: (args: Record<string, unknown>, extra: ToolCallExtra) => Promise<McpToolResult> | McpToolResult;
|
|
236
|
+
}
|
|
237
|
+
interface McpResourceContent {
|
|
238
|
+
/** 资源 URI */
|
|
239
|
+
uri: string;
|
|
240
|
+
/** MIME 类型 */
|
|
241
|
+
mimeType?: string;
|
|
242
|
+
/** 文本内容(与 blob 二选一) */
|
|
243
|
+
text?: string;
|
|
244
|
+
/** 二进制内容(Base64 编码,与 text 二选一) */
|
|
245
|
+
blob?: string;
|
|
246
|
+
}
|
|
247
|
+
interface McpResourceReadResult {
|
|
248
|
+
/** 资源内容数组(支持一个 URI 返回多段内容) */
|
|
249
|
+
contents: McpResourceContent[];
|
|
250
|
+
}
|
|
251
|
+
interface ResourceReadExtra {
|
|
252
|
+
/** 会话 ID */
|
|
253
|
+
sessionId: string;
|
|
254
|
+
/** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */
|
|
255
|
+
sendLogging: SendLoggingFn;
|
|
256
|
+
/** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */
|
|
257
|
+
sendProgress: SendProgressFn;
|
|
258
|
+
}
|
|
259
|
+
interface McpResourceDefinition {
|
|
260
|
+
/** 资源名称(必填) */
|
|
261
|
+
name: string;
|
|
262
|
+
/** 资源描述 */
|
|
263
|
+
description?: string;
|
|
264
|
+
/** MIME 类型 */
|
|
265
|
+
mimeType?: string;
|
|
266
|
+
/** 读取 handler(必填) */
|
|
267
|
+
read: (uri: string, extra: ResourceReadExtra) => Promise<McpResourceReadResult> | McpResourceReadResult;
|
|
268
|
+
}
|
|
269
|
+
interface ResourceTemplateReadExtra {
|
|
270
|
+
/** 会话 ID */
|
|
271
|
+
sessionId: string;
|
|
272
|
+
/** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */
|
|
273
|
+
sendLogging: SendLoggingFn;
|
|
274
|
+
/** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */
|
|
275
|
+
sendProgress: SendProgressFn;
|
|
276
|
+
}
|
|
277
|
+
interface McpResourceTemplateDefinition {
|
|
278
|
+
/** 资源名称(必填) */
|
|
279
|
+
name: string;
|
|
280
|
+
/** 资源描述 */
|
|
281
|
+
description?: string;
|
|
282
|
+
/** MIME 类型 */
|
|
283
|
+
mimeType?: string;
|
|
284
|
+
/** 读取 handler(必填):接收实际 URI 和从 URI 模板提取的 params */
|
|
285
|
+
read: (uri: string, params: Record<string, string>, extra: ResourceTemplateReadExtra) => Promise<McpResourceReadResult> | McpResourceReadResult;
|
|
286
|
+
}
|
|
287
|
+
interface McpPromptArgument {
|
|
288
|
+
/** 参数名称 */
|
|
289
|
+
name: string;
|
|
290
|
+
/** 参数描述 */
|
|
291
|
+
description?: string;
|
|
292
|
+
/** 是否必填 */
|
|
293
|
+
required?: boolean;
|
|
294
|
+
}
|
|
295
|
+
interface McpPromptContent {
|
|
296
|
+
/** 内容类型:text 或 image */
|
|
297
|
+
type: 'text' | 'image';
|
|
298
|
+
/** 文本内容(type=text 时) */
|
|
299
|
+
text?: string;
|
|
300
|
+
/** Base64 编码的图片数据(type=image 时) */
|
|
301
|
+
data?: string;
|
|
302
|
+
/** 图片 MIME 类型(type=image 时) */
|
|
303
|
+
mimeType?: string;
|
|
304
|
+
}
|
|
305
|
+
interface McpPromptMessage {
|
|
306
|
+
/** 角色 */
|
|
307
|
+
role: 'user' | 'assistant';
|
|
308
|
+
/** 内容 */
|
|
309
|
+
content: McpPromptContent;
|
|
310
|
+
}
|
|
311
|
+
interface McpPromptGetResult {
|
|
312
|
+
/** 提示消息列表 */
|
|
313
|
+
messages: McpPromptMessage[];
|
|
314
|
+
}
|
|
315
|
+
interface PromptGetExtra {
|
|
316
|
+
/** 会话 ID */
|
|
317
|
+
sessionId: string;
|
|
318
|
+
/** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */
|
|
319
|
+
sendLogging: SendLoggingFn;
|
|
320
|
+
/** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */
|
|
321
|
+
sendProgress: SendProgressFn;
|
|
322
|
+
}
|
|
323
|
+
interface McpPromptDefinition {
|
|
324
|
+
/** 提示描述 */
|
|
325
|
+
description?: string;
|
|
326
|
+
/** 参数定义 */
|
|
327
|
+
arguments?: McpPromptArgument[];
|
|
328
|
+
/** 获取 handler(必填) */
|
|
329
|
+
get: (args: Record<string, string>, extra: PromptGetExtra) => Promise<McpPromptGetResult> | McpPromptGetResult;
|
|
330
|
+
}
|
|
331
|
+
/** completion 引用——指向某个 prompt 或 resource template */
|
|
332
|
+
type CompletionRef = {
|
|
333
|
+
type: 'ref/prompt';
|
|
334
|
+
name: string;
|
|
335
|
+
} | {
|
|
336
|
+
type: 'ref/resource';
|
|
337
|
+
uri: string;
|
|
338
|
+
};
|
|
339
|
+
/** completion 调用上下文——传递给 handler */
|
|
340
|
+
interface CompletionContext {
|
|
341
|
+
/** 客户端已填写的其他参数(部分填充,key 为参数名,value 为已填值) */
|
|
342
|
+
arguments: Record<string, string>;
|
|
343
|
+
}
|
|
344
|
+
/** completion 返回结果 */
|
|
345
|
+
interface CompletionResult {
|
|
346
|
+
/** 候选值数组 */
|
|
347
|
+
values: string[];
|
|
348
|
+
/** 总数(可选,用于客户端提示"还有 N 项") */
|
|
349
|
+
total?: number;
|
|
350
|
+
/** 是否还有更多(可选,提示客户端可分页或继续输入) */
|
|
351
|
+
hasMore?: boolean;
|
|
352
|
+
}
|
|
353
|
+
/** completion handler 函数类型 */
|
|
354
|
+
type CompletionHandler = (value: string, context: CompletionContext) => Promise<CompletionResult> | CompletionResult;
|
|
355
|
+
declare class McpServer {
|
|
356
|
+
private options;
|
|
357
|
+
private tools;
|
|
358
|
+
private resources;
|
|
359
|
+
private resourceTemplates;
|
|
360
|
+
private prompts;
|
|
361
|
+
private completions;
|
|
362
|
+
private methods;
|
|
363
|
+
private sessions;
|
|
364
|
+
private readonly pageSize;
|
|
365
|
+
private readonly toolsListChanged;
|
|
366
|
+
private readonly resourcesListChanged;
|
|
367
|
+
private readonly promptsListChanged;
|
|
368
|
+
constructor(options: McpServerOptions);
|
|
369
|
+
/** 注册 tool */
|
|
370
|
+
tool(name: string, definition: McpToolDefinition): void;
|
|
371
|
+
/** 注册 resource */
|
|
372
|
+
resource(uri: string, definition: McpResourceDefinition): void;
|
|
373
|
+
/** 注册 resource template(RFC 6570 URI 模板) */
|
|
374
|
+
resourceTemplate(uriTemplate: string, definition: McpResourceTemplateDefinition): void;
|
|
375
|
+
/** 注册 prompt */
|
|
376
|
+
prompt(name: string, definition: McpPromptDefinition): void;
|
|
377
|
+
/**
|
|
378
|
+
* 注册参数补全 handler
|
|
379
|
+
*
|
|
380
|
+
* - `ref`:`{ type: 'ref/prompt', name }` 或 `{ type: 'ref/resource', uri }`(对资源模板,uri 是模板字符串)
|
|
381
|
+
* - `argumentName`:补全的参数名(对应 prompt arguments 中的 name,或 resource template URI 模板中的变量名)
|
|
382
|
+
* - 同一 (ref, argumentName) 重复注册抛错
|
|
383
|
+
*/
|
|
384
|
+
completion(ref: CompletionRef, argumentName: string, handler: CompletionHandler): void;
|
|
385
|
+
/**
|
|
386
|
+
* 注册自定义 JSON-RPC 方法 handler(业务拓展)
|
|
387
|
+
*
|
|
388
|
+
* - 方法名建议使用 `appName/action` 格式,避免与 MCP 标准方法冲突
|
|
389
|
+
* - 与 MCP 内置方法(initialize/ping/tools/* 等)冲突时抛错
|
|
390
|
+
* - 重复注册同名方法抛错
|
|
391
|
+
*
|
|
392
|
+
* handler 返回值规则:
|
|
393
|
+
* - 返回普通对象:作为 JSON-RPC result 字段
|
|
394
|
+
* - 返回 JsonRpcErrorResponse(含 error 字段):作为错误响应
|
|
395
|
+
*/
|
|
396
|
+
method(name: string, handler: MethodHandler): void;
|
|
397
|
+
/** 获取已注册 tool 名称列表 */
|
|
398
|
+
listTools(): string[];
|
|
399
|
+
/** 获取已注册 resource URI 列表 */
|
|
400
|
+
listResources(): string[];
|
|
401
|
+
/** 获取已注册 prompt 名称列表 */
|
|
402
|
+
listPrompts(): string[];
|
|
403
|
+
/** 获取已注册自定义方法名列表(业务拓展) */
|
|
404
|
+
listMethods(): string[];
|
|
405
|
+
/**
|
|
406
|
+
* 删除已注册 tool
|
|
407
|
+
*
|
|
408
|
+
* - `toolsListChanged: true` 时自动推送 `notifications/tools/list_changed`
|
|
409
|
+
* - `toolsListChanged: false` 时静默删除(用于 dev 热替换,客户端不感知)
|
|
410
|
+
*/
|
|
411
|
+
removeTool(name: string): boolean;
|
|
412
|
+
/**
|
|
413
|
+
* 删除已注册 resource
|
|
414
|
+
*
|
|
415
|
+
* - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
|
|
416
|
+
*/
|
|
417
|
+
removeResource(uri: string): boolean;
|
|
418
|
+
/**
|
|
419
|
+
* 删除已注册 resource template
|
|
420
|
+
*
|
|
421
|
+
* - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
|
|
422
|
+
*/
|
|
423
|
+
removeResourceTemplate(uriTemplate: string): boolean;
|
|
424
|
+
/**
|
|
425
|
+
* 删除已注册 prompt
|
|
426
|
+
*
|
|
427
|
+
* - `promptsListChanged: true` 时自动推送 `notifications/prompts/list_changed`
|
|
428
|
+
*/
|
|
429
|
+
removePrompt(name: string): boolean;
|
|
430
|
+
/**
|
|
431
|
+
* 删除已注册 completion handler
|
|
432
|
+
*
|
|
433
|
+
* - completion 无 list_changed 通知机制(客户端按需请求,无需感知列表变化)
|
|
434
|
+
*/
|
|
435
|
+
removeCompletion(ref: CompletionRef, argumentName: string): boolean;
|
|
436
|
+
/** 删除已注册自定义方法 */
|
|
437
|
+
removeMethod(name: string): boolean;
|
|
438
|
+
/**
|
|
439
|
+
* 推送 `notifications/tools/list_changed` 到所有 session 的 SSE 订阅者
|
|
440
|
+
*
|
|
441
|
+
* 客户端收到后应重新调用 `tools/list` 拉取最新列表。
|
|
442
|
+
* `removeTool` 在 `toolsListChanged: true` 时自动调用本方法,
|
|
443
|
+
* 业务方也可手动调用(如批量删除后只推送一次)。
|
|
444
|
+
*/
|
|
445
|
+
notifyToolsListChanged(): void;
|
|
446
|
+
/** 推送 `notifications/resources/list_changed` 到所有 session 的 SSE 订阅者 */
|
|
447
|
+
notifyResourcesListChanged(): void;
|
|
448
|
+
/** 推送 `notifications/prompts/list_changed` 到所有 session 的 SSE 订阅者 */
|
|
449
|
+
notifyPromptsListChanged(): void;
|
|
450
|
+
/**
|
|
451
|
+
* 向所有 session 的所有 SSE 订阅者广播通知(内部工具方法)
|
|
452
|
+
*
|
|
453
|
+
* 用于 list_changed 这种"全局广播"语义——所有 session 都应感知列表变化。
|
|
454
|
+
*/
|
|
455
|
+
private broadcastNotificationToAllSessions;
|
|
456
|
+
/** 获取会话管理器 */
|
|
457
|
+
getSessionManager(): SessionManager;
|
|
458
|
+
/** 获取 GET SSE 流心跳间隔(毫秒) */
|
|
459
|
+
getSseHeartbeatMs(): number;
|
|
460
|
+
/**
|
|
461
|
+
* 处理 JSON-RPC 请求,返回响应消息
|
|
462
|
+
*
|
|
463
|
+
* 通知(无 id)不返回响应,返回 null。
|
|
464
|
+
*/
|
|
465
|
+
handleJsonRpc(message: JsonRpcMessage, session: McpSession | undefined): Promise<JsonRpcMessage | null>;
|
|
466
|
+
private handleNotification;
|
|
467
|
+
private handleRequest;
|
|
468
|
+
private handleInitialize;
|
|
469
|
+
private handleToolsList;
|
|
470
|
+
private handleToolsCall;
|
|
471
|
+
private handleResourcesList;
|
|
472
|
+
private handleResourcesRead;
|
|
473
|
+
private handleResourcesTemplatesList;
|
|
474
|
+
private handlePromptsList;
|
|
475
|
+
private handlePromptsGet;
|
|
476
|
+
private handleLoggingSetLevel;
|
|
477
|
+
private handleResourcesSubscribe;
|
|
478
|
+
private handleResourcesUnsubscribe;
|
|
479
|
+
private handleCompletionComplete;
|
|
480
|
+
private handleCustomMethod;
|
|
481
|
+
/**
|
|
482
|
+
* 推送 notifications/message 到 session 的所有 SSE 订阅者
|
|
483
|
+
*
|
|
484
|
+
* - 无 session 或无订阅者:静默丢弃
|
|
485
|
+
* - level 低于 session.loggingLevel:静默丢弃
|
|
486
|
+
* - SSE 行格式:`data: ${JSON.stringify(notification)}\n\n`
|
|
487
|
+
*/
|
|
488
|
+
sendLogging(sessionId: string, level: LoggingLevel, data: unknown, logger?: string): void;
|
|
489
|
+
/**
|
|
490
|
+
* 推送 notifications/resources/updated 到所有订阅了该 URI 的 session
|
|
491
|
+
*
|
|
492
|
+
* - 找出所有 subscribedResources 包含该 URI 的 session
|
|
493
|
+
* - 对每个 session 的所有 SSE 订阅者推送通知
|
|
494
|
+
* - 无订阅者的 session 静默跳过
|
|
495
|
+
*/
|
|
496
|
+
sendResourceUpdated(uri: string): void;
|
|
497
|
+
/**
|
|
498
|
+
* 推送 notifications/progress 到 session 的所有 SSE 订阅者
|
|
499
|
+
*
|
|
500
|
+
* - 无 session 或无订阅者:静默丢弃
|
|
501
|
+
* - progressToken 为 undefined/null:静默丢弃(无法关联进度与请求)
|
|
502
|
+
*
|
|
503
|
+
* @param sessionId 会话 ID
|
|
504
|
+
* @param progressToken 客户端在请求 _meta.progressToken 中传入的 token(任意 JSON 值)
|
|
505
|
+
* @param progress 当前进度(数值)
|
|
506
|
+
* @param total 总数(可选)
|
|
507
|
+
*/
|
|
508
|
+
sendProgress(sessionId: string, progressToken: unknown, progress: number, total?: number): void;
|
|
509
|
+
/**
|
|
510
|
+
* 通用通知推送(业务拓展)——向指定 session 的所有 SSE 订阅者推送任意通知
|
|
511
|
+
*
|
|
512
|
+
* - 无 session 或无订阅者:静默丢弃
|
|
513
|
+
* - 不校验 method 是否符合 MCP 规范——业务方自行负责
|
|
514
|
+
* - SSE 行格式:`data: ${JSON.stringify(notification)}\n\n`
|
|
515
|
+
*
|
|
516
|
+
* @param sessionId 目标 session ID
|
|
517
|
+
* @param method 通知方法名(如 `notifications/myapp/sync`)
|
|
518
|
+
* @param params 通知参数(可选)
|
|
519
|
+
*/
|
|
520
|
+
sendNotification(sessionId: string, method: string, params?: unknown): void;
|
|
521
|
+
}
|
|
522
|
+
declare function createMcpServer(options: McpServerOptions): McpServer;
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Streamable HTTP transport:Web Request → JSON-RPC → Response
|
|
526
|
+
*
|
|
527
|
+
* MCP Streamable HTTP transport 规范:
|
|
528
|
+
* - POST:发送 JSON-RPC 消息,返回 JSON 响应或 SSE 流
|
|
529
|
+
* - GET:打开 SSE 流接收服务端推送
|
|
530
|
+
* - DELETE:终止会话
|
|
531
|
+
*
|
|
532
|
+
* 会话管理通过 Mcp-Session-Id header。
|
|
533
|
+
* 实现:POST 返回 JSON,GET 返回 SSE 流(仅心跳,不推送业务消息),DELETE 销毁会话。
|
|
534
|
+
*/
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* 处理 MCP HTTP 请求
|
|
538
|
+
*
|
|
539
|
+
* 接收 Web API Request,返回 Web API Response。
|
|
540
|
+
* 可直接在 faapi handler 中使用:
|
|
541
|
+
* ```ts
|
|
542
|
+
* export function POST(ctx) {
|
|
543
|
+
* return mcp.handleWebRequest(ctx.request);
|
|
544
|
+
* }
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
declare function handleMcpRequest(request: Request, server: McpServer): Promise<Response>;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* faapi 适配器:把 MCP Server 挂载到 faapi 路由
|
|
551
|
+
*
|
|
552
|
+
* 两种集成方式:
|
|
553
|
+
*
|
|
554
|
+
* 1. handler 风格(推荐)——在 handler.ts 中使用:
|
|
555
|
+
* ```ts
|
|
556
|
+
* // api/mcp/handler.ts
|
|
557
|
+
* import { createMcpServer, createMcpHandler } from '@faapi/mcp';
|
|
558
|
+
* const mcp = createMcpServer({ name: 'my-app', version: '1.0.0' });
|
|
559
|
+
* mcp.tool('hello', { ... });
|
|
560
|
+
* export const { POST, GET, DELETE } = createMcpHandler(mcp);
|
|
561
|
+
* ```
|
|
562
|
+
*
|
|
563
|
+
* 2. 插件风格——在 faapi.config.ts 中声明:
|
|
564
|
+
* ```ts
|
|
565
|
+
* plugins: [['@faapi/mcp', { path: '/mcp' }]]
|
|
566
|
+
* ```
|
|
567
|
+
* 需配合 mcp.tool() 注册(通常在 lifecycle.onReady 中)。
|
|
568
|
+
*
|
|
569
|
+
* 函数即接口:MCP endpoint 就是 faapi 的一个路由。
|
|
570
|
+
*/
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* 创建 faapi handler 函数
|
|
574
|
+
*
|
|
575
|
+
* 返回 { POST, GET, DELETE },可直接在 handler.ts 中导出。
|
|
576
|
+
* faapi 按参数名注入 ctx,函数内通过 ctx.request 获取 Web Request。
|
|
577
|
+
*/
|
|
578
|
+
declare function createMcpHandler(mcp: McpServer): {
|
|
579
|
+
POST: (ctx: {
|
|
580
|
+
request: Request;
|
|
581
|
+
}) => Promise<Response>;
|
|
582
|
+
GET: (ctx: {
|
|
583
|
+
request: Request;
|
|
584
|
+
}) => Promise<Response>;
|
|
585
|
+
DELETE: (ctx: {
|
|
586
|
+
request: Request;
|
|
587
|
+
}) => Promise<Response>;
|
|
588
|
+
};
|
|
589
|
+
/**
|
|
590
|
+
* 创建 Node.js 请求处理函数(供 wrapHandler 使用)
|
|
591
|
+
*
|
|
592
|
+
* 用于 faapi 插件场景:插件通过 wrapHandler 拦截指定路径,
|
|
593
|
+
* 将 Node.js IncomingMessage/ServerResponse 转为 Web Request 处理。
|
|
594
|
+
*
|
|
595
|
+
* 使用 Node.js 原生 `Readable.toWeb(req)` 转换 body,正确处理 chunked transfer
|
|
596
|
+
* (多 chunk 累积)、backpressure 和 stream error。
|
|
597
|
+
*/
|
|
598
|
+
declare function createMcpNodeHandler(mcp: McpServer): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
599
|
+
|
|
600
|
+
export { type CompletionContext, type CompletionHandler, type CompletionRef, type CompletionResult, ErrorCode, type JsonRpcError, type JsonRpcErrorResponse, type JsonRpcMessage, type JsonRpcNotification, JsonRpcParseError, type JsonRpcRequest, type JsonRpcResultResponse, type LoggingLevel, type McpPromptArgument, type McpPromptContent, type McpPromptDefinition, type McpPromptGetResult, type McpPromptMessage, type McpResourceContent, type McpResourceDefinition, type McpResourceReadResult, type McpResourceTemplateDefinition, McpServer, type McpServerOptions, type McpSession, type McpToolDefinition, type McpToolResult, type MethodHandler, type MethodHandlerResult, PROTOCOL_VERSION, type PromptGetExtra, type RequestExtra, type ResourceReadExtra, type ResourceTemplateReadExtra, SUPPORTED_PROTOCOL_VERSIONS, type SendLoggingFn, type SendProgressFn, SessionManager, type SseSubscriber, type ToolCallExtra, createErrorResponse, createMcpHandler, createMcpNodeHandler, createMcpServer, createResultResponse, handleMcpRequest, isErrorResponse, isNotification, isRequest, isResultResponse, parseJsonRpcMessage };
|