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.
Files changed (78) hide show
  1. package/README.md +7 -7
  2. package/dist/server/app.d.ts +79 -60
  3. package/dist/server/app.js +61 -70
  4. package/dist/server/app.js.map +1 -1
  5. package/dist/server/app.test.js +97 -0
  6. package/dist/server/app.test.js.map +1 -0
  7. package/dist/server/auth/index.d.ts +19 -1
  8. package/dist/server/auth/index.js.map +1 -1
  9. package/dist/server/auth/providers/auth0.d.ts +2 -2
  10. package/dist/server/auth/providers/auth0.js +13 -8
  11. package/dist/server/auth/providers/auth0.js.map +1 -1
  12. package/dist/server/auth/providers/auth0.test.js +1 -1
  13. package/dist/server/auth/providers/auth0.test.js.map +1 -1
  14. package/dist/server/auth/providers/authplane.d.ts +2 -2
  15. package/dist/server/auth/providers/authplane.js.map +1 -1
  16. package/dist/server/auth/providers/authplane.test.js +10 -7
  17. package/dist/server/auth/providers/authplane.test.js.map +1 -1
  18. package/dist/server/auth/providers/clerk.d.ts +2 -2
  19. package/dist/server/auth/providers/clerk.js.map +1 -1
  20. package/dist/server/auth/providers/clerk.test.js +1 -1
  21. package/dist/server/auth/providers/clerk.test.js.map +1 -1
  22. package/dist/server/auth/providers/custom.d.ts +3 -3
  23. package/dist/server/auth/providers/custom.js +5 -2
  24. package/dist/server/auth/providers/custom.js.map +1 -1
  25. package/dist/server/auth/providers/custom.test.js +13 -10
  26. package/dist/server/auth/providers/custom.test.js.map +1 -1
  27. package/dist/server/auth/providers/descope.d.ts +2 -2
  28. package/dist/server/auth/providers/descope.js.map +1 -1
  29. package/dist/server/auth/providers/descope.test.js +3 -3
  30. package/dist/server/auth/providers/descope.test.js.map +1 -1
  31. package/dist/server/auth/providers/stytch.d.ts +2 -2
  32. package/dist/server/auth/providers/stytch.js.map +1 -1
  33. package/dist/server/auth/providers/workos.d.ts +2 -2
  34. package/dist/server/auth/providers/workos.js.map +1 -1
  35. package/dist/server/auth/setup.test.js +56 -54
  36. package/dist/server/auth/setup.test.js.map +1 -1
  37. package/dist/server/auth-extra.test-d.js +96 -37
  38. package/dist/server/auth-extra.test-d.js.map +1 -1
  39. package/dist/server/auth.d.ts +4 -4
  40. package/dist/server/auth.js +2 -2
  41. package/dist/server/auth.js.map +1 -1
  42. package/dist/server/build-manifest.test.js +1 -1
  43. package/dist/server/build-manifest.test.js.map +1 -1
  44. package/dist/server/express.d.ts +6 -6
  45. package/dist/server/express.js +16 -22
  46. package/dist/server/express.js.map +1 -1
  47. package/dist/server/express.test.js +120 -31
  48. package/dist/server/express.test.js.map +1 -1
  49. package/dist/server/index.d.ts +4 -3
  50. package/dist/server/index.js +2 -1
  51. package/dist/server/index.js.map +1 -1
  52. package/dist/server/mcp-errors.test.js +5 -1
  53. package/dist/server/mcp-errors.test.js.map +1 -1
  54. package/dist/server/middleware.test.js +184 -135
  55. package/dist/server/middleware.test.js.map +1 -1
  56. package/dist/server/register-tool.test.js +8 -4
  57. package/dist/server/register-tool.test.js.map +1 -1
  58. package/dist/server/server.d.ts +20 -45
  59. package/dist/server/server.js +18 -63
  60. package/dist/server/server.js.map +1 -1
  61. package/dist/server/skills-integration.test.js +11 -4
  62. package/dist/server/skills-integration.test.js.map +1 -1
  63. package/dist/server/tool-extra.test.js +30 -14
  64. package/dist/server/tool-extra.test.js.map +1 -1
  65. package/dist/server/view-resource-resolution.test.js +7 -3
  66. package/dist/server/view-resource-resolution.test.js.map +1 -1
  67. package/dist/test/utils.d.ts +3 -3
  68. package/dist/test/utils.js +211 -197
  69. package/dist/test/utils.js.map +1 -1
  70. package/dist/test/view.test.js +19 -15
  71. package/dist/test/view.test.js.map +1 -1
  72. package/dist/web/generate-helpers.d.ts +15 -12
  73. package/dist/web/generate-helpers.js +15 -12
  74. package/dist/web/generate-helpers.js.map +1 -1
  75. package/package.json +2 -3
  76. package/dist/server/factory-loader.test.js +0 -81
  77. package/dist/server/factory-loader.test.js.map +0 -1
  78. /package/dist/server/{factory-loader.test.d.ts → app.test.d.ts} +0 -0
@@ -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 memoized `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →
9
- * `Response` function that builds a fresh MCP server per request. One handler
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 getMcpHandler(skybridgeApp) {
15
- let handler = mcpHandlers.get(skybridgeApp);
16
- if (!handler) {
17
- handler = createMcpHandler(() => skybridgeApp.createServerInstance(), {
18
- onerror: (error) => {
19
- if (error instanceof UnsupportedProtocolVersionError) {
20
- return;
21
- }
22
- console.error("Error handling MCP request:", error);
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 = (skybridgeApp) => {
100
- const handler = toNodeHandler(getMcpHandler(skybridgeApp));
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":"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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<Skybridge, \"express\" | \"createServerInstance\">;\n\nconst mcpHandlers = new WeakMap<SkybridgeApp, McpHttpHandler>();\n\n/**\n * The memoized `/mcp` fetch handler for a {@link Skybridge} app: a `Request` →\n * `Response` function that builds a fresh MCP server per request. One handler\n * per app, shared by the Express middleware and the shutdown drain.\n *\n * @internal\n */\nexport function getMcpHandler(skybridgeApp: SkybridgeApp): McpHttpHandler {\n let handler = mcpHandlers.get(skybridgeApp);\n if (!handler) {\n handler = 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 mcpHandlers.set(skybridgeApp, handler);\n }\n return handler;\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 errorMiddleware = [],\n}: {\n app: SkybridgeApp;\n httpServer: http.Server;\n errorMiddleware?: {\n path?: string;\n handlers: express.ErrorRequestHandler[];\n }[];\n}): Promise<express.Express> {\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(skybridgeApp));\n\n applyMiddlewares(app, errorMiddleware);\n\n app.use(\"/mcp\", defaultErrorHandler);\n\n return app;\n}\n\nconst mcpMiddleware = (skybridgeApp: SkybridgeApp): express.RequestHandler => {\n const handler = toNodeHandler(getMcpHandler(skybridgeApp));\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"]}
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0", json: { limit: "10mb" } }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "concurrent-test", version: "0.0.0" }, (server) => server.registerTool({ name: "slow", description: "slow" }, async () => {
375
- await new Promise((r) => setTimeout(r, 50));
376
- return { content: [{ type: "text", text: "done" }] };
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server);
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({ name: "t", version: "0.0.0" }, (server) => server.registerTool({
499
- name: "echo-url",
500
- description: "Echoes the request URL.",
501
- inputSchema: {},
502
- }, (_args, extra) => ({
503
- content: [{ type: "text", text: extra.http?.req?.url ?? "" }],
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);