skybridge 2.0.0-beta.dd3cba3 → 2.0.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/README.md +7 -7
- package/dist/server/app.d.ts +79 -60
- package/dist/server/app.js +61 -70
- package/dist/server/app.js.map +1 -1
- package/dist/server/app.test.js +97 -0
- package/dist/server/app.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +19 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/auth/providers/auth0.d.ts +2 -2
- package/dist/server/auth/providers/auth0.js +13 -8
- package/dist/server/auth/providers/auth0.js.map +1 -1
- package/dist/server/auth/providers/auth0.test.js +1 -1
- package/dist/server/auth/providers/auth0.test.js.map +1 -1
- package/dist/server/auth/providers/authplane.d.ts +2 -2
- package/dist/server/auth/providers/authplane.js.map +1 -1
- package/dist/server/auth/providers/authplane.test.js +10 -7
- package/dist/server/auth/providers/authplane.test.js.map +1 -1
- package/dist/server/auth/providers/clerk.d.ts +2 -2
- package/dist/server/auth/providers/clerk.js.map +1 -1
- package/dist/server/auth/providers/clerk.test.js +1 -1
- package/dist/server/auth/providers/clerk.test.js.map +1 -1
- package/dist/server/auth/providers/custom.d.ts +3 -3
- package/dist/server/auth/providers/custom.js +5 -2
- package/dist/server/auth/providers/custom.js.map +1 -1
- package/dist/server/auth/providers/custom.test.js +13 -10
- package/dist/server/auth/providers/custom.test.js.map +1 -1
- package/dist/server/auth/providers/descope.d.ts +2 -2
- package/dist/server/auth/providers/descope.js.map +1 -1
- package/dist/server/auth/providers/descope.test.js +3 -3
- package/dist/server/auth/providers/descope.test.js.map +1 -1
- package/dist/server/auth/providers/stytch.d.ts +2 -2
- package/dist/server/auth/providers/stytch.js.map +1 -1
- package/dist/server/auth/providers/workos.d.ts +2 -2
- package/dist/server/auth/providers/workos.js.map +1 -1
- package/dist/server/auth/setup.test.js +56 -54
- package/dist/server/auth/setup.test.js.map +1 -1
- package/dist/server/auth-extra.test-d.js +96 -37
- package/dist/server/auth-extra.test-d.js.map +1 -1
- package/dist/server/auth.d.ts +4 -4
- package/dist/server/auth.js +2 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/server/build-manifest.test.js +1 -1
- package/dist/server/build-manifest.test.js.map +1 -1
- package/dist/server/express.d.ts +6 -6
- package/dist/server/express.js +16 -22
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +120 -31
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/index.d.ts +4 -3
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp-errors.test.js +5 -1
- package/dist/server/mcp-errors.test.js.map +1 -1
- package/dist/server/middleware.test.js +184 -135
- package/dist/server/middleware.test.js.map +1 -1
- package/dist/server/register-tool.test.js +8 -4
- package/dist/server/register-tool.test.js.map +1 -1
- package/dist/server/server.d.ts +20 -45
- package/dist/server/server.js +18 -63
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.js +11 -4
- package/dist/server/skills-integration.test.js.map +1 -1
- package/dist/server/tool-extra.test.js +30 -14
- package/dist/server/tool-extra.test.js.map +1 -1
- package/dist/server/view-resource-resolution.test.js +7 -3
- package/dist/server/view-resource-resolution.test.js.map +1 -1
- package/dist/test/utils.d.ts +3 -3
- package/dist/test/utils.js +211 -197
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.js +19 -15
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +15 -12
- package/dist/web/generate-helpers.js +15 -12
- package/dist/web/generate-helpers.js.map +1 -1
- package/package.json +2 -3
- package/dist/server/factory-loader.test.js +0 -81
- package/dist/server/factory-loader.test.js.map +0 -1
- /package/dist/server/{factory-loader.test.d.ts → app.test.d.ts} +0 -0
package/dist/server/express.js
CHANGED
|
@@ -3,28 +3,21 @@ import { toNodeHandler } from "@modelcontextprotocol/node";
|
|
|
3
3
|
import { createMcpHandler, UnsupportedProtocolVersionError, } from "@modelcontextprotocol/server";
|
|
4
4
|
import cors from "cors";
|
|
5
5
|
import express from "express";
|
|
6
|
-
const mcpHandlers = new WeakMap();
|
|
7
6
|
/**
|
|
8
|
-
* The
|
|
9
|
-
* `Response` function that builds a fresh MCP server per request.
|
|
10
|
-
* per app, shared by the Express middleware and the shutdown drain.
|
|
7
|
+
* The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →
|
|
8
|
+
* `Response` function that builds a fresh MCP server per request.
|
|
11
9
|
*
|
|
12
10
|
* @internal
|
|
13
11
|
*/
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
mcpHandlers.set(skybridgeApp, handler);
|
|
26
|
-
}
|
|
27
|
-
return handler;
|
|
12
|
+
export function buildMcpHandler(skybridgeApp) {
|
|
13
|
+
return createMcpHandler(() => skybridgeApp.createServerInstance(), {
|
|
14
|
+
onerror: (error) => {
|
|
15
|
+
if (error instanceof UnsupportedProtocolVersionError) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.error("Error handling MCP request:", error);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
28
21
|
}
|
|
29
22
|
/**
|
|
30
23
|
* Build the bare Express app a {@link Skybridge} instance owns: the instance
|
|
@@ -68,7 +61,8 @@ function defaultErrorHandler(err, _req, res, _next) {
|
|
|
68
61
|
});
|
|
69
62
|
}
|
|
70
63
|
}
|
|
71
|
-
export async function createApp({ app: skybridgeApp, httpServer, errorMiddleware = [], }) {
|
|
64
|
+
export async function createApp({ app: skybridgeApp, httpServer, mcpHandler, errorMiddleware = [], }) {
|
|
65
|
+
await skybridgeApp.ready();
|
|
72
66
|
const app = skybridgeApp.express;
|
|
73
67
|
// Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,
|
|
74
68
|
// so a local const would defeat dead-code elimination of the dev-only imports below.
|
|
@@ -91,13 +85,13 @@ export async function createApp({ app: skybridgeApp, httpServer, errorMiddleware
|
|
|
91
85
|
app.use("/assets", cors());
|
|
92
86
|
app.use("/assets", express.static(assetsPath));
|
|
93
87
|
}
|
|
94
|
-
app.use("/mcp", mcpMiddleware(skybridgeApp));
|
|
88
|
+
app.use("/mcp", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));
|
|
95
89
|
applyMiddlewares(app, errorMiddleware);
|
|
96
90
|
app.use("/mcp", defaultErrorHandler);
|
|
97
91
|
return app;
|
|
98
92
|
}
|
|
99
|
-
const mcpMiddleware = (
|
|
100
|
-
const handler = toNodeHandler(
|
|
93
|
+
const mcpMiddleware = (mcpHandler) => {
|
|
94
|
+
const handler = toNodeHandler(mcpHandler);
|
|
101
95
|
return async (req, res, next) => {
|
|
102
96
|
try {
|
|
103
97
|
// Express strips the mount path from req.url (e.g. "/mcp" becomes "/").
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAEhB,+BAA+B,GAChC,MAAM,8BAA8B,CAAC;AACtC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/server/express.ts"],"names":[],"mappings":"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","sourcesContent":["import type http from \"node:http\";\nimport path from \"node:path\";\nimport { toNodeHandler } from \"@modelcontextprotocol/node\";\nimport {\n createMcpHandler,\n type McpHttpHandler,\n UnsupportedProtocolVersionError,\n} from \"@modelcontextprotocol/server\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport type { Skybridge } from \"./app.js\";\nimport type { JsonOptions } from \"./server.js\";\n\ntype SkybridgeApp = Pick<\n Skybridge,\n \"express\" | \"createServerInstance\" | \"ready\"\n>;\n\n/**\n * The `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →\n * `Response` function that builds a fresh MCP server per request.\n *\n * @internal\n */\nexport function buildMcpHandler(skybridgeApp: SkybridgeApp): McpHttpHandler {\n return createMcpHandler(() => skybridgeApp.createServerInstance(), {\n onerror: (error) => {\n if (error instanceof UnsupportedProtocolVersionError) {\n return;\n }\n console.error(\"Error handling MCP request:\", error);\n },\n });\n}\n\n/**\n * Build the bare Express app a {@link Skybridge} instance owns: the instance\n * plus the built-in `express.json()` body parser, tuned by the `json` config\n * field.\n *\n * @internal\n */\nexport function createBaseApp(json?: JsonOptions): express.Express {\n const app = express();\n app.use(express.json(json));\n return app;\n}\n\nfunction parseControlPort(raw: string | undefined): number | null {\n if (raw === undefined) {\n return null;\n }\n const n = Number.parseInt(raw, 10);\n if (!Number.isFinite(n) || n <= 0 || n >= 65536) {\n return null;\n }\n return n;\n}\n\nfunction applyMiddlewares(\n app: express.Express,\n middlewares: Array<{\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }>,\n): void {\n for (const middleware of middlewares) {\n if (middleware.path) {\n app.use(middleware.path, ...middleware.handlers);\n } else {\n app.use(...middleware.handlers);\n }\n }\n}\n\nfunction defaultErrorHandler(\n err: unknown,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction,\n) {\n console.error(\"Error handling MCP request:\", err);\n if (!res.headersSent) {\n res.status(500).json({\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Internal server error\" },\n id: null,\n });\n }\n}\n\nexport async function createApp({\n app: skybridgeApp,\n httpServer,\n mcpHandler,\n errorMiddleware = [],\n}: {\n app: SkybridgeApp;\n httpServer: http.Server;\n mcpHandler?: McpHttpHandler;\n errorMiddleware?: {\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }[];\n}): Promise<express.Express> {\n await skybridgeApp.ready();\n const app = skybridgeApp.express;\n\n // Read `process.env.NODE_ENV` inline: wrangler/esbuild only substitute the literal expression,\n // so a local const would defeat dead-code elimination of the dev-only imports below.\n if (process.env.NODE_ENV !== \"production\") {\n const { devtoolsStaticServer } = await import(\"@skybridge/devtools\");\n app.use(await devtoolsStaticServer());\n const { viewsDevServer } = await import(\"./viewsDevServer.js\");\n app.use(await viewsDevServer(httpServer));\n\n const controlPort = parseControlPort(process.env.__TUNNEL_CONTROL_PORT);\n if (controlPort !== null) {\n const { createTunnelProxyRouter } = await import(\n \"./tunnel-proxy-router.js\"\n );\n app.use(createTunnelProxyRouter(controlPort));\n } else if (process.env.__TUNNEL_CONTROL_PORT !== undefined) {\n console.warn(\n `Ignoring invalid __TUNNEL_CONTROL_PORT=${process.env.__TUNNEL_CONTROL_PORT}`,\n );\n }\n } else {\n const assetsPath = path.join(process.cwd(), \"dist\", \"assets\");\n\n app.use(\"/assets\", cors());\n app.use(\"/assets\", express.static(assetsPath));\n }\n\n app.use(\"/mcp\", mcpMiddleware(mcpHandler ?? buildMcpHandler(skybridgeApp)));\n\n applyMiddlewares(app, errorMiddleware);\n\n app.use(\"/mcp\", defaultErrorHandler);\n\n return app;\n}\n\nconst mcpMiddleware = (mcpHandler: McpHttpHandler): express.RequestHandler => {\n const handler = toNodeHandler(mcpHandler);\n\n return async (\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n ) => {\n try {\n // Express strips the mount path from req.url (e.g. \"/mcp\" becomes \"/\").\n // Restore it so the SDK builds the correct request URL.\n req.url = req.originalUrl;\n await handler(req, res, req.body);\n } catch (error) {\n next(error);\n }\n };\n};\n"]}
|
|
@@ -28,14 +28,22 @@ async function postApi(port) {
|
|
|
28
28
|
}
|
|
29
29
|
describe("Skybridge.express", () => {
|
|
30
30
|
it("exposes a ready Express app immediately after construction", () => {
|
|
31
|
-
const app = new Skybridge({
|
|
31
|
+
const app = new Skybridge({
|
|
32
|
+
name: "t",
|
|
33
|
+
version: "0.0.0",
|
|
34
|
+
handler: (server) => server,
|
|
35
|
+
});
|
|
32
36
|
expect(app.express).toBeDefined();
|
|
33
37
|
expect(typeof app.express.use).toBe("function");
|
|
34
38
|
expect(typeof app.express.get).toBe("function");
|
|
35
39
|
});
|
|
36
40
|
it("app.express.get registers a route reachable alongside /mcp", async () => {
|
|
37
41
|
const { createApp } = await import("./express.js");
|
|
38
|
-
const app = new Skybridge({
|
|
42
|
+
const app = new Skybridge({
|
|
43
|
+
name: "t",
|
|
44
|
+
version: "0.0.0",
|
|
45
|
+
handler: (server) => server,
|
|
46
|
+
});
|
|
39
47
|
app.express.get("/health", (_req, res) => {
|
|
40
48
|
res.json({ status: "ok" });
|
|
41
49
|
});
|
|
@@ -54,7 +62,11 @@ describe("Skybridge.express", () => {
|
|
|
54
62
|
const { createApp } = await import("./express.js");
|
|
55
63
|
const callsA = [];
|
|
56
64
|
const callsB = [];
|
|
57
|
-
const buildApp = () => new Skybridge({
|
|
65
|
+
const buildApp = () => new Skybridge({
|
|
66
|
+
name: "t",
|
|
67
|
+
version: "0.0.0",
|
|
68
|
+
handler: (server) => server,
|
|
69
|
+
});
|
|
58
70
|
const sA = buildApp();
|
|
59
71
|
sA.use((_req, _res, next) => {
|
|
60
72
|
callsA.push("first");
|
|
@@ -87,7 +99,11 @@ describe("Skybridge.express", () => {
|
|
|
87
99
|
});
|
|
88
100
|
it("useOnError still wraps thrown /mcp errors after the route is mounted", async () => {
|
|
89
101
|
const { createApp } = await import("./express.js");
|
|
90
|
-
const app = new Skybridge({
|
|
102
|
+
const app = new Skybridge({
|
|
103
|
+
name: "t",
|
|
104
|
+
version: "0.0.0",
|
|
105
|
+
handler: (server) => server,
|
|
106
|
+
});
|
|
91
107
|
// Register the error handler BEFORE createApp — useOnError should still
|
|
92
108
|
// apply it after /mcp, so /mcp errors hit it.
|
|
93
109
|
const seen = [];
|
|
@@ -126,7 +142,12 @@ describe("Skybridge JSON body parser options", () => {
|
|
|
126
142
|
});
|
|
127
143
|
}
|
|
128
144
|
it("forwards json options to express.json", async () => {
|
|
129
|
-
const app = new Skybridge({
|
|
145
|
+
const app = new Skybridge({
|
|
146
|
+
name: "t",
|
|
147
|
+
version: "0.0.0",
|
|
148
|
+
json: { limit: "10mb" },
|
|
149
|
+
handler: (server) => server,
|
|
150
|
+
});
|
|
130
151
|
app.express.post("/echo", (req, res) => {
|
|
131
152
|
res.json({ received: req.body.data.length });
|
|
132
153
|
});
|
|
@@ -137,7 +158,11 @@ describe("Skybridge JSON body parser options", () => {
|
|
|
137
158
|
expect(await res.json()).toEqual({ received: 200 * 1024 });
|
|
138
159
|
});
|
|
139
160
|
it("keeps the default 100kb limit when no options are passed", async () => {
|
|
140
|
-
const app = new Skybridge({
|
|
161
|
+
const app = new Skybridge({
|
|
162
|
+
name: "t",
|
|
163
|
+
version: "0.0.0",
|
|
164
|
+
handler: (server) => server,
|
|
165
|
+
});
|
|
141
166
|
app.express.post("/echo", (_req, res) => {
|
|
142
167
|
res.json({ ok: true });
|
|
143
168
|
});
|
|
@@ -155,7 +180,11 @@ describe("createApp", () => {
|
|
|
155
180
|
calls.push("custom");
|
|
156
181
|
next();
|
|
157
182
|
};
|
|
158
|
-
const app = new Skybridge({
|
|
183
|
+
const app = new Skybridge({
|
|
184
|
+
name: "t",
|
|
185
|
+
version: "0.0.0",
|
|
186
|
+
handler: (server) => server,
|
|
187
|
+
});
|
|
159
188
|
app.use(mw);
|
|
160
189
|
const httpServer = http.createServer();
|
|
161
190
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -171,7 +200,11 @@ describe("createApp", () => {
|
|
|
171
200
|
calls.push("auth");
|
|
172
201
|
next();
|
|
173
202
|
};
|
|
174
|
-
const app = new Skybridge({
|
|
203
|
+
const app = new Skybridge({
|
|
204
|
+
name: "t",
|
|
205
|
+
version: "0.0.0",
|
|
206
|
+
handler: (server) => server,
|
|
207
|
+
});
|
|
175
208
|
app.use("/mcp", mw);
|
|
176
209
|
const httpServer = http.createServer();
|
|
177
210
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -187,7 +220,11 @@ describe("createApp", () => {
|
|
|
187
220
|
calls.push("reject");
|
|
188
221
|
res.status(401).json({ error: "Unauthorized" });
|
|
189
222
|
};
|
|
190
|
-
const app = new Skybridge({
|
|
223
|
+
const app = new Skybridge({
|
|
224
|
+
name: "t",
|
|
225
|
+
version: "0.0.0",
|
|
226
|
+
handler: (server) => server,
|
|
227
|
+
});
|
|
191
228
|
app.use("/mcp", reject);
|
|
192
229
|
const httpServer = http.createServer();
|
|
193
230
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -209,7 +246,11 @@ describe("createApp", () => {
|
|
|
209
246
|
calls.push("B");
|
|
210
247
|
next();
|
|
211
248
|
};
|
|
212
|
-
const app = new Skybridge({
|
|
249
|
+
const app = new Skybridge({
|
|
250
|
+
name: "t",
|
|
251
|
+
version: "0.0.0",
|
|
252
|
+
handler: (server) => server,
|
|
253
|
+
});
|
|
213
254
|
app.use(mwA);
|
|
214
255
|
app.use(mwB);
|
|
215
256
|
const httpServer = http.createServer();
|
|
@@ -226,7 +267,11 @@ describe("createApp", () => {
|
|
|
226
267
|
calls.push("api");
|
|
227
268
|
next();
|
|
228
269
|
};
|
|
229
|
-
const app = new Skybridge({
|
|
270
|
+
const app = new Skybridge({
|
|
271
|
+
name: "t",
|
|
272
|
+
version: "0.0.0",
|
|
273
|
+
handler: (server) => server,
|
|
274
|
+
});
|
|
230
275
|
app.use("/api", apiMw);
|
|
231
276
|
const httpServer = http.createServer();
|
|
232
277
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -243,7 +288,11 @@ describe("createApp", () => {
|
|
|
243
288
|
router.get("/health", (_req, res) => {
|
|
244
289
|
res.json({ status: "ok" });
|
|
245
290
|
});
|
|
246
|
-
const app = new Skybridge({
|
|
291
|
+
const app = new Skybridge({
|
|
292
|
+
name: "t",
|
|
293
|
+
version: "0.0.0",
|
|
294
|
+
handler: (server) => server,
|
|
295
|
+
});
|
|
247
296
|
app.use(router);
|
|
248
297
|
const httpServer = http.createServer();
|
|
249
298
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -260,7 +309,11 @@ describe("createApp", () => {
|
|
|
260
309
|
router.get("/data", (_req, res) => {
|
|
261
310
|
res.json({ value: 42 });
|
|
262
311
|
});
|
|
263
|
-
const app = new Skybridge({
|
|
312
|
+
const app = new Skybridge({
|
|
313
|
+
name: "t",
|
|
314
|
+
version: "0.0.0",
|
|
315
|
+
handler: (server) => server,
|
|
316
|
+
});
|
|
264
317
|
app.use("/api", router);
|
|
265
318
|
const httpServer = http.createServer();
|
|
266
319
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -275,7 +328,11 @@ describe("createApp", () => {
|
|
|
275
328
|
const throwing = () => {
|
|
276
329
|
throw new Error("boom");
|
|
277
330
|
};
|
|
278
|
-
const app = new Skybridge({
|
|
331
|
+
const app = new Skybridge({
|
|
332
|
+
name: "t",
|
|
333
|
+
version: "0.0.0",
|
|
334
|
+
handler: (server) => server,
|
|
335
|
+
});
|
|
279
336
|
app.use("/explode", throwing);
|
|
280
337
|
const httpServer = http.createServer();
|
|
281
338
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -290,7 +347,11 @@ describe("createApp", () => {
|
|
|
290
347
|
it("returns 500 JSON-RPC error when the MCP handler throws and no error middleware is registered", async () => {
|
|
291
348
|
const { createApp } = await import("./express.js");
|
|
292
349
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
293
|
-
const app = new Skybridge({
|
|
350
|
+
const app = new Skybridge({
|
|
351
|
+
name: "t",
|
|
352
|
+
version: "0.0.0",
|
|
353
|
+
handler: (server) => server,
|
|
354
|
+
});
|
|
294
355
|
// Force the express-level error path: make a /mcp middleware throw so the
|
|
295
356
|
// error pipeline runs and lands in the default /mcp error handler.
|
|
296
357
|
app.use("/mcp", () => {
|
|
@@ -317,7 +378,11 @@ describe("createApp", () => {
|
|
|
317
378
|
calls.push("error-handler");
|
|
318
379
|
res.status(503).json({ custom: true });
|
|
319
380
|
};
|
|
320
|
-
const app = new Skybridge({
|
|
381
|
+
const app = new Skybridge({
|
|
382
|
+
name: "t",
|
|
383
|
+
version: "0.0.0",
|
|
384
|
+
handler: (server) => server,
|
|
385
|
+
});
|
|
321
386
|
app.use("/mcp", () => {
|
|
322
387
|
throw new Error("boom");
|
|
323
388
|
});
|
|
@@ -344,7 +409,11 @@ describe("createApp", () => {
|
|
|
344
409
|
const throwingApiRoute = (_req, _res, next) => {
|
|
345
410
|
next(new Error("api error"));
|
|
346
411
|
};
|
|
347
|
-
const app = new Skybridge({
|
|
412
|
+
const app = new Skybridge({
|
|
413
|
+
name: "t",
|
|
414
|
+
version: "0.0.0",
|
|
415
|
+
handler: (server) => server,
|
|
416
|
+
});
|
|
348
417
|
app.use("/mcp", () => {
|
|
349
418
|
throw new Error("boom");
|
|
350
419
|
});
|
|
@@ -371,10 +440,14 @@ describe("createApp", () => {
|
|
|
371
440
|
const { createApp } = await import("./express.js");
|
|
372
441
|
// Slow tool: keeps the underlying transport bound long enough to overlap
|
|
373
442
|
// with concurrent requests, exposing the shared-McpServer race.
|
|
374
|
-
const app = new Skybridge({
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
443
|
+
const app = new Skybridge({
|
|
444
|
+
name: "concurrent-test",
|
|
445
|
+
version: "0.0.0",
|
|
446
|
+
handler: (server) => server.registerTool({ name: "slow", description: "slow" }, async () => {
|
|
447
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
448
|
+
return { content: [{ type: "text", text: "done" }] };
|
|
449
|
+
}),
|
|
450
|
+
});
|
|
378
451
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
379
452
|
const httpServer = http.createServer();
|
|
380
453
|
const expressApp = await createApp({ app, httpServer });
|
|
@@ -409,7 +482,11 @@ describe("createApp Vercel mode", () => {
|
|
|
409
482
|
try {
|
|
410
483
|
vi.resetModules();
|
|
411
484
|
const { Skybridge: Reloaded } = await import("./app.js");
|
|
412
|
-
const app = new Reloaded({
|
|
485
|
+
const app = new Reloaded({
|
|
486
|
+
name: "t",
|
|
487
|
+
version: "0.0.0",
|
|
488
|
+
handler: (server) => server,
|
|
489
|
+
});
|
|
413
490
|
const result = await app.run();
|
|
414
491
|
expect(typeof result).toBe("function");
|
|
415
492
|
expect(result).toBe(app.express);
|
|
@@ -448,7 +525,11 @@ describe("createApp tunnel routes", () => {
|
|
|
448
525
|
process.env.__TUNNEL_CONTROL_PORT = String(controlPort);
|
|
449
526
|
try {
|
|
450
527
|
const { createApp } = await import("./express.js");
|
|
451
|
-
const app = new Skybridge({
|
|
528
|
+
const app = new Skybridge({
|
|
529
|
+
name: "t",
|
|
530
|
+
version: "0.0.0",
|
|
531
|
+
handler: (server) => server,
|
|
532
|
+
});
|
|
452
533
|
const httpServer = http.createServer();
|
|
453
534
|
const expressApp = await createApp({ app, httpServer });
|
|
454
535
|
const { port, server } = await listen(expressApp);
|
|
@@ -476,7 +557,11 @@ describe("createApp tunnel routes", () => {
|
|
|
476
557
|
vi.resetModules();
|
|
477
558
|
const { createApp } = await import("./express.js");
|
|
478
559
|
const { Skybridge: ReloadedSkybridge } = await import("./app.js");
|
|
479
|
-
const app = new ReloadedSkybridge({
|
|
560
|
+
const app = new ReloadedSkybridge({
|
|
561
|
+
name: "t",
|
|
562
|
+
version: "0.0.0",
|
|
563
|
+
handler: (server) => server,
|
|
564
|
+
});
|
|
480
565
|
const httpServer = http.createServer();
|
|
481
566
|
const expressApp = await createApp({ app, httpServer });
|
|
482
567
|
const { port, server } = await listen(expressApp);
|
|
@@ -495,13 +580,17 @@ describe("createApp tunnel routes", () => {
|
|
|
495
580
|
describe("createApp mount path", () => {
|
|
496
581
|
it("serves /mcp with the mount path intact on the request URL", async () => {
|
|
497
582
|
const { createApp } = await import("./express.js");
|
|
498
|
-
const app = new Skybridge({
|
|
499
|
-
name: "
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
583
|
+
const app = new Skybridge({
|
|
584
|
+
name: "t",
|
|
585
|
+
version: "0.0.0",
|
|
586
|
+
handler: (server) => server.registerTool({
|
|
587
|
+
name: "echo-url",
|
|
588
|
+
description: "Echoes the request URL.",
|
|
589
|
+
inputSchema: {},
|
|
590
|
+
}, (_args, extra) => ({
|
|
591
|
+
content: [{ type: "text", text: extra.http?.req?.url ?? "" }],
|
|
592
|
+
})),
|
|
593
|
+
});
|
|
505
594
|
const httpServer = http.createServer();
|
|
506
595
|
const expressApp = await createApp({ app, httpServer });
|
|
507
596
|
const { port, server } = await listen(expressApp);
|