mcpbox 0.2.0 → 0.2.2
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/README.md +33 -11
- package/dist/auth/oauth.js +4 -4
- package/dist/config/schema.js +8 -0
- package/dist/index.js +5 -5
- package/dist/mcp/handler.d.ts +22 -0
- package/dist/mcp/handler.js +148 -0
- package/dist/mcp/manager.d.ts +5 -12
- package/dist/mcp/manager.js +36 -33
- package/dist/server.js +4 -56
- package/package.json +2 -3
- package/dist/mcp/handlers.d.ts +0 -433
- package/dist/mcp/handlers.js +0 -144
package/README.md
CHANGED
|
@@ -5,14 +5,18 @@
|
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
|
-
**MCPBox** is a lightweight gateway that exposes local stdio-based MCP
|
|
8
|
+
**MCPBox** is a lightweight gateway that exposes local stdio-based [MCP](https://modelcontextprotocol.io) servers via Streamable HTTP, enabling Claude and other AI agents to connect from anywhere.
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Namespaces with `servername__` prefix to avoid collisions
|
|
10
|
+
- Runs multiple servers behind a single HTTP endpoint
|
|
11
|
+
- Supports Tools, Resources & Prompts
|
|
12
|
+
- Namespaces with `servername__` prefix to avoid collisions
|
|
13
|
+
- Per-server tool filtering to limit AI access and reduce context usage
|
|
13
14
|
- OAuth or API key authentication
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
<picture>
|
|
17
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/diagram-dark.excalidraw.png">
|
|
18
|
+
<img src="assets/diagram.excalidraw.png" alt="mcpbox diagram">
|
|
19
|
+
</picture>
|
|
16
20
|
|
|
17
21
|
## Quick Start
|
|
18
22
|
|
|
@@ -70,10 +74,10 @@ See [`mcpbox.example.jsonc`](mcpbox.example.jsonc) for all options. All string v
|
|
|
70
74
|
|
|
71
75
|
To expose MCPBox remotely, put it behind a TLS-terminating reverse proxy.
|
|
72
76
|
|
|
73
|
-
Before deploying:
|
|
74
|
-
- [ ] Use
|
|
75
|
-
- [ ] Set
|
|
76
|
-
- [ ] Use bcrypt hashes for passwords
|
|
77
|
+
Before deploying with OAuth:
|
|
78
|
+
- [ ] Use sqlite storage for persistence across restarts
|
|
79
|
+
- [ ] Set issuer to your public URL
|
|
80
|
+
- [ ] Use bcrypt hashes for local passwords
|
|
77
81
|
|
|
78
82
|
> [!NOTE]
|
|
79
83
|
> MCPBox is single-instance only — don't run multiple instances behind a load balancer.
|
|
@@ -93,7 +97,9 @@ Then update your config with the generated public URL:
|
|
|
93
97
|
"auth": {
|
|
94
98
|
"type": "oauth",
|
|
95
99
|
"issuer": "https://<tunnel-id>.trycloudflare.com",
|
|
96
|
-
"
|
|
100
|
+
"identityProviders": [
|
|
101
|
+
{ "type": "local", "users": [{ "username": "admin", "password": "${MCPBOX_PASSWORD}" }] }
|
|
102
|
+
],
|
|
97
103
|
"dynamicRegistration": true
|
|
98
104
|
},
|
|
99
105
|
"storage": {
|
|
@@ -126,12 +132,28 @@ claude mcp add --transport http mcpbox https://your-mcpbox-url.com
|
|
|
126
132
|
|
|
127
133
|
Requires `dynamicRegistration: true` in your config.
|
|
128
134
|
|
|
129
|
-
### MCP clients
|
|
135
|
+
### Other MCP clients
|
|
136
|
+
|
|
137
|
+
**With dynamic registration (OAuth)** — just provide the URL:
|
|
130
138
|
|
|
131
139
|
```json
|
|
132
140
|
{
|
|
133
141
|
"mcpServers": {
|
|
134
142
|
"mcpbox": {
|
|
143
|
+
"type": "http",
|
|
144
|
+
"url": "https://your-mcpbox-url.com"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**With API key:**
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"mcpServers": {
|
|
155
|
+
"mcpbox": {
|
|
156
|
+
"type": "http",
|
|
135
157
|
"url": "https://your-mcpbox-url.com",
|
|
136
158
|
"headers": {
|
|
137
159
|
"Authorization": "Bearer YOUR_API_KEY"
|
package/dist/auth/oauth.js
CHANGED
|
@@ -63,7 +63,7 @@ export class OAuthServer {
|
|
|
63
63
|
return {
|
|
64
64
|
resource: this.config.issuer,
|
|
65
65
|
authorization_servers: [this.config.issuer],
|
|
66
|
-
scopes_supported: ["mcp
|
|
66
|
+
scopes_supported: ["mcp"],
|
|
67
67
|
bearer_methods_supported: ["header"],
|
|
68
68
|
// Non-standard: logo for client display
|
|
69
69
|
logo_uri: `${this.config.issuer}/logo.png`,
|
|
@@ -87,7 +87,7 @@ export class OAuthServer {
|
|
|
87
87
|
token_endpoint: `${this.config.issuer}/token`,
|
|
88
88
|
grant_types_supported: grantTypes,
|
|
89
89
|
token_endpoint_auth_methods_supported: ["none", "client_secret_post"],
|
|
90
|
-
scopes_supported: ["mcp
|
|
90
|
+
scopes_supported: ["mcp"],
|
|
91
91
|
};
|
|
92
92
|
// Only advertise authorization endpoint if identity providers are configured
|
|
93
93
|
if (hasProviders) {
|
|
@@ -656,7 +656,7 @@ export class OAuthServer {
|
|
|
656
656
|
this.store.saveAccessToken({
|
|
657
657
|
token: hashSecret(accessToken),
|
|
658
658
|
clientId,
|
|
659
|
-
scope: "mcp
|
|
659
|
+
scope: "mcp",
|
|
660
660
|
expiresAt: Date.now() + expiresIn * 1000,
|
|
661
661
|
userId: `client:${clientId}`, // Mark as client-authenticated
|
|
662
662
|
});
|
|
@@ -666,7 +666,7 @@ export class OAuthServer {
|
|
|
666
666
|
access_token: accessToken, // Return unhashed token to client
|
|
667
667
|
token_type: "Bearer",
|
|
668
668
|
expires_in: expiresIn,
|
|
669
|
-
scope: "mcp
|
|
669
|
+
scope: "mcp",
|
|
670
670
|
});
|
|
671
671
|
}
|
|
672
672
|
// RFC 6749 Section 4.1: Authorization Code Grant
|
package/dist/config/schema.js
CHANGED
|
@@ -129,6 +129,14 @@ export const AuthConfigSchema = z.discriminatedUnion("type", [
|
|
|
129
129
|
return oauth.identityProviders && oauth.identityProviders.length > 0;
|
|
130
130
|
}, {
|
|
131
131
|
message: "dynamic registration requires identity providers to be configured for user login",
|
|
132
|
+
})
|
|
133
|
+
.refine((oauth) => {
|
|
134
|
+
if (!oauth.clients)
|
|
135
|
+
return true;
|
|
136
|
+
const ids = oauth.clients.map((c) => c.clientId);
|
|
137
|
+
return new Set(ids).size === ids.length;
|
|
138
|
+
}, {
|
|
139
|
+
message: "Duplicate client IDs are not allowed",
|
|
132
140
|
}),
|
|
133
141
|
]);
|
|
134
142
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
import { loadConfig, resolveConfigPath, } from "./config/loader.js";
|
|
3
4
|
import { configureLogger, logger } from "./logger.js";
|
|
4
5
|
import { createServer } from "./server.js";
|
|
@@ -33,10 +34,6 @@ function parseArgs(args) {
|
|
|
33
34
|
else if (arg === "-c" || arg === "--config") {
|
|
34
35
|
result.config = args[++i];
|
|
35
36
|
}
|
|
36
|
-
else if (!arg.startsWith("-")) {
|
|
37
|
-
// Positional arg = config path (backwards compat)
|
|
38
|
-
result.config = arg;
|
|
39
|
-
}
|
|
40
37
|
}
|
|
41
38
|
return result;
|
|
42
39
|
}
|
|
@@ -50,6 +47,10 @@ if (args.version) {
|
|
|
50
47
|
process.exit(0);
|
|
51
48
|
}
|
|
52
49
|
const configPath = resolveConfigPath(args.config);
|
|
50
|
+
if (args.config && !existsSync(configPath)) {
|
|
51
|
+
console.error(`Config file not found: ${configPath}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
53
54
|
let config;
|
|
54
55
|
let warnings;
|
|
55
56
|
try {
|
|
@@ -57,7 +58,6 @@ try {
|
|
|
57
58
|
}
|
|
58
59
|
catch (error) {
|
|
59
60
|
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
-
// Log to stderr directly since logger config isn't loaded yet
|
|
61
61
|
console.error(`Failed to load config from ${configPath}:\n${message}`);
|
|
62
62
|
process.exit(1);
|
|
63
63
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import type { Context } from "hono";
|
|
3
|
+
import type { McpManager } from "./manager.js";
|
|
4
|
+
export declare function createMcpHandler(mcpManager: McpManager): (c: Context) => Promise<(Response & import("hono").TypedResponse<{
|
|
5
|
+
jsonrpc: "2.0";
|
|
6
|
+
id: string | number;
|
|
7
|
+
error: {
|
|
8
|
+
code: number;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
12
|
+
jsonrpc: "2.0";
|
|
13
|
+
id: string | number;
|
|
14
|
+
result: import("hono/utils/types").JSONValue;
|
|
15
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
16
|
+
jsonrpc: string;
|
|
17
|
+
error: {
|
|
18
|
+
code: ErrorCode;
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
id: null;
|
|
22
|
+
}, 400, "json">) | (Response & import("hono").TypedResponse<null, 202, "body">)>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { CallToolRequestSchema, CompleteRequestSchema, ErrorCode, GetPromptRequestSchema, InitializeRequestSchema, isJSONRPCNotification, JSONRPCRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, PingRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
import { NAME, VERSION } from "../version.js";
|
|
4
|
+
function jsonrpcResult(id, result) {
|
|
5
|
+
return { jsonrpc: "2.0", id, result };
|
|
6
|
+
}
|
|
7
|
+
function jsonrpcError(id, code, message) {
|
|
8
|
+
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
9
|
+
}
|
|
10
|
+
export function createMcpHandler(mcpManager) {
|
|
11
|
+
return async (c) => {
|
|
12
|
+
let body;
|
|
13
|
+
try {
|
|
14
|
+
body = await c.req.json();
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
logger.warn({ error: e instanceof Error ? e.message : String(e) }, "MCP parse error");
|
|
18
|
+
return c.json({
|
|
19
|
+
jsonrpc: "2.0",
|
|
20
|
+
error: { code: ErrorCode.ParseError, message: "Parse error" },
|
|
21
|
+
id: null,
|
|
22
|
+
}, 400);
|
|
23
|
+
}
|
|
24
|
+
if (isJSONRPCNotification(body)) {
|
|
25
|
+
const method = body.method;
|
|
26
|
+
logger.debug({ method }, "MCP notification");
|
|
27
|
+
return c.body(null, 202);
|
|
28
|
+
}
|
|
29
|
+
const envelope = JSONRPCRequestSchema.safeParse(body);
|
|
30
|
+
if (!envelope.success) {
|
|
31
|
+
return c.json({
|
|
32
|
+
jsonrpc: "2.0",
|
|
33
|
+
error: {
|
|
34
|
+
code: ErrorCode.InvalidRequest,
|
|
35
|
+
message: "Invalid request",
|
|
36
|
+
},
|
|
37
|
+
id: null,
|
|
38
|
+
}, 400);
|
|
39
|
+
}
|
|
40
|
+
const request = envelope.data;
|
|
41
|
+
logger.debug({ method: request.method, id: request.id }, "MCP request");
|
|
42
|
+
return handleRequest(c, request, mcpManager);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async function handleRequest(c, request, mcpManager) {
|
|
46
|
+
const { id, method } = request;
|
|
47
|
+
if (method === "initialize") {
|
|
48
|
+
const parsed = InitializeRequestSchema.safeParse(request);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
51
|
+
}
|
|
52
|
+
const result = {
|
|
53
|
+
protocolVersion: "2025-11-25",
|
|
54
|
+
capabilities: {
|
|
55
|
+
tools: { listChanged: true },
|
|
56
|
+
resources: { listChanged: true },
|
|
57
|
+
prompts: { listChanged: true },
|
|
58
|
+
completions: {},
|
|
59
|
+
},
|
|
60
|
+
serverInfo: {
|
|
61
|
+
name: NAME,
|
|
62
|
+
version: VERSION,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
return c.json(jsonrpcResult(id, result));
|
|
66
|
+
}
|
|
67
|
+
if (method === "ping") {
|
|
68
|
+
const parsed = PingRequestSchema.safeParse(request);
|
|
69
|
+
if (!parsed.success) {
|
|
70
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
71
|
+
}
|
|
72
|
+
return c.json(jsonrpcResult(id, {}));
|
|
73
|
+
}
|
|
74
|
+
if (method === "tools/list") {
|
|
75
|
+
const parsed = ListToolsRequestSchema.safeParse(request);
|
|
76
|
+
if (!parsed.success) {
|
|
77
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
78
|
+
}
|
|
79
|
+
return c.json(jsonrpcResult(id, { tools: mcpManager.listTools() }));
|
|
80
|
+
}
|
|
81
|
+
if (method === "tools/call") {
|
|
82
|
+
const parsed = CallToolRequestSchema.safeParse(request);
|
|
83
|
+
if (!parsed.success) {
|
|
84
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = await mcpManager.callTool(parsed.data.params);
|
|
88
|
+
return c.json(jsonrpcResult(id, result));
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return c.json(jsonrpcError(id, ErrorCode.InternalError, error instanceof Error ? error.message : "Internal error"));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (method === "resources/list") {
|
|
95
|
+
const parsed = ListResourcesRequestSchema.safeParse(request);
|
|
96
|
+
if (!parsed.success) {
|
|
97
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
98
|
+
}
|
|
99
|
+
return c.json(jsonrpcResult(id, { resources: mcpManager.listResources() }));
|
|
100
|
+
}
|
|
101
|
+
if (method === "resources/read") {
|
|
102
|
+
const parsed = ReadResourceRequestSchema.safeParse(request);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const result = await mcpManager.readResource(parsed.data.params);
|
|
108
|
+
return c.json(jsonrpcResult(id, result));
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return c.json(jsonrpcError(id, ErrorCode.InternalError, error instanceof Error ? error.message : "Internal error"));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (method === "prompts/list") {
|
|
115
|
+
const parsed = ListPromptsRequestSchema.safeParse(request);
|
|
116
|
+
if (!parsed.success) {
|
|
117
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
118
|
+
}
|
|
119
|
+
return c.json(jsonrpcResult(id, { prompts: mcpManager.listPrompts() }));
|
|
120
|
+
}
|
|
121
|
+
if (method === "prompts/get") {
|
|
122
|
+
const parsed = GetPromptRequestSchema.safeParse(request);
|
|
123
|
+
if (!parsed.success) {
|
|
124
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const result = await mcpManager.getPrompt(parsed.data.params);
|
|
128
|
+
return c.json(jsonrpcResult(id, result));
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return c.json(jsonrpcError(id, ErrorCode.InternalError, error instanceof Error ? error.message : "Internal error"));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (method === "completion/complete") {
|
|
135
|
+
const parsed = CompleteRequestSchema.safeParse(request);
|
|
136
|
+
if (!parsed.success) {
|
|
137
|
+
return c.json(jsonrpcError(id, ErrorCode.InvalidParams, "Invalid params"));
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const result = await mcpManager.complete(parsed.data.params);
|
|
141
|
+
return c.json(jsonrpcResult(id, result));
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
return c.json(jsonrpcError(id, ErrorCode.InternalError, error instanceof Error ? error.message : "Internal error"));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return c.json(jsonrpcError(id, ErrorCode.MethodNotFound, `Method not found: ${method}`));
|
|
148
|
+
}
|
package/dist/mcp/manager.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
2
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
-
import type { CallToolResult, CompleteResult, GetPromptResult, Prompt, ReadResourceResult, Resource, Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { CallToolRequestParams, CallToolResult, CompleteRequestParams, CompleteResult, GetPromptRequestParams, GetPromptResult, Prompt, ReadResourceRequestParams, ReadResourceResult, Resource, Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import type { LogConfig, McpConfig } from "../config/types.js";
|
|
5
5
|
export interface ManagedMcp {
|
|
6
6
|
name: string;
|
|
@@ -33,16 +33,9 @@ export declare class McpManager {
|
|
|
33
33
|
prompts: number;
|
|
34
34
|
}>;
|
|
35
35
|
}>;
|
|
36
|
-
callTool(
|
|
37
|
-
readResource(
|
|
38
|
-
getPrompt(
|
|
39
|
-
complete(
|
|
40
|
-
type: string;
|
|
41
|
-
name?: string;
|
|
42
|
-
uri?: string;
|
|
43
|
-
}, argument: {
|
|
44
|
-
name: string;
|
|
45
|
-
value: string;
|
|
46
|
-
}): Promise<CompleteResult>;
|
|
36
|
+
callTool(params: CallToolRequestParams): Promise<CallToolResult>;
|
|
37
|
+
readResource(params: ReadResourceRequestParams): Promise<ReadResourceResult>;
|
|
38
|
+
getPrompt(params: GetPromptRequestParams): Promise<GetPromptResult>;
|
|
39
|
+
complete(params: CompleteRequestParams): Promise<CompleteResult>;
|
|
47
40
|
private resolveCompletionRef;
|
|
48
41
|
}
|
package/dist/mcp/manager.js
CHANGED
|
@@ -217,91 +217,94 @@ export class McpManager {
|
|
|
217
217
|
}
|
|
218
218
|
return { servers };
|
|
219
219
|
}
|
|
220
|
-
async callTool(
|
|
221
|
-
const mcpName = this.toolToMcp.get(
|
|
220
|
+
async callTool(params) {
|
|
221
|
+
const mcpName = this.toolToMcp.get(params.name);
|
|
222
222
|
if (!mcpName) {
|
|
223
|
-
logger.warn(`Unknown tool called: ${
|
|
224
|
-
throw new Error(`Unknown tool: ${
|
|
223
|
+
logger.warn(`Unknown tool called: ${params.name}`);
|
|
224
|
+
throw new Error(`Unknown tool: ${params.name}`);
|
|
225
225
|
}
|
|
226
226
|
const mcp = this.mcps.get(mcpName);
|
|
227
227
|
if (!mcp) {
|
|
228
|
-
logger.error({ mcpName }, `MCP not found for tool: ${
|
|
228
|
+
logger.error({ mcpName }, `MCP not found for tool: ${params.name}`);
|
|
229
229
|
throw new Error(`MCP not found: ${mcpName}`);
|
|
230
230
|
}
|
|
231
231
|
// Strip namespace prefix to get original tool name
|
|
232
232
|
const originalName = this.useNamespacing
|
|
233
|
-
? stripNamespace(mcpName,
|
|
234
|
-
:
|
|
235
|
-
logger.info({
|
|
233
|
+
? stripNamespace(mcpName, params.name)
|
|
234
|
+
: params.name;
|
|
235
|
+
logger.info({ arguments: params.arguments }, `Tool call: ${params.name}`);
|
|
236
236
|
const startTime = Date.now();
|
|
237
237
|
const result = await mcp.client.callTool({
|
|
238
238
|
name: originalName,
|
|
239
|
-
arguments:
|
|
239
|
+
arguments: params.arguments,
|
|
240
240
|
});
|
|
241
241
|
const duration = Date.now() - startTime;
|
|
242
242
|
logger.info({
|
|
243
243
|
duration: `${duration}ms`,
|
|
244
244
|
isError: result.isError ?? false,
|
|
245
|
-
}, `Tool result: ${
|
|
245
|
+
}, `Tool result: ${params.name}`);
|
|
246
246
|
return result;
|
|
247
247
|
}
|
|
248
|
-
async readResource(
|
|
249
|
-
const mcpName = this.resourceToMcp.get(
|
|
248
|
+
async readResource(params) {
|
|
249
|
+
const mcpName = this.resourceToMcp.get(params.uri);
|
|
250
250
|
if (!mcpName) {
|
|
251
|
-
logger.warn(`Unknown resource: ${
|
|
252
|
-
throw new Error(`Unknown resource: ${
|
|
251
|
+
logger.warn(`Unknown resource: ${params.uri}`);
|
|
252
|
+
throw new Error(`Unknown resource: ${params.uri}`);
|
|
253
253
|
}
|
|
254
254
|
const mcp = this.mcps.get(mcpName);
|
|
255
255
|
if (!mcp) {
|
|
256
|
-
logger.error({ mcpName }, `MCP not found for resource: ${
|
|
256
|
+
logger.error({ mcpName }, `MCP not found for resource: ${params.uri}`);
|
|
257
257
|
throw new Error(`MCP not found: ${mcpName}`);
|
|
258
258
|
}
|
|
259
259
|
// Strip namespace prefix to get original URI
|
|
260
260
|
const originalUri = this.useNamespacing
|
|
261
|
-
? stripNamespace(mcpName,
|
|
262
|
-
:
|
|
263
|
-
logger.info(`Resource read: ${
|
|
261
|
+
? stripNamespace(mcpName, params.uri)
|
|
262
|
+
: params.uri;
|
|
263
|
+
logger.info(`Resource read: ${params.uri}`);
|
|
264
264
|
const startTime = Date.now();
|
|
265
265
|
const result = await mcp.client.readResource({ uri: originalUri });
|
|
266
266
|
const duration = Date.now() - startTime;
|
|
267
|
-
logger.info({ duration: `${duration}ms` }, `Resource result: ${
|
|
267
|
+
logger.info({ duration: `${duration}ms` }, `Resource result: ${params.uri}`);
|
|
268
268
|
return result;
|
|
269
269
|
}
|
|
270
|
-
async getPrompt(
|
|
271
|
-
const mcpName = this.promptToMcp.get(
|
|
270
|
+
async getPrompt(params) {
|
|
271
|
+
const mcpName = this.promptToMcp.get(params.name);
|
|
272
272
|
if (!mcpName) {
|
|
273
|
-
logger.warn(`Unknown prompt: ${
|
|
274
|
-
throw new Error(`Unknown prompt: ${
|
|
273
|
+
logger.warn(`Unknown prompt: ${params.name}`);
|
|
274
|
+
throw new Error(`Unknown prompt: ${params.name}`);
|
|
275
275
|
}
|
|
276
276
|
const mcp = this.mcps.get(mcpName);
|
|
277
277
|
if (!mcp) {
|
|
278
|
-
logger.error({ mcpName }, `MCP not found for prompt: ${
|
|
278
|
+
logger.error({ mcpName }, `MCP not found for prompt: ${params.name}`);
|
|
279
279
|
throw new Error(`MCP not found: ${mcpName}`);
|
|
280
280
|
}
|
|
281
281
|
// Strip namespace prefix to get original name
|
|
282
282
|
const originalName = this.useNamespacing
|
|
283
|
-
? stripNamespace(mcpName,
|
|
284
|
-
:
|
|
285
|
-
logger.info({
|
|
283
|
+
? stripNamespace(mcpName, params.name)
|
|
284
|
+
: params.name;
|
|
285
|
+
logger.info({ arguments: params.arguments }, `Prompt get: ${params.name}`);
|
|
286
286
|
const startTime = Date.now();
|
|
287
287
|
const result = await mcp.client.getPrompt({
|
|
288
288
|
name: originalName,
|
|
289
|
-
arguments:
|
|
289
|
+
arguments: params.arguments,
|
|
290
290
|
});
|
|
291
291
|
const duration = Date.now() - startTime;
|
|
292
|
-
logger.info({ duration: `${duration}ms` }, `Prompt result: ${
|
|
292
|
+
logger.info({ duration: `${duration}ms` }, `Prompt result: ${params.name}`);
|
|
293
293
|
return result;
|
|
294
294
|
}
|
|
295
|
-
async complete(
|
|
296
|
-
const { mcpName, originalRef } = this.resolveCompletionRef(ref);
|
|
295
|
+
async complete(params) {
|
|
296
|
+
const { mcpName, originalRef } = this.resolveCompletionRef(params.ref);
|
|
297
297
|
const mcp = this.mcps.get(mcpName);
|
|
298
298
|
if (!mcp) {
|
|
299
299
|
logger.error({ mcpName }, "MCP not found for completion");
|
|
300
300
|
throw new Error(`MCP not found: ${mcpName}`);
|
|
301
301
|
}
|
|
302
|
-
logger.info({ ref, argument }, "Completion request");
|
|
302
|
+
logger.info({ ref: params.ref, argument: params.argument }, "Completion request");
|
|
303
303
|
const startTime = Date.now();
|
|
304
|
-
const result = await mcp.client.complete({
|
|
304
|
+
const result = await mcp.client.complete({
|
|
305
|
+
ref: originalRef,
|
|
306
|
+
argument: params.argument,
|
|
307
|
+
});
|
|
305
308
|
const duration = Date.now() - startTime;
|
|
306
309
|
logger.info({ duration: `${duration}ms` }, "Completion result");
|
|
307
310
|
return result;
|
package/dist/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { OAuthServer } from "./auth/oauth.js";
|
|
|
7
7
|
import { GitHubIdentityProvider } from "./auth/providers/github.js";
|
|
8
8
|
import { LocalIdentityProvider } from "./auth/providers/local.js";
|
|
9
9
|
import { logger } from "./logger.js";
|
|
10
|
-
import {
|
|
10
|
+
import { createMcpHandler } from "./mcp/handler.js";
|
|
11
11
|
import { McpManager } from "./mcp/manager.js";
|
|
12
12
|
import { MemoryStore } from "./storage/memory.js";
|
|
13
13
|
import { SqliteStore } from "./storage/sqlite.js";
|
|
@@ -150,67 +150,15 @@ export async function createServer(config) {
|
|
|
150
150
|
}
|
|
151
151
|
return next();
|
|
152
152
|
});
|
|
153
|
-
// MCP handler
|
|
154
|
-
const handleMcp = async (c) => {
|
|
155
|
-
let message;
|
|
156
|
-
try {
|
|
157
|
-
message = await c.req.json();
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
logger.warn({ error: e instanceof Error ? e.message : String(e) }, "MCP parse error");
|
|
161
|
-
return c.json({
|
|
162
|
-
jsonrpc: "2.0",
|
|
163
|
-
error: { code: -32700, message: "Parse error" },
|
|
164
|
-
id: null,
|
|
165
|
-
}, 400);
|
|
166
|
-
}
|
|
167
|
-
const method = "method" in message ? message.method : undefined;
|
|
168
|
-
const id = "id" in message ? message.id : undefined;
|
|
169
|
-
logger.debug({ method, id }, "MCP request");
|
|
170
|
-
if (method === "initialize") {
|
|
171
|
-
return handleInitialize(c, message);
|
|
172
|
-
}
|
|
173
|
-
if (method === "notifications/initialized") {
|
|
174
|
-
return handleInitialized(c);
|
|
175
|
-
}
|
|
176
|
-
if (method === "tools/list") {
|
|
177
|
-
return handleToolsList(c, message, mcpManager);
|
|
178
|
-
}
|
|
179
|
-
if (method === "tools/call") {
|
|
180
|
-
const params = message.params;
|
|
181
|
-
return handleToolsCall(c, message, mcpManager, params);
|
|
182
|
-
}
|
|
183
|
-
if (method === "resources/list") {
|
|
184
|
-
return handleResourcesList(c, message, mcpManager);
|
|
185
|
-
}
|
|
186
|
-
if (method === "resources/read") {
|
|
187
|
-
const params = message.params;
|
|
188
|
-
return handleResourcesRead(c, message, mcpManager, params);
|
|
189
|
-
}
|
|
190
|
-
if (method === "prompts/list") {
|
|
191
|
-
return handlePromptsList(c, message, mcpManager);
|
|
192
|
-
}
|
|
193
|
-
if (method === "prompts/get") {
|
|
194
|
-
const params = message.params;
|
|
195
|
-
return handlePromptsGet(c, message, mcpManager, params);
|
|
196
|
-
}
|
|
197
|
-
if (method === "ping") {
|
|
198
|
-
return handlePing(c, message);
|
|
199
|
-
}
|
|
200
|
-
if (method === "completion/complete") {
|
|
201
|
-
const params = message.params;
|
|
202
|
-
return handleCompletionComplete(c, message, mcpManager, params);
|
|
203
|
-
}
|
|
204
|
-
return handleMethodNotFound(c, message, method ?? "unknown");
|
|
205
|
-
};
|
|
206
153
|
// Status endpoint (protected)
|
|
207
154
|
protectedRoutes.get("/status", async (c) => {
|
|
208
155
|
const health = await mcpManager.checkHealth();
|
|
209
156
|
return c.json({ servers: health.servers });
|
|
210
157
|
});
|
|
211
158
|
// MCP endpoints (protected)
|
|
212
|
-
|
|
213
|
-
protectedRoutes.post("/
|
|
159
|
+
const mcp = createMcpHandler(mcpManager);
|
|
160
|
+
protectedRoutes.post("/", mcp);
|
|
161
|
+
protectedRoutes.post("/mcp", mcp);
|
|
214
162
|
// Mount protected routes
|
|
215
163
|
app.route("/", protectedRoutes);
|
|
216
164
|
// 404 for other routes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpbox",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A lightweight gateway that exposes local stdio-based MCP servers via Streamable HTTP",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@hono/node-server": "^1.19.9",
|
|
53
53
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
54
|
-
"bcryptjs": "^
|
|
54
|
+
"bcryptjs": "^3.0.0",
|
|
55
55
|
"hono": "^4.11.7",
|
|
56
56
|
"pino": "^10.3.0",
|
|
57
57
|
"pino-pretty": "^13.1.3",
|
|
@@ -59,7 +59,6 @@
|
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@biomejs/biome": "^2.3.14",
|
|
62
|
-
"@types/bcryptjs": "^2.4.6",
|
|
63
62
|
"@types/node": "^25.1.0",
|
|
64
63
|
"tsx": "^4.21.0",
|
|
65
64
|
"typescript": "^5.9.3"
|
package/dist/mcp/handlers.d.ts
DELETED
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
-
import type { Context } from "hono";
|
|
3
|
-
import type { McpManager } from "./manager.js";
|
|
4
|
-
export declare function handleInitialize(c: Context, message: JSONRPCMessage): Response & import("hono").TypedResponse<{
|
|
5
|
-
jsonrpc: string;
|
|
6
|
-
id: string | number | undefined;
|
|
7
|
-
result: {
|
|
8
|
-
protocolVersion: string;
|
|
9
|
-
capabilities: {
|
|
10
|
-
tools: {
|
|
11
|
-
listChanged: true;
|
|
12
|
-
};
|
|
13
|
-
resources: {
|
|
14
|
-
listChanged: true;
|
|
15
|
-
};
|
|
16
|
-
prompts: {
|
|
17
|
-
listChanged: true;
|
|
18
|
-
};
|
|
19
|
-
completions: {};
|
|
20
|
-
};
|
|
21
|
-
serverInfo: {
|
|
22
|
-
name: string;
|
|
23
|
-
version: string;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">;
|
|
27
|
-
export declare function handleInitialized(c: Context): Response & import("hono").TypedResponse<null, 202, "body">;
|
|
28
|
-
export declare function handleToolsList(c: Context, message: JSONRPCMessage, mcpManager: McpManager): Response & import("hono").TypedResponse<{
|
|
29
|
-
jsonrpc: string;
|
|
30
|
-
id: string | number | undefined;
|
|
31
|
-
result: {
|
|
32
|
-
tools: {
|
|
33
|
-
inputSchema: {
|
|
34
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
35
|
-
type: "object";
|
|
36
|
-
properties?: {
|
|
37
|
-
[x: string]: never;
|
|
38
|
-
} | undefined;
|
|
39
|
-
required?: string[] | undefined;
|
|
40
|
-
};
|
|
41
|
-
name: string;
|
|
42
|
-
description?: string | undefined;
|
|
43
|
-
outputSchema?: {
|
|
44
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
45
|
-
type: "object";
|
|
46
|
-
properties?: {
|
|
47
|
-
[x: string]: never;
|
|
48
|
-
} | undefined;
|
|
49
|
-
required?: string[] | undefined;
|
|
50
|
-
} | undefined;
|
|
51
|
-
annotations?: {
|
|
52
|
-
title?: string | undefined;
|
|
53
|
-
readOnlyHint?: boolean | undefined;
|
|
54
|
-
destructiveHint?: boolean | undefined;
|
|
55
|
-
idempotentHint?: boolean | undefined;
|
|
56
|
-
openWorldHint?: boolean | undefined;
|
|
57
|
-
} | undefined;
|
|
58
|
-
execution?: {
|
|
59
|
-
taskSupport?: "optional" | "required" | "forbidden" | undefined;
|
|
60
|
-
} | undefined;
|
|
61
|
-
_meta?: {
|
|
62
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
63
|
-
} | undefined;
|
|
64
|
-
icons?: {
|
|
65
|
-
src: string;
|
|
66
|
-
mimeType?: string | undefined;
|
|
67
|
-
sizes?: string[] | undefined;
|
|
68
|
-
theme?: "light" | "dark" | undefined;
|
|
69
|
-
}[] | undefined;
|
|
70
|
-
title?: string | undefined;
|
|
71
|
-
}[];
|
|
72
|
-
};
|
|
73
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">;
|
|
74
|
-
export declare function handleToolsCall(c: Context, message: JSONRPCMessage, mcpManager: McpManager, params: {
|
|
75
|
-
name: string;
|
|
76
|
-
arguments?: Record<string, unknown>;
|
|
77
|
-
}): Promise<(Response & import("hono").TypedResponse<{
|
|
78
|
-
jsonrpc: string;
|
|
79
|
-
id: string | number | undefined;
|
|
80
|
-
result: {
|
|
81
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
82
|
-
content: ({
|
|
83
|
-
type: "text";
|
|
84
|
-
text: string;
|
|
85
|
-
annotations?: {
|
|
86
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
87
|
-
priority?: number | undefined;
|
|
88
|
-
lastModified?: string | undefined;
|
|
89
|
-
} | undefined;
|
|
90
|
-
_meta?: {
|
|
91
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
92
|
-
} | undefined;
|
|
93
|
-
} | {
|
|
94
|
-
type: "image";
|
|
95
|
-
data: string;
|
|
96
|
-
mimeType: string;
|
|
97
|
-
annotations?: {
|
|
98
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
99
|
-
priority?: number | undefined;
|
|
100
|
-
lastModified?: string | undefined;
|
|
101
|
-
} | undefined;
|
|
102
|
-
_meta?: {
|
|
103
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
104
|
-
} | undefined;
|
|
105
|
-
} | {
|
|
106
|
-
type: "audio";
|
|
107
|
-
data: string;
|
|
108
|
-
mimeType: string;
|
|
109
|
-
annotations?: {
|
|
110
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
111
|
-
priority?: number | undefined;
|
|
112
|
-
lastModified?: string | undefined;
|
|
113
|
-
} | undefined;
|
|
114
|
-
_meta?: {
|
|
115
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
116
|
-
} | undefined;
|
|
117
|
-
} | {
|
|
118
|
-
uri: string;
|
|
119
|
-
name: string;
|
|
120
|
-
type: "resource_link";
|
|
121
|
-
description?: string | undefined;
|
|
122
|
-
mimeType?: string | undefined;
|
|
123
|
-
annotations?: {
|
|
124
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
125
|
-
priority?: number | undefined;
|
|
126
|
-
lastModified?: string | undefined;
|
|
127
|
-
} | undefined;
|
|
128
|
-
_meta?: {
|
|
129
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
130
|
-
} | undefined;
|
|
131
|
-
icons?: {
|
|
132
|
-
src: string;
|
|
133
|
-
mimeType?: string | undefined;
|
|
134
|
-
sizes?: string[] | undefined;
|
|
135
|
-
theme?: "light" | "dark" | undefined;
|
|
136
|
-
}[] | undefined;
|
|
137
|
-
title?: string | undefined;
|
|
138
|
-
} | {
|
|
139
|
-
type: "resource";
|
|
140
|
-
resource: {
|
|
141
|
-
uri: string;
|
|
142
|
-
text: string;
|
|
143
|
-
mimeType?: string | undefined;
|
|
144
|
-
_meta?: {
|
|
145
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
146
|
-
} | undefined;
|
|
147
|
-
} | {
|
|
148
|
-
uri: string;
|
|
149
|
-
blob: string;
|
|
150
|
-
mimeType?: string | undefined;
|
|
151
|
-
_meta?: {
|
|
152
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
153
|
-
} | undefined;
|
|
154
|
-
};
|
|
155
|
-
annotations?: {
|
|
156
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
157
|
-
priority?: number | undefined;
|
|
158
|
-
lastModified?: string | undefined;
|
|
159
|
-
} | undefined;
|
|
160
|
-
_meta?: {
|
|
161
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
162
|
-
} | undefined;
|
|
163
|
-
})[];
|
|
164
|
-
_meta?: {
|
|
165
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
166
|
-
progressToken?: string | number | undefined;
|
|
167
|
-
"io.modelcontextprotocol/related-task"?: {
|
|
168
|
-
taskId: string;
|
|
169
|
-
} | undefined;
|
|
170
|
-
} | undefined;
|
|
171
|
-
structuredContent?: {
|
|
172
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
173
|
-
} | undefined;
|
|
174
|
-
isError?: boolean | undefined;
|
|
175
|
-
};
|
|
176
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
177
|
-
jsonrpc: string;
|
|
178
|
-
id: string | number | undefined;
|
|
179
|
-
error: {
|
|
180
|
-
code: number;
|
|
181
|
-
message: string;
|
|
182
|
-
};
|
|
183
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
184
|
-
export declare function handlePing(c: Context, message: JSONRPCMessage): Response & import("hono").TypedResponse<{
|
|
185
|
-
jsonrpc: string;
|
|
186
|
-
id: string | number | undefined;
|
|
187
|
-
result: {};
|
|
188
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">;
|
|
189
|
-
export declare function handleResourcesList(c: Context, message: JSONRPCMessage, mcpManager: McpManager): Response & import("hono").TypedResponse<{
|
|
190
|
-
jsonrpc: string;
|
|
191
|
-
id: string | number | undefined;
|
|
192
|
-
result: {
|
|
193
|
-
resources: {
|
|
194
|
-
uri: string;
|
|
195
|
-
name: string;
|
|
196
|
-
description?: string | undefined;
|
|
197
|
-
mimeType?: string | undefined;
|
|
198
|
-
annotations?: {
|
|
199
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
200
|
-
priority?: number | undefined;
|
|
201
|
-
lastModified?: string | undefined;
|
|
202
|
-
} | undefined;
|
|
203
|
-
_meta?: {
|
|
204
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
205
|
-
} | undefined;
|
|
206
|
-
icons?: {
|
|
207
|
-
src: string;
|
|
208
|
-
mimeType?: string | undefined;
|
|
209
|
-
sizes?: string[] | undefined;
|
|
210
|
-
theme?: "light" | "dark" | undefined;
|
|
211
|
-
}[] | undefined;
|
|
212
|
-
title?: string | undefined;
|
|
213
|
-
}[];
|
|
214
|
-
};
|
|
215
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">;
|
|
216
|
-
export declare function handleResourcesRead(c: Context, message: JSONRPCMessage, mcpManager: McpManager, params: {
|
|
217
|
-
uri: string;
|
|
218
|
-
}): Promise<(Response & import("hono").TypedResponse<{
|
|
219
|
-
jsonrpc: string;
|
|
220
|
-
id: string | number | undefined;
|
|
221
|
-
result: {
|
|
222
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
223
|
-
contents: ({
|
|
224
|
-
uri: string;
|
|
225
|
-
text: string;
|
|
226
|
-
mimeType?: string | undefined;
|
|
227
|
-
_meta?: {
|
|
228
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
229
|
-
} | undefined;
|
|
230
|
-
} | {
|
|
231
|
-
uri: string;
|
|
232
|
-
blob: string;
|
|
233
|
-
mimeType?: string | undefined;
|
|
234
|
-
_meta?: {
|
|
235
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
236
|
-
} | undefined;
|
|
237
|
-
})[];
|
|
238
|
-
_meta?: {
|
|
239
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
240
|
-
progressToken?: string | number | undefined;
|
|
241
|
-
"io.modelcontextprotocol/related-task"?: {
|
|
242
|
-
taskId: string;
|
|
243
|
-
} | undefined;
|
|
244
|
-
} | undefined;
|
|
245
|
-
};
|
|
246
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
247
|
-
jsonrpc: string;
|
|
248
|
-
id: string | number | undefined;
|
|
249
|
-
error: {
|
|
250
|
-
code: number;
|
|
251
|
-
message: string;
|
|
252
|
-
};
|
|
253
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
254
|
-
export declare function handlePromptsList(c: Context, message: JSONRPCMessage, mcpManager: McpManager): Response & import("hono").TypedResponse<{
|
|
255
|
-
jsonrpc: string;
|
|
256
|
-
id: string | number | undefined;
|
|
257
|
-
result: {
|
|
258
|
-
prompts: {
|
|
259
|
-
name: string;
|
|
260
|
-
description?: string | undefined;
|
|
261
|
-
arguments?: {
|
|
262
|
-
name: string;
|
|
263
|
-
description?: string | undefined;
|
|
264
|
-
required?: boolean | undefined;
|
|
265
|
-
}[] | undefined;
|
|
266
|
-
_meta?: {
|
|
267
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
268
|
-
} | undefined;
|
|
269
|
-
icons?: {
|
|
270
|
-
src: string;
|
|
271
|
-
mimeType?: string | undefined;
|
|
272
|
-
sizes?: string[] | undefined;
|
|
273
|
-
theme?: "light" | "dark" | undefined;
|
|
274
|
-
}[] | undefined;
|
|
275
|
-
title?: string | undefined;
|
|
276
|
-
}[];
|
|
277
|
-
};
|
|
278
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">;
|
|
279
|
-
export declare function handlePromptsGet(c: Context, message: JSONRPCMessage, mcpManager: McpManager, params: {
|
|
280
|
-
name: string;
|
|
281
|
-
arguments?: Record<string, string>;
|
|
282
|
-
}): Promise<(Response & import("hono").TypedResponse<{
|
|
283
|
-
jsonrpc: string;
|
|
284
|
-
id: string | number | undefined;
|
|
285
|
-
result: {
|
|
286
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
287
|
-
messages: {
|
|
288
|
-
role: "user" | "assistant";
|
|
289
|
-
content: {
|
|
290
|
-
type: "text";
|
|
291
|
-
text: string;
|
|
292
|
-
annotations?: {
|
|
293
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
294
|
-
priority?: number | undefined;
|
|
295
|
-
lastModified?: string | undefined;
|
|
296
|
-
} | undefined;
|
|
297
|
-
_meta?: {
|
|
298
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
299
|
-
} | undefined;
|
|
300
|
-
} | {
|
|
301
|
-
type: "image";
|
|
302
|
-
data: string;
|
|
303
|
-
mimeType: string;
|
|
304
|
-
annotations?: {
|
|
305
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
306
|
-
priority?: number | undefined;
|
|
307
|
-
lastModified?: string | undefined;
|
|
308
|
-
} | undefined;
|
|
309
|
-
_meta?: {
|
|
310
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
311
|
-
} | undefined;
|
|
312
|
-
} | {
|
|
313
|
-
type: "audio";
|
|
314
|
-
data: string;
|
|
315
|
-
mimeType: string;
|
|
316
|
-
annotations?: {
|
|
317
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
318
|
-
priority?: number | undefined;
|
|
319
|
-
lastModified?: string | undefined;
|
|
320
|
-
} | undefined;
|
|
321
|
-
_meta?: {
|
|
322
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
323
|
-
} | undefined;
|
|
324
|
-
} | {
|
|
325
|
-
uri: string;
|
|
326
|
-
name: string;
|
|
327
|
-
type: "resource_link";
|
|
328
|
-
description?: string | undefined;
|
|
329
|
-
mimeType?: string | undefined;
|
|
330
|
-
annotations?: {
|
|
331
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
332
|
-
priority?: number | undefined;
|
|
333
|
-
lastModified?: string | undefined;
|
|
334
|
-
} | undefined;
|
|
335
|
-
_meta?: {
|
|
336
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
337
|
-
} | undefined;
|
|
338
|
-
icons?: {
|
|
339
|
-
src: string;
|
|
340
|
-
mimeType?: string | undefined;
|
|
341
|
-
sizes?: string[] | undefined;
|
|
342
|
-
theme?: "light" | "dark" | undefined;
|
|
343
|
-
}[] | undefined;
|
|
344
|
-
title?: string | undefined;
|
|
345
|
-
} | {
|
|
346
|
-
type: "resource";
|
|
347
|
-
resource: {
|
|
348
|
-
uri: string;
|
|
349
|
-
text: string;
|
|
350
|
-
mimeType?: string | undefined;
|
|
351
|
-
_meta?: {
|
|
352
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
353
|
-
} | undefined;
|
|
354
|
-
} | {
|
|
355
|
-
uri: string;
|
|
356
|
-
blob: string;
|
|
357
|
-
mimeType?: string | undefined;
|
|
358
|
-
_meta?: {
|
|
359
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
360
|
-
} | undefined;
|
|
361
|
-
};
|
|
362
|
-
annotations?: {
|
|
363
|
-
audience?: ("user" | "assistant")[] | undefined;
|
|
364
|
-
priority?: number | undefined;
|
|
365
|
-
lastModified?: string | undefined;
|
|
366
|
-
} | undefined;
|
|
367
|
-
_meta?: {
|
|
368
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
369
|
-
} | undefined;
|
|
370
|
-
};
|
|
371
|
-
}[];
|
|
372
|
-
_meta?: {
|
|
373
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
374
|
-
progressToken?: string | number | undefined;
|
|
375
|
-
"io.modelcontextprotocol/related-task"?: {
|
|
376
|
-
taskId: string;
|
|
377
|
-
} | undefined;
|
|
378
|
-
} | undefined;
|
|
379
|
-
description?: string | undefined;
|
|
380
|
-
};
|
|
381
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
382
|
-
jsonrpc: string;
|
|
383
|
-
id: string | number | undefined;
|
|
384
|
-
error: {
|
|
385
|
-
code: number;
|
|
386
|
-
message: string;
|
|
387
|
-
};
|
|
388
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
389
|
-
export declare function handleCompletionComplete(c: Context, message: JSONRPCMessage, mcpManager: McpManager, params: {
|
|
390
|
-
ref: {
|
|
391
|
-
type: string;
|
|
392
|
-
name?: string;
|
|
393
|
-
uri?: string;
|
|
394
|
-
};
|
|
395
|
-
argument: {
|
|
396
|
-
name: string;
|
|
397
|
-
value: string;
|
|
398
|
-
};
|
|
399
|
-
}): Promise<(Response & import("hono").TypedResponse<{
|
|
400
|
-
jsonrpc: string;
|
|
401
|
-
id: string | number | undefined;
|
|
402
|
-
result: {
|
|
403
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
404
|
-
completion: {
|
|
405
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
406
|
-
values: string[];
|
|
407
|
-
total?: number | undefined;
|
|
408
|
-
hasMore?: boolean | undefined;
|
|
409
|
-
};
|
|
410
|
-
_meta?: {
|
|
411
|
-
[x: string]: import("hono/utils/types").JSONValue;
|
|
412
|
-
progressToken?: string | number | undefined;
|
|
413
|
-
"io.modelcontextprotocol/related-task"?: {
|
|
414
|
-
taskId: string;
|
|
415
|
-
} | undefined;
|
|
416
|
-
} | undefined;
|
|
417
|
-
};
|
|
418
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
419
|
-
jsonrpc: string;
|
|
420
|
-
id: string | number | undefined;
|
|
421
|
-
error: {
|
|
422
|
-
code: number;
|
|
423
|
-
message: string;
|
|
424
|
-
};
|
|
425
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
426
|
-
export declare function handleMethodNotFound(c: Context, message: JSONRPCMessage, method: string): Response & import("hono").TypedResponse<{
|
|
427
|
-
jsonrpc: string;
|
|
428
|
-
id: string | number | null;
|
|
429
|
-
error: {
|
|
430
|
-
code: number;
|
|
431
|
-
message: string;
|
|
432
|
-
};
|
|
433
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">;
|
package/dist/mcp/handlers.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { NAME, VERSION } from "../version.js";
|
|
2
|
-
function getMessageId(message) {
|
|
3
|
-
return "id" in message ? message.id : undefined;
|
|
4
|
-
}
|
|
5
|
-
export function handleInitialize(c, message) {
|
|
6
|
-
return c.json({
|
|
7
|
-
jsonrpc: "2.0",
|
|
8
|
-
id: getMessageId(message),
|
|
9
|
-
result: {
|
|
10
|
-
protocolVersion: "2025-11-25",
|
|
11
|
-
capabilities: {
|
|
12
|
-
tools: { listChanged: true },
|
|
13
|
-
resources: { listChanged: true },
|
|
14
|
-
prompts: { listChanged: true },
|
|
15
|
-
completions: {},
|
|
16
|
-
},
|
|
17
|
-
serverInfo: {
|
|
18
|
-
name: NAME,
|
|
19
|
-
version: VERSION,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
export function handleInitialized(c) {
|
|
25
|
-
return c.body(null, 202);
|
|
26
|
-
}
|
|
27
|
-
export function handleToolsList(c, message, mcpManager) {
|
|
28
|
-
const tools = mcpManager.listTools();
|
|
29
|
-
return c.json({
|
|
30
|
-
jsonrpc: "2.0",
|
|
31
|
-
id: getMessageId(message),
|
|
32
|
-
result: { tools },
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
export async function handleToolsCall(c, message, mcpManager, params) {
|
|
36
|
-
try {
|
|
37
|
-
const result = await mcpManager.callTool(params.name, params.arguments ?? {});
|
|
38
|
-
return c.json({
|
|
39
|
-
jsonrpc: "2.0",
|
|
40
|
-
id: getMessageId(message),
|
|
41
|
-
result,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
return c.json({
|
|
46
|
-
jsonrpc: "2.0",
|
|
47
|
-
id: getMessageId(message),
|
|
48
|
-
error: {
|
|
49
|
-
code: -32603,
|
|
50
|
-
message: error instanceof Error ? error.message : "Internal error",
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
export function handlePing(c, message) {
|
|
56
|
-
return c.json({
|
|
57
|
-
jsonrpc: "2.0",
|
|
58
|
-
id: getMessageId(message),
|
|
59
|
-
result: {},
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
export function handleResourcesList(c, message, mcpManager) {
|
|
63
|
-
const resources = mcpManager.listResources();
|
|
64
|
-
return c.json({
|
|
65
|
-
jsonrpc: "2.0",
|
|
66
|
-
id: getMessageId(message),
|
|
67
|
-
result: { resources },
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
export async function handleResourcesRead(c, message, mcpManager, params) {
|
|
71
|
-
try {
|
|
72
|
-
const result = await mcpManager.readResource(params.uri);
|
|
73
|
-
return c.json({
|
|
74
|
-
jsonrpc: "2.0",
|
|
75
|
-
id: getMessageId(message),
|
|
76
|
-
result,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
return c.json({
|
|
81
|
-
jsonrpc: "2.0",
|
|
82
|
-
id: getMessageId(message),
|
|
83
|
-
error: {
|
|
84
|
-
code: -32603,
|
|
85
|
-
message: error instanceof Error ? error.message : "Internal error",
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
export function handlePromptsList(c, message, mcpManager) {
|
|
91
|
-
const prompts = mcpManager.listPrompts();
|
|
92
|
-
return c.json({
|
|
93
|
-
jsonrpc: "2.0",
|
|
94
|
-
id: getMessageId(message),
|
|
95
|
-
result: { prompts },
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
export async function handlePromptsGet(c, message, mcpManager, params) {
|
|
99
|
-
try {
|
|
100
|
-
const result = await mcpManager.getPrompt(params.name, params.arguments);
|
|
101
|
-
return c.json({
|
|
102
|
-
jsonrpc: "2.0",
|
|
103
|
-
id: getMessageId(message),
|
|
104
|
-
result,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
return c.json({
|
|
109
|
-
jsonrpc: "2.0",
|
|
110
|
-
id: getMessageId(message),
|
|
111
|
-
error: {
|
|
112
|
-
code: -32603,
|
|
113
|
-
message: error instanceof Error ? error.message : "Internal error",
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
export async function handleCompletionComplete(c, message, mcpManager, params) {
|
|
119
|
-
try {
|
|
120
|
-
const result = await mcpManager.complete(params.ref, params.argument);
|
|
121
|
-
return c.json({
|
|
122
|
-
jsonrpc: "2.0",
|
|
123
|
-
id: getMessageId(message),
|
|
124
|
-
result,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
catch (error) {
|
|
128
|
-
return c.json({
|
|
129
|
-
jsonrpc: "2.0",
|
|
130
|
-
id: getMessageId(message),
|
|
131
|
-
error: {
|
|
132
|
-
code: -32603,
|
|
133
|
-
message: error instanceof Error ? error.message : "Internal error",
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
export function handleMethodNotFound(c, message, method) {
|
|
139
|
-
return c.json({
|
|
140
|
-
jsonrpc: "2.0",
|
|
141
|
-
id: getMessageId(message) ?? null,
|
|
142
|
-
error: { code: -32601, message: `Method not found: ${method}` },
|
|
143
|
-
});
|
|
144
|
-
}
|