skybridge 0.0.0-next.1ec1c19 → 0.0.0-next.204309e
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 +2 -9
- 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 +75 -21
- 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-name.test-d.d.ts +1 -0
- package/dist/server/view-name.test-d.js +8 -0
- package/dist/server/view-name.test-d.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 +22 -5
- 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`.
|
|
@@ -49,8 +51,16 @@ export interface ViewCsp {
|
|
|
49
51
|
*/
|
|
50
52
|
export interface ViewNameRegistry {
|
|
51
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve view component names from a registry: the union of its keys, or
|
|
56
|
+
* `string` when the registry is empty. The empty case happens before
|
|
57
|
+
* `.skybridge/views.d.ts` is generated; falling back to `string` keeps valid
|
|
58
|
+
* view names from erroring on a fresh checkout, and narrowing kicks in once
|
|
59
|
+
* the generated file augments the registry.
|
|
60
|
+
*/
|
|
61
|
+
export type ViewNameFor<Registry> = [keyof Registry & string] extends [never] ? string : keyof Registry & string;
|
|
52
62
|
/** Union of valid view component names. Narrowed by {@link ViewNameRegistry}. */
|
|
53
|
-
export type ViewName =
|
|
63
|
+
export type ViewName = ViewNameFor<ViewNameRegistry>;
|
|
54
64
|
/**
|
|
55
65
|
* Pass under `view` in a tool's `registerTool` config to render the tool's
|
|
56
66
|
* result through a Skybridge view instead of a plain text response.
|
|
@@ -80,17 +90,42 @@ export type SecurityScheme = {
|
|
|
80
90
|
type: "oauth2";
|
|
81
91
|
scopes?: string[];
|
|
82
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
|
+
};
|
|
83
107
|
/**
|
|
84
108
|
* Options forwarded to the built-in `express.json()` body parser. Derived
|
|
85
109
|
* from Express's own types so the public API doesn't depend on `body-parser`.
|
|
86
110
|
*/
|
|
87
111
|
export type JsonOptions = NonNullable<Parameters<typeof express.json>[0]>;
|
|
88
|
-
/**
|
|
89
|
-
|
|
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> {
|
|
90
120
|
/** Options for the built-in `express.json()` middleware, e.g. `{ limit: "10mb" }`. */
|
|
91
121
|
json?: JsonOptions;
|
|
92
122
|
/** Resource-server OAuth config. When set, mounts well-known metadata and bearer auth on `/mcp`. */
|
|
93
|
-
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;
|
|
94
129
|
}
|
|
95
130
|
/**
|
|
96
131
|
* Well-known keys recognized by host runtimes when set on a tool's `_meta`.
|
|
@@ -150,10 +185,10 @@ type ExtractMeta<T> = [Extract<T, {
|
|
|
150
185
|
}> extends {
|
|
151
186
|
_meta: infer M;
|
|
152
187
|
} ? Simplify<M> : unknown;
|
|
153
|
-
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 & {
|
|
154
189
|
[K in TName]: ToolDef<ShapeOutput<TInput>, TOutput, TResponseMetadata>;
|
|
155
|
-
}>;
|
|
156
|
-
interface
|
|
190
|
+
}, TAuthExtra>;
|
|
191
|
+
interface ToolConfigBase<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
157
192
|
name: string;
|
|
158
193
|
title?: string;
|
|
159
194
|
description?: string;
|
|
@@ -161,6 +196,17 @@ interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
|
161
196
|
outputSchema?: ZodRawShapeCompat | AnySchema;
|
|
162
197
|
annotations?: ToolAnnotations;
|
|
163
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;
|
|
164
210
|
/**
|
|
165
211
|
* Declares which auth schemes this tool supports (e.g. `noauth`, `oauth2`).
|
|
166
212
|
* Lets clients label tools that require sign-in before calling, and pass
|
|
@@ -169,8 +215,8 @@ interface ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
|
169
215
|
* enhanced behavior to authenticated ones.
|
|
170
216
|
*/
|
|
171
217
|
securitySchemes?: SecurityScheme[];
|
|
172
|
-
|
|
173
|
-
|
|
218
|
+
};
|
|
219
|
+
type ToolConfig<TInput extends ZodRawShapeCompat | AnySchema> = ToolConfigBase<TInput> & ToolAuthConfig;
|
|
174
220
|
/**
|
|
175
221
|
* Optional client-supplied hints attached to `params._meta` on every tool call
|
|
176
222
|
* by the Apps SDK host. Hints only: never use for authorization, and tolerate
|
|
@@ -200,14 +246,15 @@ export interface ClientHintsMeta {
|
|
|
200
246
|
/** Stable id for the currently mounted widget instance. */
|
|
201
247
|
"openai/widgetSessionId"?: string;
|
|
202
248
|
}
|
|
203
|
-
type ToolHandlerExtra = Omit<RequestHandlerExtra<ServerRequest, ServerNotification>, "_meta"> & {
|
|
249
|
+
type ToolHandlerExtra<TAuthExtra extends ExtraClaims = ExtraClaims> = Omit<RequestHandlerExtra<ServerRequest, ServerNotification>, "_meta" | "authInfo"> & {
|
|
204
250
|
_meta?: RequestMeta & ClientHintsMeta;
|
|
251
|
+
authInfo?: AuthInfo<TAuthExtra>;
|
|
205
252
|
};
|
|
206
253
|
type ToolHandler<TInput extends ZodRawShapeCompat, TReturn extends {
|
|
207
254
|
content?: HandlerContent;
|
|
208
255
|
} = {
|
|
209
256
|
content?: HandlerContent;
|
|
210
|
-
}> = (args: ShapeOutput<TInput>, extra: ToolHandlerExtra) => TReturn | Promise<TReturn>;
|
|
257
|
+
}, TAuthExtra extends ExtraClaims = ExtraClaims> = (args: ShapeOutput<TInput>, extra: ToolHandlerExtra<TAuthExtra>) => TReturn | Promise<TReturn>;
|
|
211
258
|
/**
|
|
212
259
|
* Coerce a tool handler's return value into an MCP `content` array. Strings
|
|
213
260
|
* become a single `TextContent`; a single block is wrapped in an array;
|
|
@@ -228,7 +275,8 @@ declare const McpServerBaseOmitted: new (...args: ConstructorParameters<typeof M
|
|
|
228
275
|
export declare function __setBuildManifest(manifest: Record<string, {
|
|
229
276
|
file: string;
|
|
230
277
|
}>): void;
|
|
231
|
-
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 {
|
|
232
280
|
readonly $types: McpServerTypes<TTools>;
|
|
233
281
|
/**
|
|
234
282
|
* The underlying Express app. Use this to extend the HTTP server with
|
|
@@ -260,7 +308,11 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
260
308
|
private viteManifest;
|
|
261
309
|
private readonly serverInfo;
|
|
262
310
|
private readonly serverOptions?;
|
|
263
|
-
|
|
311
|
+
private oauthEnabled;
|
|
312
|
+
private resolveResourceMetadataUrl?;
|
|
313
|
+
private securitySchemesByTool;
|
|
314
|
+
constructor(serverInfo: Implementation, options?: ServerOptions, skybridgeOptions?: SkybridgeServerOptions<TAuthExtra>);
|
|
315
|
+
private setupSkills;
|
|
264
316
|
/**
|
|
265
317
|
* Register Express middleware on the underlying app. Mirrors `app.use` —
|
|
266
318
|
* pass handlers directly or a path-prefixed handler list. Register before
|
|
@@ -287,12 +339,12 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
287
339
|
useOnError(...handlers: ErrorRequestHandler[]): this;
|
|
288
340
|
useOnError(path: string, ...handlers: ErrorRequestHandler[]): this;
|
|
289
341
|
/** Register MCP protocol-level middleware (catch-all). */
|
|
290
|
-
mcpMiddleware(handler: McpMiddlewareFn): this;
|
|
342
|
+
mcpMiddleware(handler: McpMiddlewareFn<TAuthExtra>): this;
|
|
291
343
|
/** Register MCP protocol-level middleware for all requests (`extra` is `McpExtra`). */
|
|
292
344
|
mcpMiddleware(filter: "request", handler: (request: {
|
|
293
345
|
method: string;
|
|
294
346
|
params: Record<string, unknown>;
|
|
295
|
-
}, extra: McpExtra
|
|
347
|
+
}, extra: McpExtra<TAuthExtra>, next: () => Promise<ServerResult>) => Promise<unknown> | unknown): this;
|
|
296
348
|
/** Register MCP protocol-level middleware for all notifications (`extra` is `undefined`). */
|
|
297
349
|
mcpMiddleware(filter: "notification", handler: (request: {
|
|
298
350
|
method: string;
|
|
@@ -302,7 +354,7 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
302
354
|
* Register MCP protocol-level middleware for an exact method.
|
|
303
355
|
* Narrows `params`, `extra`, and `next()` result based on the method string.
|
|
304
356
|
*/
|
|
305
|
-
mcpMiddleware<M extends McpMethodString>(filter: M, handler: McpTypedMiddlewareFn<M>): this;
|
|
357
|
+
mcpMiddleware<M extends McpMethodString>(filter: M, handler: McpTypedMiddlewareFn<M, TAuthExtra>): this;
|
|
306
358
|
/**
|
|
307
359
|
* Register MCP protocol-level middleware for a wildcard pattern (e.g. `"tools/*"`).
|
|
308
360
|
* `next()` returns the union of result types for matching methods.
|
|
@@ -310,13 +362,13 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
310
362
|
mcpMiddleware<W extends McpWildcard>(filter: W, handler: (request: {
|
|
311
363
|
method: string;
|
|
312
364
|
params: Record<string, unknown>;
|
|
313
|
-
}, extra: McpExtraFor<W>, next: () => Promise<McpResultFor<W>>) => Promise<unknown> | unknown): this;
|
|
365
|
+
}, extra: McpExtraFor<W, TAuthExtra>, next: () => Promise<McpResultFor<W>>) => Promise<unknown> | unknown): this;
|
|
314
366
|
/**
|
|
315
367
|
* Register MCP protocol-level middleware with a method filter.
|
|
316
368
|
* Filter can be an exact method (`"tools/call"`), wildcard (`"tools/*"`),
|
|
317
369
|
* category (`"request"` | `"notification"`), or an array of those.
|
|
318
370
|
*/
|
|
319
|
-
mcpMiddleware(filter: McpMiddlewareFilter, handler: McpMiddlewareFn): this;
|
|
371
|
+
mcpMiddleware(filter: McpMiddlewareFilter, handler: McpMiddlewareFn<TAuthExtra>): this;
|
|
320
372
|
private applyMcpMiddleware;
|
|
321
373
|
/**
|
|
322
374
|
* Connect to an MCP transport (override of the SDK's `connect`). Use this
|
|
@@ -358,7 +410,7 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
358
410
|
private registerViewResources;
|
|
359
411
|
private registerViewResource;
|
|
360
412
|
private serveLegacyAppsSdkUrl;
|
|
361
|
-
private
|
|
413
|
+
private decorateToolHandler;
|
|
362
414
|
private computeViewVersionParam;
|
|
363
415
|
private lookupViewFile;
|
|
364
416
|
private lookupDistFile;
|
|
@@ -405,7 +457,9 @@ export declare class McpServer<TTools extends Record<string, ToolDef> = Record<n
|
|
|
405
457
|
content?: HandlerContent;
|
|
406
458
|
}>(config: ToolConfig<InputArgs> & {
|
|
407
459
|
name: TName;
|
|
408
|
-
}, cb: ToolHandler<InputArgs, TReturn>): AddTool<TTools, TName, InputArgs, ExtractStructuredContent<TReturn>, ExtractMeta<TReturn
|
|
409
|
-
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;
|
|
410
464
|
}
|
|
411
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
|
}
|