skybridge 0.0.0-dev.d2586b1 → 0.0.0-dev.d26b118
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 +19 -9
- package/dist/cli/detect-port.d.ts +18 -0
- package/dist/cli/detect-port.js +61 -0
- package/dist/cli/detect-port.js.map +1 -0
- package/dist/cli/header.js +1 -1
- package/dist/cli/header.js.map +1 -1
- package/dist/cli/types.d.ts +5 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +2 -1
- package/dist/cli/use-execute-steps.js +6 -1
- package/dist/cli/use-execute-steps.js.map +1 -1
- package/dist/cli/use-messages.d.ts +3 -0
- package/dist/cli/use-messages.js +11 -0
- package/dist/cli/use-messages.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +2 -0
- package/dist/cli/use-nodemon.js +71 -0
- package/dist/cli/use-nodemon.js.map +1 -0
- package/dist/cli/use-tunnel.d.ts +14 -0
- package/dist/cli/use-tunnel.js +97 -0
- package/dist/cli/use-tunnel.js.map +1 -0
- package/dist/cli/use-typescript-check.d.ts +8 -0
- package/dist/cli/use-typescript-check.js +59 -0
- package/dist/cli/use-typescript-check.js.map +1 -0
- package/dist/commands/build.js +30 -3
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +3 -1
- package/dist/commands/dev.js +30 -21
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.d.ts +3 -1
- package/dist/commands/start.js +37 -15
- package/dist/commands/start.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.d.ts +5 -6
- package/dist/server/asset-base-url-transform-plugin.js +8 -9
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.test.js +12 -13
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
- package/dist/server/content-helpers.d.ts +27 -0
- package/dist/server/content-helpers.js +40 -0
- package/dist/server/content-helpers.js.map +1 -0
- package/dist/server/express.d.ts +15 -0
- package/dist/server/express.js +77 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.test.d.ts +1 -0
- package/dist/server/express.test.js +252 -0
- package/dist/server/express.test.js.map +1 -0
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/metric.d.ts +14 -0
- package/dist/server/metric.js +62 -0
- package/dist/server/metric.js.map +1 -0
- package/dist/server/middleware.d.ts +124 -0
- package/dist/server/middleware.js +93 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/middleware.test-d.d.ts +1 -0
- package/dist/server/middleware.test-d.js +75 -0
- package/dist/server/middleware.test-d.js.map +1 -0
- package/dist/server/middleware.test.d.ts +1 -0
- package/dist/server/middleware.test.js +493 -0
- package/dist/server/middleware.test.js.map +1 -0
- package/dist/server/server.d.ts +108 -60
- package/dist/server/server.js +309 -54
- package/dist/server/server.js.map +1 -1
- package/dist/server/templates/development.hbs +1 -54
- package/dist/server/widgetsDevServer.d.ts +2 -11
- package/dist/server/widgetsDevServer.js +22 -29
- package/dist/server/widgetsDevServer.js.map +1 -1
- package/dist/test/utils.d.ts +6 -14
- package/dist/test/utils.js +35 -30
- package/dist/test/utils.js.map +1 -1
- package/dist/test/widget.test.js +298 -107
- package/dist/test/widget.test.js.map +1 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +7 -5
- package/dist/web/bridges/apps-sdk/adaptor.js +39 -16
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +1 -1
- package/dist/web/bridges/apps-sdk/index.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +24 -13
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +16 -4
- package/dist/web/bridges/mcp-app/adaptor.js +121 -38
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +13 -30
- package/dist/web/bridges/mcp-app/bridge.js +43 -196
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +5 -3
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +2 -2
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -41
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/types.d.ts +15 -4
- package/dist/web/components/modal-provider.js +2 -4
- package/dist/web/components/modal-provider.js.map +1 -1
- package/dist/web/create-store.js +15 -1
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +9 -4
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.test.js +11 -8
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +1 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.js +4 -0
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +3 -3
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test-d.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test-d.js +8 -0
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +2 -1
- package/dist/web/hooks/use-files.js +1 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +27 -3
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +3 -3
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +3 -1
- package/dist/web/hooks/use-open-external.js +1 -1
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +26 -11
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +5 -1
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +5 -11
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +1 -1
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.js +18 -2
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +29 -1
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-widget-state.test.js +123 -7
- package/dist/web/hooks/use-widget-state.test.js.map +1 -1
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +4 -1
- package/dist/web/plugin/plugin.js +140 -17
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/plugin/scan-widgets.d.ts +1 -0
- package/dist/web/plugin/scan-widgets.js +71 -0
- package/dist/web/plugin/scan-widgets.js.map +1 -0
- package/dist/web/plugin/transform-data-llm.js +1 -1
- package/dist/web/plugin/transform-data-llm.js.map +1 -1
- package/dist/web/plugin/validate-widget.d.ts +1 -0
- package/dist/web/plugin/validate-widget.js +9 -0
- package/dist/web/plugin/validate-widget.js.map +1 -0
- package/dist/web/plugin/validate-widget.test.d.ts +1 -0
- package/dist/web/plugin/validate-widget.test.js +24 -0
- package/dist/web/plugin/validate-widget.test.js.map +1 -0
- package/dist/web/proxy.js +0 -1
- package/dist/web/proxy.js.map +1 -1
- package/package.json +29 -24
- package/tsconfig.base.json +33 -0
package/dist/server/server.d.ts
CHANGED
|
@@ -1,43 +1,68 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { McpUiToolMeta } from "@modelcontextprotocol/ext-apps";
|
|
2
2
|
import { McpServer as McpServerBase, type RegisteredTool } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import type { AnySchema, SchemaOutput, ZodRawShapeCompat } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
4
4
|
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { ContentBlock, ServerNotification, ServerRequest, ServerResult, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import type { ErrorRequestHandler, RequestHandler } from "express";
|
|
7
|
+
import type { McpExtra, McpExtraFor, McpMethodString, McpMiddlewareFilter, McpMiddlewareFn, McpResultFor, McpTypedMiddlewareFn, McpWildcard } from "./middleware.js";
|
|
6
8
|
export type ToolDef<TInput = unknown, TOutput = unknown, TResponseMetadata = unknown> = {
|
|
7
9
|
input: TInput;
|
|
8
10
|
output: TOutput;
|
|
9
11
|
responseMetadata: TResponseMetadata;
|
|
10
12
|
};
|
|
11
|
-
/**
|
|
12
|
-
* Extended MCP Apps CSP with upcoming fields from ext-apps PR #158
|
|
13
|
-
* and Skybridge-specific fields for OpenAI compatibility
|
|
14
|
-
* @see https://github.com/modelcontextprotocol/ext-apps/pull/158
|
|
15
|
-
*/
|
|
16
|
-
type ExtendedMcpUiResourceCsp = McpUiResourceMeta["csp"] & {
|
|
17
|
-
/**
|
|
18
|
-
* Origins that can receive openExternal redirects without safe-link modal (OpenAI-specific)
|
|
19
|
-
* @see https://developers.openai.com/apps-sdk/reference#component-resource-_meta-fields
|
|
20
|
-
*/
|
|
21
|
-
redirectDomains?: string[];
|
|
22
|
-
};
|
|
23
|
-
/** Extended MCP Apps resource metadata with upcoming CSP fields */
|
|
24
|
-
type ExtendedMcpUiResourceMeta = Omit<McpUiResourceMeta, "csp"> & {
|
|
25
|
-
csp?: ExtendedMcpUiResourceCsp;
|
|
26
|
-
};
|
|
27
|
-
/** User-provided resource configuration with optional CSP override */
|
|
28
|
-
export type WidgetResourceMeta = {
|
|
29
|
-
ui?: ExtendedMcpUiResourceMeta;
|
|
30
|
-
} & Resource["_meta"];
|
|
31
13
|
export type WidgetHostType = "apps-sdk" | "mcp-app";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
14
|
+
export interface ViewCsp {
|
|
15
|
+
resourceDomains?: string[];
|
|
16
|
+
connectDomains?: string[];
|
|
17
|
+
frameDomains?: string[];
|
|
18
|
+
redirectDomains?: string[];
|
|
19
|
+
baseUriDomains?: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface WidgetNameRegistry {
|
|
22
|
+
}
|
|
23
|
+
export type WidgetName = keyof WidgetNameRegistry extends never ? string : keyof WidgetNameRegistry & string;
|
|
24
|
+
export interface ViewConfig {
|
|
25
|
+
component: WidgetName;
|
|
26
|
+
description?: string;
|
|
35
27
|
hosts?: WidgetHostType[];
|
|
28
|
+
prefersBorder?: boolean;
|
|
29
|
+
domain?: string;
|
|
30
|
+
csp?: ViewCsp;
|
|
31
|
+
_meta?: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
export interface KnownToolMeta {
|
|
34
|
+
"openai/widgetAccessible"?: boolean;
|
|
35
|
+
"openai/toolInvocation/invoking"?: string;
|
|
36
|
+
"openai/toolInvocation/invoked"?: string;
|
|
37
|
+
}
|
|
38
|
+
export type ToolMeta = KnownToolMeta & Record<string, unknown>;
|
|
39
|
+
export type HandlerContent = string | ContentBlock | ContentBlock[];
|
|
40
|
+
/** @see https://developers.openai.com/apps-sdk/reference#tool-descriptor-parameters */
|
|
41
|
+
type OpenaiToolMeta = {
|
|
42
|
+
"openai/outputTemplate": string;
|
|
43
|
+
"openai/widgetAccessible"?: boolean;
|
|
44
|
+
"openai/toolInvocation/invoking"?: string;
|
|
45
|
+
"openai/toolInvocation/invoked"?: string;
|
|
46
|
+
};
|
|
47
|
+
/** @see https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx#resource-discovery */
|
|
48
|
+
type McpAppsToolMeta = {
|
|
49
|
+
ui: McpUiToolMeta;
|
|
36
50
|
};
|
|
37
|
-
type
|
|
51
|
+
type InternalToolMeta = Partial<OpenaiToolMeta & McpAppsToolMeta>;
|
|
52
|
+
/**
|
|
53
|
+
* Type-level marker interface for cross-package type inference.
|
|
54
|
+
*/
|
|
55
|
+
export interface McpServerTypes<TTools extends Record<string, ToolDef>> {
|
|
56
|
+
readonly tools: TTools;
|
|
57
|
+
}
|
|
38
58
|
type Simplify<T> = {
|
|
39
59
|
[K in keyof T]: T[K];
|
|
40
60
|
};
|
|
61
|
+
type ShapeOutput<Shape extends ZodRawShapeCompat> = Simplify<{
|
|
62
|
+
[K in keyof Shape as undefined extends SchemaOutput<Shape[K]> ? never : K]: SchemaOutput<Shape[K]>;
|
|
63
|
+
} & {
|
|
64
|
+
[K in keyof Shape as undefined extends SchemaOutput<Shape[K]> ? K : never]?: SchemaOutput<Shape[K]>;
|
|
65
|
+
}>;
|
|
41
66
|
type ExtractStructuredContent<T> = T extends {
|
|
42
67
|
structuredContent: infer SC;
|
|
43
68
|
} ? Simplify<SC> : never;
|
|
@@ -48,52 +73,75 @@ type ExtractMeta<T> = [Extract<T, {
|
|
|
48
73
|
}> extends {
|
|
49
74
|
_meta: infer M;
|
|
50
75
|
} ? Simplify<M> : unknown;
|
|
51
|
-
/**
|
|
52
|
-
* Type-level marker interface for cross-package type inference.
|
|
53
|
-
* This enables TypeScript to infer tool types across package boundaries
|
|
54
|
-
* using structural typing on the $types property, rather than relying on
|
|
55
|
-
* class generic inference which fails when McpServer comes from different
|
|
56
|
-
* package installations.
|
|
57
|
-
*
|
|
58
|
-
* Inspired by tRPC's _def pattern and Hono's type markers.
|
|
59
|
-
*/
|
|
60
|
-
export interface McpServerTypes<TTools extends Record<string, ToolDef>> {
|
|
61
|
-
readonly tools: TTools;
|
|
62
|
-
}
|
|
63
|
-
type ShapeOutput<Shape extends ZodRawShapeCompat> = Simplify<{
|
|
64
|
-
[K in keyof Shape as undefined extends SchemaOutput<Shape[K]> ? never : K]: SchemaOutput<Shape[K]>;
|
|
65
|
-
} & {
|
|
66
|
-
[K in keyof Shape as undefined extends SchemaOutput<Shape[K]> ? K : never]?: SchemaOutput<Shape[K]>;
|
|
67
|
-
}>;
|
|
68
76
|
type AddTool<TTools, TName extends string, TInput extends ZodRawShapeCompat, TOutput, TResponseMetadata = unknown> = McpServer<TTools & {
|
|
69
77
|
[K in TName]: ToolDef<ShapeOutput<TInput>, TOutput, TResponseMetadata>;
|
|
70
78
|
}>;
|
|
71
|
-
|
|
79
|
+
interface UnifiedToolConfig<TInput extends ZodRawShapeCompat | AnySchema> {
|
|
80
|
+
name: string;
|
|
72
81
|
title?: string;
|
|
73
82
|
description?: string;
|
|
74
83
|
inputSchema?: TInput;
|
|
75
84
|
outputSchema?: ZodRawShapeCompat | AnySchema;
|
|
76
85
|
annotations?: ToolAnnotations;
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
view?: ViewConfig;
|
|
87
|
+
_meta?: ToolMeta;
|
|
88
|
+
}
|
|
79
89
|
type ToolHandler<TInput extends ZodRawShapeCompat, TReturn extends {
|
|
80
|
-
content:
|
|
81
|
-
} =
|
|
82
|
-
|
|
90
|
+
content: HandlerContent;
|
|
91
|
+
} = {
|
|
92
|
+
content: HandlerContent;
|
|
93
|
+
}> = (args: ShapeOutput<TInput>, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => TReturn | Promise<TReturn>;
|
|
94
|
+
export declare function normalizeContent(content: HandlerContent): ContentBlock[];
|
|
95
|
+
interface McpServerBaseOmitted extends Omit<McpServerBase, "registerTool" | "connect"> {
|
|
96
|
+
}
|
|
97
|
+
declare const McpServerBaseOmitted: new (...args: ConstructorParameters<typeof McpServerBase>) => McpServerBaseOmitted;
|
|
98
|
+
export declare class McpServer<TTools extends Record<string, ToolDef> = Record<never, ToolDef>> extends McpServerBaseOmitted {
|
|
83
99
|
readonly $types: McpServerTypes<TTools>;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
private express?;
|
|
101
|
+
private customMiddleware;
|
|
102
|
+
private customErrorMiddleware;
|
|
103
|
+
private mcpMiddlewareEntries;
|
|
104
|
+
private mcpMiddlewareApplied;
|
|
105
|
+
private claimedWidgets;
|
|
106
|
+
use(...handlers: RequestHandler[]): this;
|
|
107
|
+
use(path: string, ...handlers: RequestHandler[]): this;
|
|
108
|
+
useOnError(...handlers: ErrorRequestHandler[]): this;
|
|
109
|
+
useOnError(path: string, ...handlers: ErrorRequestHandler[]): this;
|
|
110
|
+
mcpMiddleware(handler: McpMiddlewareFn): this;
|
|
111
|
+
mcpMiddleware(filter: "request", handler: (request: {
|
|
112
|
+
method: string;
|
|
113
|
+
params: Record<string, unknown>;
|
|
114
|
+
}, extra: McpExtra, next: () => Promise<ServerResult>) => Promise<unknown> | unknown): this;
|
|
115
|
+
mcpMiddleware(filter: "notification", handler: (request: {
|
|
116
|
+
method: string;
|
|
117
|
+
params: Record<string, unknown>;
|
|
118
|
+
}, extra: undefined, next: () => Promise<undefined>) => Promise<unknown> | unknown): this;
|
|
119
|
+
mcpMiddleware<M extends McpMethodString>(filter: M, handler: McpTypedMiddlewareFn<M>): this;
|
|
120
|
+
mcpMiddleware<W extends McpWildcard>(filter: W, handler: (request: {
|
|
121
|
+
method: string;
|
|
122
|
+
params: Record<string, unknown>;
|
|
123
|
+
}, extra: McpExtraFor<W>, next: () => Promise<McpResultFor<W>>) => Promise<unknown> | unknown): this;
|
|
124
|
+
mcpMiddleware(filter: McpMiddlewareFilter, handler: McpMiddlewareFn): this;
|
|
125
|
+
private applyMcpMiddleware;
|
|
126
|
+
connect(transport: Parameters<typeof McpServerBase.prototype.connect>[0]): Promise<void>;
|
|
127
|
+
run(): Promise<void>;
|
|
128
|
+
/** @internal */
|
|
129
|
+
enforceOneToolPerWidget(component: string, toolName: string): void;
|
|
130
|
+
/** @internal */
|
|
131
|
+
registerViewResources(toolName: string, view: ViewConfig, toolMeta: InternalToolMeta): void;
|
|
94
132
|
private registerWidgetResource;
|
|
133
|
+
/** @internal */
|
|
134
|
+
wrapHandlerWithViewUUID<InputArgs extends ZodRawShapeCompat>(cb: ToolHandler<InputArgs>): ToolHandler<InputArgs>;
|
|
135
|
+
/** @internal */
|
|
136
|
+
wrapHandlerWithContentNormalization<InputArgs extends ZodRawShapeCompat>(cb: ToolHandler<InputArgs>): ToolHandler<InputArgs>;
|
|
137
|
+
private lookupWidgetFile;
|
|
95
138
|
private lookupDistFile;
|
|
96
|
-
private lookupDistFileWithIndexFallback;
|
|
97
139
|
private readManifest;
|
|
140
|
+
registerTool<TName extends string, InputArgs extends ZodRawShapeCompat, TReturn extends {
|
|
141
|
+
content: HandlerContent;
|
|
142
|
+
}>(config: UnifiedToolConfig<InputArgs> & {
|
|
143
|
+
name: TName;
|
|
144
|
+
}, cb: ToolHandler<InputArgs, TReturn>): AddTool<TTools, TName, InputArgs, ExtractStructuredContent<TReturn>, ExtractMeta<TReturn>>;
|
|
145
|
+
registerTool<InputArgs extends ZodRawShapeCompat>(config: UnifiedToolConfig<InputArgs>, cb: ToolHandler<InputArgs>): RegisteredTool;
|
|
98
146
|
}
|
|
99
147
|
export {};
|
package/dist/server/server.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import http from "node:http";
|
|
2
4
|
import path from "node:path";
|
|
3
5
|
import { McpServer as McpServerBase, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
6
|
import { mergeWith, union } from "es-toolkit";
|
|
7
|
+
import { createApp } from "./express.js";
|
|
8
|
+
import { createMiddlewareEntry } from "./metric.js";
|
|
9
|
+
import { buildMiddlewareChain, getHandlerMaps } from "./middleware.js";
|
|
5
10
|
import { templateHelper } from "./templateHelper.js";
|
|
6
11
|
const mergeWithUnion = (target, source) => {
|
|
7
12
|
return mergeWith(target, source, (targetVal, sourceVal) => {
|
|
@@ -10,111 +15,309 @@ const mergeWithUnion = (target, source) => {
|
|
|
10
15
|
}
|
|
11
16
|
});
|
|
12
17
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Content normalization
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
export function normalizeContent(content) {
|
|
22
|
+
if (typeof content === "string") {
|
|
23
|
+
return [{ type: "text", text: content }];
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(content)) {
|
|
26
|
+
return content;
|
|
27
|
+
}
|
|
28
|
+
return [content];
|
|
29
|
+
}
|
|
30
|
+
const McpServerBaseOmitted = McpServerBase;
|
|
31
|
+
export class McpServer extends McpServerBaseOmitted {
|
|
32
|
+
express;
|
|
33
|
+
customMiddleware = [];
|
|
34
|
+
customErrorMiddleware = [];
|
|
35
|
+
mcpMiddlewareEntries = [];
|
|
36
|
+
mcpMiddlewareApplied = false;
|
|
37
|
+
claimedWidgets = new Map();
|
|
38
|
+
use(pathOrHandler, ...handlers) {
|
|
39
|
+
if (typeof pathOrHandler === "string") {
|
|
40
|
+
this.customMiddleware.push({
|
|
41
|
+
path: pathOrHandler,
|
|
42
|
+
handlers,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this.customMiddleware.push({
|
|
47
|
+
handlers: [pathOrHandler, ...handlers],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
useOnError(pathOrHandler, ...handlers) {
|
|
53
|
+
if (typeof pathOrHandler === "string") {
|
|
54
|
+
this.customErrorMiddleware.push({ path: pathOrHandler, handlers });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.customErrorMiddleware.push({
|
|
58
|
+
handlers: [pathOrHandler, ...handlers],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
mcpMiddleware(filterOrHandler,
|
|
64
|
+
// biome-ignore lint/suspicious/noExplicitAny: overloads narrow the handler type at call sites; implementation must accept all variants
|
|
65
|
+
maybeHandler) {
|
|
66
|
+
if (this.mcpMiddlewareApplied) {
|
|
67
|
+
throw new Error("Cannot register MCP middleware after run() or connect() has been called");
|
|
68
|
+
}
|
|
69
|
+
const handler = maybeHandler;
|
|
70
|
+
if (typeof filterOrHandler === "function") {
|
|
71
|
+
this.mcpMiddlewareEntries.push({
|
|
72
|
+
filter: null,
|
|
73
|
+
handler: filterOrHandler,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (handler) {
|
|
77
|
+
this.mcpMiddlewareEntries.push({
|
|
78
|
+
filter: filterOrHandler,
|
|
79
|
+
handler,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error("mcpMiddleware requires a handler function when a filter is provided");
|
|
84
|
+
}
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
applyMcpMiddleware() {
|
|
88
|
+
if (this.mcpMiddlewareApplied) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.mcpMiddlewareApplied = true;
|
|
92
|
+
const monitoringEntry = createMiddlewareEntry();
|
|
93
|
+
const entries = monitoringEntry
|
|
94
|
+
? [monitoringEntry, ...this.mcpMiddlewareEntries]
|
|
95
|
+
: this.mcpMiddlewareEntries;
|
|
96
|
+
if (entries.length === 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { requestHandlers, notificationHandlers } = getHandlerMaps(this.server);
|
|
100
|
+
const instrumentMap = (map, isNotification) => {
|
|
101
|
+
for (const [method, handler] of map) {
|
|
102
|
+
map.set(method, buildMiddlewareChain(method, isNotification, handler, entries));
|
|
103
|
+
}
|
|
104
|
+
const originalSet = map.set.bind(map);
|
|
105
|
+
map.set = (method, handler) => originalSet(method, buildMiddlewareChain(method, isNotification, handler, entries));
|
|
18
106
|
};
|
|
19
|
-
|
|
107
|
+
instrumentMap(requestHandlers, false);
|
|
108
|
+
instrumentMap(notificationHandlers, true);
|
|
109
|
+
}
|
|
110
|
+
async connect(transport) {
|
|
111
|
+
this.applyMcpMiddleware();
|
|
112
|
+
return McpServerBase.prototype.connect.call(this, transport);
|
|
113
|
+
}
|
|
114
|
+
async run() {
|
|
115
|
+
this.applyMcpMiddleware();
|
|
116
|
+
const httpServer = http.createServer();
|
|
117
|
+
if (!this.express) {
|
|
118
|
+
this.express = await createApp({
|
|
119
|
+
mcpServer: this,
|
|
120
|
+
httpServer,
|
|
121
|
+
customMiddleware: this.customMiddleware,
|
|
122
|
+
errorMiddleware: this.customErrorMiddleware,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
httpServer.on("request", this.express);
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
httpServer.on("error", (error) => {
|
|
128
|
+
console.error("Failed to start server:", error);
|
|
129
|
+
reject(error);
|
|
130
|
+
});
|
|
131
|
+
const port = parseInt(process.env.__PORT ?? "3000", 10);
|
|
132
|
+
httpServer.listen(port, () => {
|
|
133
|
+
resolve();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// registerTool is defined via interface merging + prototype assignment below
|
|
138
|
+
// to avoid TypeScript override signature incompatibility with the base class.
|
|
139
|
+
// -------------------------------------------------------------------------
|
|
140
|
+
// View (widget) resource registration
|
|
141
|
+
// -------------------------------------------------------------------------
|
|
142
|
+
/** @internal */
|
|
143
|
+
enforceOneToolPerWidget(component, toolName) {
|
|
144
|
+
const existingTool = this.claimedWidgets.get(component);
|
|
145
|
+
if (existingTool) {
|
|
146
|
+
throw new Error(`skybridge: widget "${component}" is already used by tool "${existingTool}". Tool "${toolName}" cannot also reference it — each widget backs exactly one tool.`);
|
|
147
|
+
}
|
|
148
|
+
this.claimedWidgets.set(component, toolName);
|
|
149
|
+
}
|
|
150
|
+
/** @internal */
|
|
151
|
+
registerViewResources(toolName, view, toolMeta) {
|
|
152
|
+
const hosts = view.hosts ?? ["apps-sdk", "mcp-app"];
|
|
153
|
+
if (hosts.includes("apps-sdk")) {
|
|
20
154
|
const widgetConfig = {
|
|
21
155
|
hostType: "apps-sdk",
|
|
22
|
-
uri: `ui://widgets/apps-sdk/${
|
|
156
|
+
uri: `ui://widgets/apps-sdk/${view.component}.html`,
|
|
23
157
|
mimeType: "text/html+skybridge",
|
|
24
|
-
buildContentMeta: ({ resourceDomains, connectDomains, domain }) => {
|
|
25
|
-
const userUi = userMeta?.ui;
|
|
26
|
-
const userCsp = userUi?.csp;
|
|
158
|
+
buildContentMeta: ({ resourceDomains, connectDomains, domain }, overrides) => {
|
|
27
159
|
const defaults = {
|
|
28
160
|
"openai/widgetCSP": {
|
|
29
161
|
resource_domains: resourceDomains,
|
|
30
162
|
connect_domains: connectDomains,
|
|
31
163
|
},
|
|
32
164
|
"openai/widgetDomain": domain,
|
|
33
|
-
"openai/widgetDescription":
|
|
165
|
+
"openai/widgetDescription": view.description,
|
|
34
166
|
};
|
|
35
|
-
const
|
|
167
|
+
const fromView = {
|
|
36
168
|
"openai/widgetCSP": {
|
|
37
|
-
resource_domains:
|
|
38
|
-
connect_domains:
|
|
39
|
-
frame_domains:
|
|
40
|
-
redirect_domains:
|
|
169
|
+
resource_domains: view.csp?.resourceDomains,
|
|
170
|
+
connect_domains: view.csp?.connectDomains,
|
|
171
|
+
frame_domains: view.csp?.frameDomains,
|
|
172
|
+
redirect_domains: view.csp?.redirectDomains,
|
|
41
173
|
},
|
|
42
|
-
"openai/widgetDomain":
|
|
43
|
-
"openai/widgetPrefersBorder":
|
|
174
|
+
"openai/widgetDomain": view.domain,
|
|
175
|
+
"openai/widgetPrefersBorder": view.prefersBorder,
|
|
44
176
|
};
|
|
45
|
-
const
|
|
46
|
-
|
|
177
|
+
const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
|
|
178
|
+
"openai/widgetDomain": overrides.domain,
|
|
179
|
+
});
|
|
180
|
+
if (view._meta) {
|
|
181
|
+
return { ...base, ...view._meta };
|
|
182
|
+
}
|
|
183
|
+
return base;
|
|
47
184
|
},
|
|
48
185
|
};
|
|
49
186
|
this.registerWidgetResource({
|
|
50
|
-
name,
|
|
187
|
+
name: toolName,
|
|
51
188
|
widgetConfig,
|
|
52
|
-
|
|
189
|
+
view,
|
|
53
190
|
});
|
|
54
191
|
toolMeta["openai/outputTemplate"] = widgetConfig.uri;
|
|
55
192
|
}
|
|
56
|
-
if (
|
|
193
|
+
if (hosts.includes("mcp-app")) {
|
|
57
194
|
const widgetConfig = {
|
|
58
195
|
hostType: "mcp-app",
|
|
59
|
-
uri: `ui://widgets/ext-apps/${
|
|
196
|
+
uri: `ui://widgets/ext-apps/${view.component}.html`,
|
|
60
197
|
mimeType: "text/html;profile=mcp-app",
|
|
61
|
-
buildContentMeta: ({ resourceDomains, connectDomains, domain }) => {
|
|
198
|
+
buildContentMeta: ({ resourceDomains, connectDomains, domain, baseUriDomains }, overrides) => {
|
|
62
199
|
const defaults = {
|
|
63
200
|
ui: {
|
|
64
201
|
csp: {
|
|
65
202
|
resourceDomains,
|
|
66
203
|
connectDomains,
|
|
204
|
+
baseUriDomains,
|
|
67
205
|
},
|
|
68
206
|
domain,
|
|
69
207
|
},
|
|
70
208
|
};
|
|
71
|
-
|
|
209
|
+
const fromView = {
|
|
210
|
+
ui: {
|
|
211
|
+
...(view.description && { description: view.description }),
|
|
212
|
+
...(view.prefersBorder !== undefined && {
|
|
213
|
+
prefersBorder: view.prefersBorder,
|
|
214
|
+
}),
|
|
215
|
+
...(view.domain && { domain: view.domain }),
|
|
216
|
+
csp: {
|
|
217
|
+
...(view.csp?.resourceDomains && {
|
|
218
|
+
resourceDomains: view.csp.resourceDomains,
|
|
219
|
+
}),
|
|
220
|
+
...(view.csp?.connectDomains && {
|
|
221
|
+
connectDomains: view.csp.connectDomains,
|
|
222
|
+
}),
|
|
223
|
+
...(view.csp?.frameDomains && {
|
|
224
|
+
frameDomains: view.csp.frameDomains,
|
|
225
|
+
}),
|
|
226
|
+
...(view.csp?.baseUriDomains && {
|
|
227
|
+
baseUriDomains: view.csp.baseUriDomains,
|
|
228
|
+
}),
|
|
229
|
+
...(view.csp?.redirectDomains && {
|
|
230
|
+
redirectDomains: view.csp.redirectDomains,
|
|
231
|
+
}),
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
const base = mergeWithUnion(mergeWithUnion(defaults, fromView), {
|
|
236
|
+
ui: overrides,
|
|
237
|
+
});
|
|
238
|
+
if (view._meta) {
|
|
239
|
+
return { ...base, ...view._meta };
|
|
240
|
+
}
|
|
241
|
+
return base;
|
|
72
242
|
},
|
|
73
243
|
};
|
|
74
244
|
this.registerWidgetResource({
|
|
75
|
-
name,
|
|
245
|
+
name: toolName,
|
|
76
246
|
widgetConfig,
|
|
77
|
-
|
|
247
|
+
view,
|
|
78
248
|
});
|
|
249
|
+
// @ts-expect-error - For backwards compatibility with Claude current implementation of the specs
|
|
250
|
+
toolMeta["ui/resourceUri"] = widgetConfig.uri;
|
|
79
251
|
toolMeta.ui = { resourceUri: widgetConfig.uri };
|
|
80
252
|
}
|
|
81
|
-
this.registerTool(name, {
|
|
82
|
-
...toolConfig,
|
|
83
|
-
_meta: toolMeta,
|
|
84
|
-
}, toolCallback);
|
|
85
|
-
return this;
|
|
86
|
-
}
|
|
87
|
-
registerTool(name, config, cb) {
|
|
88
|
-
super.registerTool(name, config, cb);
|
|
89
|
-
return this;
|
|
90
253
|
}
|
|
91
|
-
registerWidgetResource({ name, widgetConfig,
|
|
254
|
+
registerWidgetResource({ name, widgetConfig, view, }) {
|
|
92
255
|
const { hostType, uri: widgetUri, mimeType, buildContentMeta, } = widgetConfig;
|
|
93
|
-
this.registerResource(name, widgetUri, {
|
|
256
|
+
this.registerResource(name, widgetUri, { description: view.description }, async (uri, extra) => {
|
|
94
257
|
const isProduction = process.env.NODE_ENV === "production";
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
258
|
+
const isClaude = extra?.requestInfo?.headers?.["user-agent"] === "Claude-User";
|
|
259
|
+
const headers = extra?.requestInfo?.headers || {};
|
|
260
|
+
const header = (key) => {
|
|
261
|
+
const val = headers[key];
|
|
262
|
+
return Array.isArray(val) ? val[0] : val;
|
|
263
|
+
};
|
|
264
|
+
let serverUrl;
|
|
265
|
+
const forwardedHost = header("x-forwarded-host");
|
|
266
|
+
const origin = header("origin");
|
|
267
|
+
const host = header("host");
|
|
268
|
+
if (forwardedHost) {
|
|
269
|
+
const proto = header("x-forwarded-proto") || "https";
|
|
270
|
+
serverUrl = `${proto}://${forwardedHost}`;
|
|
271
|
+
}
|
|
272
|
+
else if (origin) {
|
|
273
|
+
serverUrl = origin;
|
|
274
|
+
}
|
|
275
|
+
else if (host) {
|
|
276
|
+
const proto = ["127.0.0.1:", "localhost:"].some((p) => host.startsWith(p))
|
|
277
|
+
? "http"
|
|
278
|
+
: "https";
|
|
279
|
+
serverUrl = `${proto}://${host}`;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
const devPort = process.env.__PORT || "3000";
|
|
283
|
+
serverUrl = `http://localhost:${devPort}`;
|
|
284
|
+
}
|
|
99
285
|
const html = isProduction
|
|
100
286
|
? templateHelper.renderProduction({
|
|
101
287
|
hostType,
|
|
102
288
|
serverUrl,
|
|
103
|
-
widgetFile: this.
|
|
289
|
+
widgetFile: this.lookupWidgetFile(view.component),
|
|
104
290
|
styleFile: this.lookupDistFile("style.css"),
|
|
105
291
|
})
|
|
106
292
|
: templateHelper.renderDevelopment({
|
|
107
293
|
hostType,
|
|
108
294
|
serverUrl,
|
|
109
|
-
widgetName:
|
|
295
|
+
widgetName: view.component,
|
|
110
296
|
});
|
|
111
|
-
const
|
|
297
|
+
const connectDomains = [serverUrl];
|
|
298
|
+
if (!isProduction) {
|
|
299
|
+
const wsUrl = new URL(serverUrl);
|
|
300
|
+
wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
301
|
+
connectDomains.push(wsUrl.origin);
|
|
302
|
+
}
|
|
303
|
+
let contentMetaOverrides = {};
|
|
304
|
+
if (isClaude) {
|
|
305
|
+
const pathname = extra?.requestInfo?.url?.pathname ?? "";
|
|
306
|
+
const rawUrl = header("x-alpic-forwarded-url") ?? `${serverUrl}${pathname}`;
|
|
307
|
+
const url = rawUrl.endsWith("/") ? rawUrl.slice(0, -1) : rawUrl;
|
|
308
|
+
const hash = crypto
|
|
309
|
+
.createHash("sha256")
|
|
310
|
+
.update(url)
|
|
311
|
+
.digest("hex")
|
|
312
|
+
.slice(0, 32);
|
|
313
|
+
contentMetaOverrides = { domain: `${hash}.claudemcpcontent.com` };
|
|
314
|
+
}
|
|
112
315
|
const contentMeta = buildContentMeta({
|
|
113
316
|
resourceDomains: [serverUrl],
|
|
114
|
-
connectDomains
|
|
317
|
+
connectDomains,
|
|
115
318
|
domain: serverUrl,
|
|
116
319
|
baseUriDomains: [serverUrl],
|
|
117
|
-
});
|
|
320
|
+
}, contentMetaOverrides);
|
|
118
321
|
return {
|
|
119
322
|
contents: [
|
|
120
323
|
{ uri: uri.href, mimeType, text: html, _meta: contentMeta },
|
|
@@ -122,18 +325,70 @@ export class McpServer extends McpServerBase {
|
|
|
122
325
|
};
|
|
123
326
|
});
|
|
124
327
|
}
|
|
125
|
-
|
|
328
|
+
// -------------------------------------------------------------------------
|
|
329
|
+
// Handler wrapping
|
|
330
|
+
// -------------------------------------------------------------------------
|
|
331
|
+
/** @internal */
|
|
332
|
+
wrapHandlerWithViewUUID(cb) {
|
|
333
|
+
return async (args, extra) => {
|
|
334
|
+
const result = await cb(args, extra);
|
|
335
|
+
return {
|
|
336
|
+
...result,
|
|
337
|
+
content: normalizeContent(result.content),
|
|
338
|
+
_meta: {
|
|
339
|
+
...result._meta,
|
|
340
|
+
viewUUID: crypto.randomUUID(),
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/** @internal */
|
|
346
|
+
wrapHandlerWithContentNormalization(cb) {
|
|
347
|
+
return async (args, extra) => {
|
|
348
|
+
const result = await cb(args, extra);
|
|
349
|
+
return {
|
|
350
|
+
...result,
|
|
351
|
+
content: normalizeContent(result.content),
|
|
352
|
+
};
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// -------------------------------------------------------------------------
|
|
356
|
+
// Manifest helpers
|
|
357
|
+
// -------------------------------------------------------------------------
|
|
358
|
+
lookupWidgetFile(widgetName) {
|
|
126
359
|
const manifest = this.readManifest();
|
|
127
|
-
|
|
360
|
+
const file = manifest[widgetName]?.file;
|
|
361
|
+
if (!file) {
|
|
362
|
+
throw new Error(`Widget "${widgetName}" not found in Vite manifest. Did the build complete successfully? Expected key "${widgetName}" in dist/assets/.vite/manifest.json.`);
|
|
363
|
+
}
|
|
364
|
+
return file;
|
|
128
365
|
}
|
|
129
|
-
|
|
366
|
+
lookupDistFile(key) {
|
|
130
367
|
const manifest = this.readManifest();
|
|
131
|
-
|
|
132
|
-
const indexFileKey = `${basePath}/index.tsx`;
|
|
133
|
-
return manifest[flatFileKey]?.file ?? manifest[indexFileKey]?.file;
|
|
368
|
+
return manifest[key]?.file;
|
|
134
369
|
}
|
|
135
370
|
readManifest() {
|
|
136
371
|
return JSON.parse(readFileSync(path.join(process.cwd(), "dist", "assets", ".vite", "manifest.json"), "utf-8"));
|
|
137
372
|
}
|
|
373
|
+
registerTool(...args) {
|
|
374
|
+
const baseFn = McpServerBase.prototype.registerTool;
|
|
375
|
+
if (typeof args[0] === "string") {
|
|
376
|
+
baseFn.call(this, args[0], args[1], args[2]);
|
|
377
|
+
return this;
|
|
378
|
+
}
|
|
379
|
+
const config = args[0];
|
|
380
|
+
const cb = args[1];
|
|
381
|
+
const { name, view, _meta: userToolMeta, ...toolFields } = config;
|
|
382
|
+
const toolMeta = { ...userToolMeta };
|
|
383
|
+
if (view) {
|
|
384
|
+
this.enforceOneToolPerWidget(view.component, name);
|
|
385
|
+
this.registerViewResources(name, view, toolMeta);
|
|
386
|
+
}
|
|
387
|
+
const wrappedCb = view
|
|
388
|
+
? this.wrapHandlerWithViewUUID(cb)
|
|
389
|
+
: this.wrapHandlerWithContentNormalization(cb);
|
|
390
|
+
baseFn.call(this, name, { ...toolFields, _meta: toolMeta }, wrappedCb);
|
|
391
|
+
return this;
|
|
392
|
+
}
|
|
138
393
|
}
|
|
139
394
|
//# sourceMappingURL=server.js.map
|