skybridge 0.0.0-dev.bfa3fec → 0.0.0-dev.bfb8742
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 +3 -0
- package/dist/cli/build-helpers.d.ts +6 -2
- package/dist/cli/build-helpers.js +37 -12
- package/dist/cli/build-helpers.js.map +1 -1
- package/dist/cli/build-helpers.test.js +71 -3
- package/dist/cli/build-helpers.test.js.map +1 -1
- package/dist/cli/build-steps.d.ts +2 -0
- package/dist/cli/build-steps.js +73 -0
- package/dist/cli/build-steps.js.map +1 -0
- package/dist/cli/build-steps.test.d.ts +1 -0
- package/dist/cli/build-steps.test.js +52 -0
- package/dist/cli/build-steps.test.js.map +1 -0
- package/dist/cli/header.d.ts +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +5 -0
- package/dist/cli/{resolve-views-dir.js → resolve-skybridge-config.js} +4 -3
- package/dist/cli/resolve-skybridge-config.js.map +1 -0
- package/dist/cli/run-plain.d.ts +18 -0
- package/dist/cli/run-plain.js +89 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +14 -0
- package/dist/cli/use-nodemon.js +71 -60
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-typescript-check.d.ts +8 -2
- package/dist/cli/use-typescript-check.js +70 -67
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -2
- package/dist/commands/build.js +2 -61
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +34 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/context-warnings.d.ts +5 -0
- package/dist/context-warnings.js +31 -0
- package/dist/context-warnings.js.map +1 -0
- package/dist/context-warnings.test.d.ts +1 -0
- package/dist/context-warnings.test.js +28 -0
- package/dist/context-warnings.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +30 -4
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/auth/providers/auth0.d.ts +24 -2
- package/dist/server/auth/providers/auth0.js.map +1 -1
- package/dist/server/auth/providers/auth0.test.js +4 -2
- package/dist/server/auth/providers/auth0.test.js.map +1 -1
- package/dist/server/auth/providers/authplane.d.ts +49 -0
- package/dist/server/auth/providers/authplane.js +57 -0
- package/dist/server/auth/providers/authplane.js.map +1 -0
- package/dist/server/auth/providers/authplane.test.d.ts +1 -0
- package/dist/server/auth/providers/authplane.test.js +140 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +39 -2
- package/dist/server/auth/providers/clerk.js +4 -1
- package/dist/server/auth/providers/clerk.js.map +1 -1
- package/dist/server/auth/providers/clerk.test.js +5 -3
- package/dist/server/auth/providers/clerk.test.js.map +1 -1
- package/dist/server/auth/providers/custom.d.ts +18 -2
- package/dist/server/auth/providers/custom.js +29 -10
- package/dist/server/auth/providers/custom.js.map +1 -1
- package/dist/server/auth/providers/custom.test.js +61 -10
- package/dist/server/auth/providers/custom.test.js.map +1 -1
- package/dist/server/auth/providers/descope.d.ts +31 -2
- package/dist/server/auth/providers/descope.js +13 -2
- package/dist/server/auth/providers/descope.js.map +1 -1
- package/dist/server/auth/providers/descope.test.js +42 -17
- package/dist/server/auth/providers/descope.test.js.map +1 -1
- package/dist/server/auth/providers/stytch.d.ts +15 -2
- package/dist/server/auth/providers/stytch.js +4 -1
- package/dist/server/auth/providers/stytch.js.map +1 -1
- package/dist/server/auth/providers/verify-spy.d.ts +12 -0
- package/dist/server/auth/providers/verify-spy.js +21 -0
- package/dist/server/auth/providers/verify-spy.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +24 -2
- package/dist/server/auth/providers/workos.js +4 -1
- package/dist/server/auth/providers/workos.js.map +1 -1
- package/dist/server/auth/security-schemes.d.ts +23 -0
- package/dist/server/auth/security-schemes.js +64 -0
- package/dist/server/auth/security-schemes.js.map +1 -0
- package/dist/server/auth/security-schemes.test.d.ts +1 -0
- package/dist/server/auth/security-schemes.test.js +99 -0
- package/dist/server/auth/security-schemes.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +3 -1
- package/dist/server/auth/setup.js +65 -24
- package/dist/server/auth/setup.js.map +1 -1
- package/dist/server/auth/setup.test.js +328 -5
- package/dist/server/auth/setup.test.js.map +1 -1
- package/dist/server/auth/verify.d.ts +37 -3
- package/dist/server/auth/verify.js +26 -2
- package/dist/server/auth/verify.js.map +1 -1
- package/dist/server/auth/verify.test.js +49 -0
- package/dist/server/auth/verify.test.js.map +1 -1
- package/dist/server/auth-extra.test-d.d.ts +1 -0
- package/dist/server/auth-extra.test-d.js +82 -0
- package/dist/server/auth-extra.test-d.js.map +1 -0
- package/dist/server/auth.d.ts +39 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/host.d.ts +2 -0
- package/dist/server/host.js +11 -0
- package/dist/server/host.js.map +1 -0
- package/dist/server/host.test.d.ts +1 -0
- package/dist/server/host.test.js +13 -0
- package/dist/server/host.test.js.map +1 -0
- package/dist/server/index.d.ts +11 -7
- package/dist/server/index.js +6 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware.d.ts +31 -6
- package/dist/server/middleware.js +33 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/middleware.test.js +37 -1
- package/dist/server/middleware.test.js.map +1 -1
- package/dist/server/server.d.ts +66 -20
- package/dist/server/server.js +153 -21
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.d.ts +1 -0
- package/dist/server/skills-integration.test.js +79 -0
- package/dist/server/skills-integration.test.js.map +1 -0
- package/dist/server/skills.d.ts +29 -0
- package/dist/server/skills.js +195 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/skills.test.d.ts +1 -0
- package/dist/server/skills.test.js +247 -0
- package/dist/server/skills.test.js.map +1 -0
- package/dist/server/view-resource-resolution.test.js +69 -1
- package/dist/server/view-resource-resolution.test.js.map +1 -1
- package/dist/server/viewsDevServer.js +4 -1
- package/dist/server/viewsDevServer.js.map +1 -1
- package/dist/test/utils.d.ts +3 -3
- package/dist/test/view.test.js +19 -3
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/adaptor.js +20 -3
- package/dist/web/bridges/adaptor.js.map +1 -1
- package/dist/web/bridges/adaptor.test.js +32 -4
- package/dist/web/bridges/adaptor.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.js +5 -3
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +2 -0
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/components/modal-provider.d.ts +1 -1
- package/dist/web/data-llm.d.ts +1 -1
- package/dist/web/data-llm.test.js +9 -0
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +1 -0
- package/dist/web/hooks/index.js +1 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +2 -1
- package/dist/web/hooks/test/utils.js +1 -1
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-host-info.d.ts +22 -0
- package/dist/web/hooks/use-host-info.js +30 -0
- package/dist/web/hooks/use-host-info.js.map +1 -0
- package/dist/web/hooks/use-host-info.test.d.ts +1 -0
- package/dist/web/hooks/use-host-info.test.js +59 -0
- package/dist/web/hooks/use-host-info.test.js.map +1 -0
- package/dist/web/hooks/use-view-state.js +19 -9
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +31 -0
- package/dist/web/hooks/use-view-state.test.js.map +1 -1
- package/dist/web/plugin/plugin-build.test.d.ts +1 -0
- package/dist/web/plugin/plugin-build.test.js +54 -0
- package/dist/web/plugin/plugin-build.test.js.map +1 -0
- package/dist/web/plugin/plugin.d.ts +6 -0
- package/dist/web/plugin/plugin.js +18 -7
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/plugin/plugin.test.d.ts +1 -0
- package/dist/web/plugin/plugin.test.js +61 -0
- package/dist/web/plugin/plugin.test.js.map +1 -0
- package/package.json +21 -20
- package/dist/cli/resolve-views-dir.d.ts +0 -1
- package/dist/cli/resolve-views-dir.js.map +0 -1
package/dist/server/server.d.ts
CHANGED
|
@@ -6,7 +6,9 @@ import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/proto
|
|
|
6
6
|
import type { ContentBlock, Implementation, RequestMeta, ServerNotification, ServerRequest, ServerResult, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
7
7
|
import express, { type ErrorRequestHandler, type Express, type RequestHandler } from "express";
|
|
8
8
|
import type { OAuthConfig } from "./auth/index.js";
|
|
9
|
+
import type { AuthInfo, ExtraClaims } from "./auth.js";
|
|
9
10
|
import type { McpExtra, McpExtraFor, McpMethodString, McpMiddlewareFilter, McpMiddlewareFn, McpResultFor, McpTypedMiddlewareFn, McpWildcard } from "./middleware.js";
|
|
11
|
+
import { type SkillsManifest } from "./skills.js";
|
|
10
12
|
/**
|
|
11
13
|
* Type marker for a registered tool — carries its input, output, and response
|
|
12
14
|
* metadata shapes so views can infer types from `typeof server`.
|
|
@@ -88,17 +90,42 @@ export type SecurityScheme = {
|
|
|
88
90
|
type: "oauth2";
|
|
89
91
|
scopes?: string[];
|
|
90
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Declarative per-tool auth. Enforced when the server has an `oauth` provider:
|
|
95
|
+
* anonymous or under-scoped calls are rejected before the handler runs. Omit
|
|
96
|
+
* `auth` entirely for the secure default (sign-in required, no specific scope).
|
|
97
|
+
*/
|
|
98
|
+
export type ToolAuth = {
|
|
99
|
+
/**
|
|
100
|
+
* When `true`, the tool is callable signed out; the token is still used when
|
|
101
|
+
* one is present. Omit (or `false`) to require sign-in.
|
|
102
|
+
*/
|
|
103
|
+
allowsAnonymous?: boolean;
|
|
104
|
+
/** OAuth scopes the caller's token must carry to invoke the tool. */
|
|
105
|
+
scopes?: string[];
|
|
106
|
+
};
|
|
91
107
|
/**
|
|
92
108
|
* Options forwarded to the built-in `express.json()` body parser. Derived
|
|
93
109
|
* from Express's own types so the public API doesn't depend on `body-parser`.
|
|
94
110
|
*/
|
|
95
111
|
export type JsonOptions = NonNullable<Parameters<typeof express.json>[0]>;
|
|
96
|
-
/**
|
|
97
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Skybridge-specific server options, passed as the third `McpServer` constructor
|
|
114
|
+
* argument.
|
|
115
|
+
*
|
|
116
|
+
* @typeParam TAuthExtra - Claims the `oauth` verifier populates. Inferred from
|
|
117
|
+
* the config a provider returns, and carried on to tool handlers.
|
|
118
|
+
*/
|
|
119
|
+
export interface SkybridgeServerOptions<TAuthExtra extends ExtraClaims = ExtraClaims> {
|
|
98
120
|
/** Options for the built-in `express.json()` middleware, e.g. `{ limit: "10mb" }`. */
|
|
99
121
|
json?: JsonOptions;
|
|
100
122
|
/** Resource-server OAuth config. When set, mounts well-known metadata and bearer auth on `/mcp`. */
|
|
101
|
-
oauth?: OAuthConfig
|
|
123
|
+
oauth?: OAuthConfig<TAuthExtra>;
|
|
124
|
+
/**
|
|
125
|
+
* @experimental Serve Agent Skills from `src/skills` over MCP (SEP-2640).
|
|
126
|
+
* API may change.
|
|
127
|
+
*/
|
|
128
|
+
skills?: boolean;
|
|
102
129
|
}
|
|
103
130
|
/**
|
|
104
131
|
* Well-known keys recognized by host runtimes when set on a tool's `_meta`.
|
|
@@ -158,10 +185,10 @@ type ExtractMeta<T> = [Extract<T, {
|
|
|
158
185
|
}> extends {
|
|
159
186
|
_meta: infer M;
|
|
160
187
|
} ? Simplify<M> : unknown;
|
|
161
|
-
type AddTool<TTools, TName extends string, TInput extends ZodRawShapeCompat, TOutput, TResponseMetadata = unknown> = McpServer<TTools & {
|
|
188
|
+
type AddTool<TTools, TName extends string, TInput extends ZodRawShapeCompat, TOutput, TResponseMetadata = unknown, TAuthExtra extends ExtraClaims = ExtraClaims> = McpServer<TTools & {
|
|
162
189
|
[K in TName]: ToolDef<ShapeOutput<TInput>, TOutput, TResponseMetadata>;
|
|
163
|
-
}>;
|
|
164
|
-
interface
|
|
190
|
+
}, TAuthExtra>;
|
|
191
|
+
interface ToolConfigBase<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
165
192
|
name: string;
|
|
166
193
|
title?: string;
|
|
167
194
|
description?: string;
|
|
@@ -169,6 +196,17 @@ interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
|
169
196
|
outputSchema?: ZodRawShapeCompat | AnySchema;
|
|
170
197
|
annotations?: ToolAnnotations;
|
|
171
198
|
view?: ViewConfig;
|
|
199
|
+
_meta?: ToolMeta;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The auth face of a tool config: either the high-level `auth` shorthand or the
|
|
203
|
+
* low-level `securitySchemes` escape hatch, never both.
|
|
204
|
+
*/
|
|
205
|
+
type ToolAuthConfig = {
|
|
206
|
+
auth?: ToolAuth;
|
|
207
|
+
securitySchemes?: never;
|
|
208
|
+
} | {
|
|
209
|
+
auth?: never;
|
|
172
210
|
/**
|
|
173
211
|
* Declares which auth schemes this tool supports (e.g. `noauth`, `oauth2`).
|
|
174
212
|
* Lets clients label tools that require sign-in before calling, and pass
|
|
@@ -177,8 +215,8 @@ interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
|
177
215
|
* enhanced behavior to authenticated ones.
|
|
178
216
|
*/
|
|
179
217
|
securitySchemes?: SecurityScheme[];
|
|
180
|
-
|
|
181
|
-
|
|
218
|
+
};
|
|
219
|
+
type ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> = ToolConfigBase<TInput> & ToolAuthConfig;
|
|
182
220
|
/**
|
|
183
221
|
* Optional client-supplied hints attached to `params._meta` on every tool call
|
|
184
222
|
* by the Apps SDK host. Hints only: never use for authorization, and tolerate
|
|
@@ -208,14 +246,15 @@ export interface ClientHintsMeta {
|
|
|
208
246
|
/** Stable id for the currently mounted widget instance. */
|
|
209
247
|
"openai/widgetSessionId"?: string;
|
|
210
248
|
}
|
|
211
|
-
type ToolHandlerExtra = Omit<RequestHandlerExtra<ServerRequest, ServerNotification>, "_meta"> & {
|
|
249
|
+
type ToolHandlerExtra<TAuthExtra extends ExtraClaims = ExtraClaims> = Omit<RequestHandlerExtra<ServerRequest, ServerNotification>, "_meta" | "authInfo"> & {
|
|
212
250
|
_meta?: RequestMeta & ClientHintsMeta;
|
|
251
|
+
authInfo?: AuthInfo<TAuthExtra>;
|
|
213
252
|
};
|
|
214
253
|
type ToolHandler<TInput extends ZodRawShapeCompat, TReturn extends {
|
|
215
254
|
content?: HandlerContent;
|
|
216
255
|
} = {
|
|
217
256
|
content?: HandlerContent;
|
|
218
|
-
}> = (args: ShapeOutput<TInput>, extra: ToolHandlerExtra) => TReturn | Promise<TReturn>;
|
|
257
|
+
}, TAuthExtra extends ExtraClaims = ExtraClaims> = (args: ShapeOutput<TInput>, extra: ToolHandlerExtra<TAuthExtra>) => TReturn | Promise<TReturn>;
|
|
219
258
|
/**
|
|
220
259
|
* Coerce a tool handler's return value into an MCP `content` array. Strings
|
|
221
260
|
* become a single `TextContent`; a single block is wrapped in an array;
|
|
@@ -236,7 +275,8 @@ declare const McpServerBaseOmitted: new (...args: ConstructorParameters<typeof M
|
|
|
236
275
|
export declare function __setBuildManifest(manifest: Record<string, {
|
|
237
276
|
file: string;
|
|
238
277
|
}>): void;
|
|
239
|
-
export declare
|
|
278
|
+
export declare function __setSkillsManifest(manifest: SkillsManifest): void;
|
|
279
|
+
export declare class McpServer<TTools extends Record<string, ToolDef> = Record<never, ToolDef>, TAuthExtra extends ExtraClaims = ExtraClaims> extends McpServerBaseOmitted {
|
|
240
280
|
readonly $types: McpServerTypes<TTools>;
|
|
241
281
|
/**
|
|
242
282
|
* The underlying Express app. Use this to extend the HTTP server with
|
|
@@ -268,7 +308,11 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
268
308
|
private viteManifest;
|
|
269
309
|
private readonly serverInfo;
|
|
270
310
|
private readonly serverOptions?;
|
|
271
|
-
|
|
311
|
+
private oauthEnabled;
|
|
312
|
+
private resolveResourceMetadataUrl?;
|
|
313
|
+
private securitySchemesByTool;
|
|
314
|
+
constructor(serverInfo: Implementation, options?: ServerOptions, skybridgeOptions?: SkybridgeServerOptions<TAuthExtra>);
|
|
315
|
+
private setupSkills;
|
|
272
316
|
/**
|
|
273
317
|
* Register Express middleware on the underlying app. Mirrors `app.use` —
|
|
274
318
|
* pass handlers directly or a path-prefixed handler list. Register before
|
|
@@ -295,12 +339,12 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
295
339
|
useOnError(...handlers: ErrorRequestHandler[]): this;
|
|
296
340
|
useOnError(path: string, ...handlers: ErrorRequestHandler[]): this;
|
|
297
341
|
/** Register MCP protocol-level middleware (catch-all). */
|
|
298
|
-
mcpMiddleware(handler: McpMiddlewareFn): this;
|
|
342
|
+
mcpMiddleware(handler: McpMiddlewareFn<TAuthExtra>): this;
|
|
299
343
|
/** Register MCP protocol-level middleware for all requests (`extra` is `McpExtra`). */
|
|
300
344
|
mcpMiddleware(filter: "request", handler: (request: {
|
|
301
345
|
method: string;
|
|
302
346
|
params: Record<string, unknown>;
|
|
303
|
-
}, extra: McpExtra
|
|
347
|
+
}, extra: McpExtra<TAuthExtra>, next: () => Promise<ServerResult>) => Promise<unknown> | unknown): this;
|
|
304
348
|
/** Register MCP protocol-level middleware for all notifications (`extra` is `undefined`). */
|
|
305
349
|
mcpMiddleware(filter: "notification", handler: (request: {
|
|
306
350
|
method: string;
|
|
@@ -310,7 +354,7 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
310
354
|
* Register MCP protocol-level middleware for an exact method.
|
|
311
355
|
* Narrows `params`, `extra`, and `next()` result based on the method string.
|
|
312
356
|
*/
|
|
313
|
-
mcpMiddleware<M extends McpMethodString>(filter: M, handler: McpTypedMiddlewareFn<M>): this;
|
|
357
|
+
mcpMiddleware<M extends McpMethodString>(filter: M, handler: McpTypedMiddlewareFn<M, TAuthExtra>): this;
|
|
314
358
|
/**
|
|
315
359
|
* Register MCP protocol-level middleware for a wildcard pattern (e.g. `"tools/*"`).
|
|
316
360
|
* `next()` returns the union of result types for matching methods.
|
|
@@ -318,13 +362,13 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
318
362
|
mcpMiddleware<W extends McpWildcard>(filter: W, handler: (request: {
|
|
319
363
|
method: string;
|
|
320
364
|
params: Record<string, unknown>;
|
|
321
|
-
}, extra: McpExtraFor<W>, next: () => Promise<McpResultFor<W>>) => Promise<unknown> | unknown): this;
|
|
365
|
+
}, extra: McpExtraFor<W, TAuthExtra>, next: () => Promise<McpResultFor<W>>) => Promise<unknown> | unknown): this;
|
|
322
366
|
/**
|
|
323
367
|
* Register MCP protocol-level middleware with a method filter.
|
|
324
368
|
* Filter can be an exact method (`"tools/call"`), wildcard (`"tools/*"`),
|
|
325
369
|
* category (`"request"` | `"notification"`), or an array of those.
|
|
326
370
|
*/
|
|
327
|
-
mcpMiddleware(filter: McpMiddlewareFilter, handler: McpMiddlewareFn): this;
|
|
371
|
+
mcpMiddleware(filter: McpMiddlewareFilter, handler: McpMiddlewareFn<TAuthExtra>): this;
|
|
328
372
|
private applyMcpMiddleware;
|
|
329
373
|
/**
|
|
330
374
|
* Connect to an MCP transport (override of the SDK's `connect`). Use this
|
|
@@ -366,7 +410,7 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
366
410
|
private registerViewResources;
|
|
367
411
|
private registerViewResource;
|
|
368
412
|
private serveLegacyAppsSdkUrl;
|
|
369
|
-
private
|
|
413
|
+
private decorateToolHandler;
|
|
370
414
|
private computeViewVersionParam;
|
|
371
415
|
private lookupViewFile;
|
|
372
416
|
private lookupDistFile;
|
|
@@ -413,7 +457,9 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
413
457
|
content?: HandlerContent;
|
|
414
458
|
}>(config: ToolConfig<InputArgs> & {
|
|
415
459
|
name: TName;
|
|
416
|
-
}, cb: ToolHandler<InputArgs, TReturn>): AddTool<TTools, TName, InputArgs, ExtractStructuredContent<TReturn>, ExtractMeta<TReturn
|
|
417
|
-
registerTool<InputArgs extends ZodRawShapeCompat>(config: ToolConfig<InputArgs>, cb: ToolHandler<InputArgs
|
|
460
|
+
}, cb: ToolHandler<InputArgs, TReturn, TAuthExtra>): AddTool<TTools, TName, InputArgs, ExtractStructuredContent<TReturn>, ExtractMeta<TReturn>, TAuthExtra>;
|
|
461
|
+
registerTool<InputArgs extends ZodRawShapeCompat>(config: ToolConfig<InputArgs>, cb: ToolHandler<InputArgs, {
|
|
462
|
+
content?: HandlerContent;
|
|
463
|
+
}, TAuthExtra>): this;
|
|
418
464
|
}
|
|
419
465
|
export {};
|
package/dist/server/server.js
CHANGED
|
@@ -6,11 +6,15 @@ import { Server as SdkServer, } from "@modelcontextprotocol/sdk/server/index.js"
|
|
|
6
6
|
import { McpServer as McpServerBase } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
7
|
import { mergeWith, union } from "es-toolkit";
|
|
8
8
|
import express, {} from "express";
|
|
9
|
+
import { warnOnLargeToolOutput } from "../context-warnings.js";
|
|
10
|
+
import { authToSecuritySchemes, evaluateSecuritySchemes, inBandChallengeResult, } from "./auth/security-schemes.js";
|
|
9
11
|
import { setupOAuth } from "./auth/setup.js";
|
|
10
12
|
import { createApp } from "./express.js";
|
|
13
|
+
import { hostFromUserAgent } from "./host.js";
|
|
11
14
|
import { createMiddlewareEntry } from "./metric.js";
|
|
12
|
-
import { buildMiddlewareChain, getHandlerMaps } from "./middleware.js";
|
|
15
|
+
import { buildMiddlewareChain, captureToolError, getHandlerMaps, } from "./middleware.js";
|
|
13
16
|
import { resolveServerOrigin } from "./requestOrigin.js";
|
|
17
|
+
import { discoverSkills, registerSkills, SKILLS_EXTENSION_KEY, } from "./skills.js";
|
|
14
18
|
import { templateHelper } from "./templateHelper.js";
|
|
15
19
|
const mergeWithUnion = (target, source) => {
|
|
16
20
|
return mergeWith(target, source, (targetVal, sourceVal) => {
|
|
@@ -19,6 +23,20 @@ const mergeWithUnion = (target, source) => {
|
|
|
19
23
|
}
|
|
20
24
|
});
|
|
21
25
|
};
|
|
26
|
+
const SKILLS_DIR = "src/skills";
|
|
27
|
+
/**
|
|
28
|
+
* Normalize an `x-forwarded-prefix` value into a leading-slash, no-trailing-slash
|
|
29
|
+
* path. Takes the first hop of a comma-separated proxy chain.
|
|
30
|
+
* "/v1/", "v1", "/v1, /internal" → "/v1"; "", "/", undefined → "".
|
|
31
|
+
*/
|
|
32
|
+
function normalizeForwardedPrefix(raw) {
|
|
33
|
+
const firstHop = raw?.split(",")[0]?.trim() ?? "";
|
|
34
|
+
const trimmed = firstHop.replace(/\/+$/, "");
|
|
35
|
+
if (trimmed === "") {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
39
|
+
}
|
|
22
40
|
/**
|
|
23
41
|
* Drop the query string from a `ui://` view URI, leaving the bare path. The
|
|
24
42
|
* `?v=` cache key is the only query we append, so a plain split is enough and
|
|
@@ -92,6 +110,47 @@ let pendingBuildManifest = null;
|
|
|
92
110
|
export function __setBuildManifest(manifest) {
|
|
93
111
|
pendingBuildManifest = manifest;
|
|
94
112
|
}
|
|
113
|
+
let pendingSkillsManifest = null;
|
|
114
|
+
export function __setSkillsManifest(manifest) {
|
|
115
|
+
pendingSkillsManifest = manifest;
|
|
116
|
+
}
|
|
117
|
+
// Pure and `this`-free so it can run inside the `super(...)` call, before `this`
|
|
118
|
+
// exists — the capability must be present for the `initialize` response.
|
|
119
|
+
function withSkillsCapability(options, skybridgeOptions) {
|
|
120
|
+
if (!skybridgeOptions?.skills) {
|
|
121
|
+
return options;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
...options,
|
|
125
|
+
capabilities: {
|
|
126
|
+
...options?.capabilities,
|
|
127
|
+
extensions: {
|
|
128
|
+
...options?.capabilities?.extensions,
|
|
129
|
+
[SKILLS_EXTENSION_KEY]: { directoryRead: true },
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// Collapses registerTool's two overloads into a single { config, cb } shape.
|
|
135
|
+
// registerTool("greet", { description }, handler)
|
|
136
|
+
// -> { config: { name: "greet", description }, cb: handler }
|
|
137
|
+
// registerTool({ name: "greet", description }, handler)
|
|
138
|
+
// -> { config: { name: "greet", description }, cb: handler }
|
|
139
|
+
function normalizeRegisterToolArgs(args) {
|
|
140
|
+
if (typeof args[0] === "string") {
|
|
141
|
+
return {
|
|
142
|
+
config: {
|
|
143
|
+
name: args[0],
|
|
144
|
+
...args[1],
|
|
145
|
+
},
|
|
146
|
+
cb: args[2],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
config: args[0],
|
|
151
|
+
cb: args[1],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
95
154
|
export class McpServer extends McpServerBaseOmitted {
|
|
96
155
|
/**
|
|
97
156
|
* The underlying Express app. Use this to extend the HTTP server with
|
|
@@ -123,14 +182,19 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
123
182
|
viteManifest = null;
|
|
124
183
|
serverInfo;
|
|
125
184
|
serverOptions;
|
|
185
|
+
oauthEnabled = false;
|
|
186
|
+
resolveResourceMetadataUrl;
|
|
187
|
+
securitySchemesByTool = new Map();
|
|
126
188
|
constructor(serverInfo, options, skybridgeOptions) {
|
|
127
|
-
|
|
189
|
+
const mergedOptions = withSkillsCapability(options, skybridgeOptions);
|
|
190
|
+
super(serverInfo, mergedOptions);
|
|
128
191
|
this.serverInfo = serverInfo;
|
|
129
|
-
this.serverOptions =
|
|
192
|
+
this.serverOptions = mergedOptions;
|
|
130
193
|
this.express = express();
|
|
131
194
|
this.express.use(express.json(skybridgeOptions?.json));
|
|
132
195
|
if (skybridgeOptions?.oauth) {
|
|
133
|
-
|
|
196
|
+
this.oauthEnabled = true;
|
|
197
|
+
this.resolveResourceMetadataUrl = setupOAuth(this.express, skybridgeOptions.oauth, this.securitySchemesByTool);
|
|
134
198
|
}
|
|
135
199
|
// Pick up the manifest if `dist/__entry.js` primed it before importing
|
|
136
200
|
// user code. Consume-once: clear after the first construction so a
|
|
@@ -141,6 +205,19 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
141
205
|
this.setViteManifest(pendingBuildManifest);
|
|
142
206
|
pendingBuildManifest = null;
|
|
143
207
|
}
|
|
208
|
+
this.setupSkills(Boolean(skybridgeOptions?.skills));
|
|
209
|
+
}
|
|
210
|
+
setupSkills(enabled) {
|
|
211
|
+
const manifest = pendingSkillsManifest;
|
|
212
|
+
pendingSkillsManifest = null;
|
|
213
|
+
if (!enabled) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const skills = manifest ?? discoverSkills(SKILLS_DIR);
|
|
217
|
+
if (skills.length === 0) {
|
|
218
|
+
console.warn(`skybridge: the "skills" option is enabled but no skills were found in "${SKILLS_DIR}". Add a <name>/SKILL.md there, or remove the option.`);
|
|
219
|
+
}
|
|
220
|
+
registerSkills(this, skills);
|
|
144
221
|
}
|
|
145
222
|
use(pathOrHandler, ...handlers) {
|
|
146
223
|
// Branching is load-bearing: Express's `app.use` overloads can't be
|
|
@@ -249,16 +326,33 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
249
326
|
}
|
|
250
327
|
},
|
|
251
328
|
};
|
|
329
|
+
// ChatGPT reads `securitySchemes` at the tool descriptor top level (SEP-1488,
|
|
330
|
+
// still Draft), but the SDK's registerTool strips unknown top-level fields, so
|
|
331
|
+
// it's stashed in `_meta` at registration. This restores it to the top level
|
|
332
|
+
// on tools/list output. Remove once SEP-1488 lands and the SDK preserves it.
|
|
333
|
+
// { name: "checkout", _meta: { securitySchemes: [{ type: "oauth2" }] } }
|
|
334
|
+
// -> { name: "checkout", _meta: {…}, securitySchemes: [{ type: "oauth2" }] }
|
|
335
|
+
const toolsListSecuritySchemesEntry = {
|
|
336
|
+
filter: "tools/list",
|
|
337
|
+
handler: async (_req, _extra, next) => {
|
|
338
|
+
const result = (await next());
|
|
339
|
+
for (const tool of result.tools) {
|
|
340
|
+
const schemes = tool._meta?.securitySchemes;
|
|
341
|
+
if (schemes && !("securitySchemes" in tool)) {
|
|
342
|
+
tool.securitySchemes = schemes;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
},
|
|
347
|
+
};
|
|
252
348
|
const monitoringEntry = createMiddlewareEntry();
|
|
253
349
|
const entries = [
|
|
254
350
|
...(monitoringEntry ? [monitoringEntry] : []),
|
|
255
351
|
viewListMetaEntry,
|
|
256
352
|
viewReadResolveEntry,
|
|
353
|
+
toolsListSecuritySchemesEntry,
|
|
257
354
|
...this.mcpMiddlewareEntries,
|
|
258
355
|
];
|
|
259
|
-
if (entries.length === 0) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
356
|
const { requestHandlers, notificationHandlers } = getHandlerMaps(this.server);
|
|
263
357
|
const instrumentMap = (map, isNotification) => {
|
|
264
358
|
for (const [method, handler] of map) {
|
|
@@ -381,8 +475,13 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
381
475
|
const val = headers[key];
|
|
382
476
|
return Array.isArray(val) ? val[0] : val;
|
|
383
477
|
};
|
|
384
|
-
const isClaude = header("user-agent") === "
|
|
478
|
+
const isClaude = hostFromUserAgent(header("user-agent")) === "claude";
|
|
385
479
|
const serverUrl = resolveServerOrigin(header);
|
|
480
|
+
// Path prefix the proxy routed this request under (e.g. `foo.com/v1`). Read
|
|
481
|
+
// per-request so one process can serve many hosts/prefixes at once: the
|
|
482
|
+
// origin is recovered from x-forwarded-host, the prefix from
|
|
483
|
+
// x-forwarded-prefix. Empty when served at the origin root.
|
|
484
|
+
const assetsBasePath = normalizeForwardedPrefix(header("x-forwarded-prefix"));
|
|
386
485
|
const connectDomains = [serverUrl];
|
|
387
486
|
if (!isProduction) {
|
|
388
487
|
const wsUrl = new URL(serverUrl);
|
|
@@ -403,7 +502,7 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
403
502
|
.slice(0, 32);
|
|
404
503
|
contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
|
|
405
504
|
}
|
|
406
|
-
return { serverUrl, connectDomains, contentMetaOverrides };
|
|
505
|
+
return { serverUrl, assetsBasePath, connectDomains, contentMetaOverrides };
|
|
407
506
|
}
|
|
408
507
|
registerViewResources(toolName, view, toolMeta) {
|
|
409
508
|
// Append a content-derived version param so hosts (e.g. ChatGPT) bust
|
|
@@ -491,17 +590,21 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
491
590
|
this.serveLegacyAppsSdkUrl(view.component, viewUri);
|
|
492
591
|
this.registerResource(name, viewUri, { description: view.description }, async (uri, extra) => {
|
|
493
592
|
const isProduction = process.env.NODE_ENV === "production";
|
|
494
|
-
const { serverUrl } = this.resolveViewRequestContext(extra);
|
|
593
|
+
const { serverUrl, assetsBasePath } = this.resolveViewRequestContext(extra);
|
|
594
|
+
// The view resolves all assets (template imports + runtime lazy chunks
|
|
595
|
+
// via `window.skybridge.serverUrl`) against this base, so it carries the
|
|
596
|
+
// proxy path prefix. CSP domains in `buildMeta` stay the bare origin.
|
|
597
|
+
const viewBase = `${serverUrl}${assetsBasePath}`;
|
|
495
598
|
const html = isProduction
|
|
496
599
|
? templateHelper.renderProduction({
|
|
497
600
|
hostType,
|
|
498
|
-
serverUrl,
|
|
601
|
+
serverUrl: viewBase,
|
|
499
602
|
viewFile: this.lookupViewFile(view.component),
|
|
500
603
|
styleFile: this.lookupDistFile("style.css") ?? "",
|
|
501
604
|
})
|
|
502
605
|
: templateHelper.renderDevelopment({
|
|
503
606
|
hostType,
|
|
504
|
-
serverUrl,
|
|
607
|
+
serverUrl: viewBase,
|
|
505
608
|
viewName: view.component,
|
|
506
609
|
});
|
|
507
610
|
return {
|
|
@@ -516,9 +619,29 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
516
619
|
this.viewUriByPath.set(`ui://widgets/apps-sdk/${component}.html`, canonicalUri);
|
|
517
620
|
this.viewUriByPath.set(`ui://widgets/ext-apps/${component}.html`, canonicalUri);
|
|
518
621
|
}
|
|
519
|
-
|
|
622
|
+
decorateToolHandler(cb, { attachViewUUID, securitySchemes, toolName, }) {
|
|
520
623
|
return async (args, extra) => {
|
|
521
|
-
const
|
|
624
|
+
const toolExtra = extra ?? args;
|
|
625
|
+
if (this.oauthEnabled) {
|
|
626
|
+
const failure = evaluateSecuritySchemes(securitySchemes, toolExtra?.authInfo);
|
|
627
|
+
if (failure) {
|
|
628
|
+
const headers = toolExtra?.requestInfo?.headers ?? {};
|
|
629
|
+
const header = (key) => {
|
|
630
|
+
const value = headers[key];
|
|
631
|
+
return Array.isArray(value) ? value[0] : value;
|
|
632
|
+
};
|
|
633
|
+
return inBandChallengeResult(failure, this.resolveResourceMetadataUrl?.(header));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
let result;
|
|
637
|
+
try {
|
|
638
|
+
result = await cb(args, extra);
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
captureToolError(toolExtra, error);
|
|
642
|
+
throw error;
|
|
643
|
+
}
|
|
644
|
+
warnOnLargeToolOutput(result, toolName);
|
|
522
645
|
return {
|
|
523
646
|
...result,
|
|
524
647
|
content: normalizeContent(result.content),
|
|
@@ -582,14 +705,19 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
582
705
|
}
|
|
583
706
|
registerTool(...args) {
|
|
584
707
|
const baseFn = McpServerBase.prototype.registerTool;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
708
|
+
const { config, cb } = normalizeRegisterToolArgs(args);
|
|
709
|
+
const { name, view, auth, securitySchemes: rawSecuritySchemes, _meta: userToolMeta, ...toolFields } = config;
|
|
710
|
+
const authNeedsProvider = auth !== undefined &&
|
|
711
|
+
(!auth.allowsAnonymous || Boolean(auth.scopes?.length));
|
|
712
|
+
if (rawSecuritySchemes === undefined &&
|
|
713
|
+
authNeedsProvider &&
|
|
714
|
+
!this.oauthEnabled) {
|
|
715
|
+
throw new Error(`Tool "${name}" sets \`auth: ${JSON.stringify(auth)}\` but the server has no \`oauth\` provider configured.`);
|
|
588
716
|
}
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
const { name, view, securitySchemes, _meta: userToolMeta, ...toolFields } = config;
|
|
717
|
+
const securitySchemes = rawSecuritySchemes ??
|
|
718
|
+
(auth && this.oauthEnabled ? authToSecuritySchemes(auth) : undefined);
|
|
592
719
|
const toolMeta = { ...userToolMeta };
|
|
720
|
+
this.securitySchemesByTool.set(name, securitySchemes);
|
|
593
721
|
if (securitySchemes) {
|
|
594
722
|
// SEP-1488 puts `securitySchemes` at the top level of the tool
|
|
595
723
|
// descriptor, but the SDK's `registerTool` drops unknown top-level
|
|
@@ -602,7 +730,11 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
602
730
|
this.enforceOneToolPerView(view.component, name);
|
|
603
731
|
this.registerViewResources(name, view, toolMeta);
|
|
604
732
|
}
|
|
605
|
-
const wrappedCb = this.
|
|
733
|
+
const wrappedCb = this.decorateToolHandler(cb, {
|
|
734
|
+
attachViewUUID: Boolean(view),
|
|
735
|
+
securitySchemes,
|
|
736
|
+
toolName: name,
|
|
737
|
+
});
|
|
606
738
|
baseFn.call(this, name, { ...toolFields, _meta: toolMeta }, wrappedCb);
|
|
607
739
|
return this;
|
|
608
740
|
}
|