skybridge 0.0.0-dev.f57f202 → 0.0.0-dev.f58ff90
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 +9 -9
- package/dist/cli/build-helpers.d.ts +1 -1
- package/dist/cli/build-helpers.js +9 -1
- package/dist/cli/build-helpers.js.map +1 -1
- package/dist/cli/build-helpers.test.js +24 -0
- package/dist/cli/build-helpers.test.js.map +1 -1
- package/dist/cli/build-steps.js +16 -5
- package/dist/cli/build-steps.js.map +1 -1
- package/dist/cli/build-steps.test.js +20 -0
- package/dist/cli/build-steps.test.js.map +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +1 -0
- package/dist/cli/resolve-skybridge-config.js +11 -1
- package/dist/cli/resolve-skybridge-config.js.map +1 -1
- package/dist/cli/use-typescript-check.js +4 -2
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/server/app.d.ts +22 -5
- package/dist/server/app.js +24 -13
- package/dist/server/app.js.map +1 -1
- package/dist/server/app.test.js +81 -0
- package/dist/server/app.test.js.map +1 -1
- package/dist/server/events.d.ts +113 -0
- package/dist/server/events.js +318 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/events.test.d.ts +1 -0
- package/dist/server/events.test.js +172 -0
- package/dist/server/events.test.js.map +1 -0
- package/dist/server/express.d.ts +3 -2
- package/dist/server/express.js +17 -11
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +45 -0
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/form-input.d.ts +120 -0
- package/dist/server/form-input.js +89 -0
- package/dist/server/form-input.js.map +1 -0
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.js +3 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp-errors.test.js +188 -0
- package/dist/server/mcp-errors.test.js.map +1 -1
- package/dist/server/metric.d.ts +5 -3
- package/dist/server/metric.js +5 -5
- package/dist/server/metric.js.map +1 -1
- package/dist/server/register-tool.test.js +153 -0
- package/dist/server/register-tool.test.js.map +1 -1
- package/dist/server/server.d.ts +179 -10
- package/dist/server/server.js +247 -23
- package/dist/server/server.js.map +1 -1
- package/dist/server/templateHelper.d.ts +1 -0
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.js +26 -12
- package/dist/server/templates.generated.js.map +1 -1
- package/dist/server/tool-extra.test.js +79 -0
- package/dist/server/tool-extra.test.js.map +1 -1
- package/dist/server/view-resource-resolution.test.js +35 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -1
- package/dist/web/bridges/adaptor.d.ts +9 -2
- package/dist/web/bridges/adaptor.js +113 -10
- package/dist/web/bridges/adaptor.js.map +1 -1
- package/dist/web/bridges/adaptor.test.js +66 -5
- package/dist/web/bridges/adaptor.test.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +3 -4
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +3 -3
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +3 -3
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +5 -13
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/types.d.ts +45 -6
- package/dist/web/bridges/types.js +1 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/hooks/index.d.ts +2 -0
- package/dist/web/hooks/index.js +2 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/openai-extensions.test.js +1 -8
- package/dist/web/hooks/openai-extensions.test.js.map +1 -1
- package/dist/web/hooks/use-call-tool.js +1 -1
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +6 -2
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-deep-link.js +1 -27
- package/dist/web/hooks/use-deep-link.js.map +1 -1
- package/dist/web/hooks/use-file-resource.d.ts +37 -0
- package/dist/web/hooks/use-file-resource.js +98 -0
- package/dist/web/hooks/use-file-resource.js.map +1 -0
- package/dist/web/hooks/use-file-resource.test.d.ts +1 -0
- package/dist/web/hooks/use-file-resource.test.js +65 -0
- package/dist/web/hooks/use-file-resource.test.js.map +1 -0
- package/dist/web/hooks/use-host.d.ts +3 -3
- package/dist/web/hooks/use-host.js +4 -4
- package/dist/web/hooks/use-host.js.map +1 -1
- package/dist/web/hooks/use-model-context.js +3 -5
- package/dist/web/hooks/use-model-context.js.map +1 -1
- package/dist/web/hooks/use-open-file.d.ts +20 -0
- package/dist/web/hooks/use-open-file.js +25 -0
- package/dist/web/hooks/use-open-file.js.map +1 -0
- package/dist/web/hooks/use-register-view-tool.d.ts +1 -1
- package/dist/web/hooks/use-register-view-tool.js +1 -1
- package/dist/web/hooks/use-register-view-tool.js.map +1 -1
- package/dist/web/hooks/use-send-follow-up-message.d.ts +8 -2
- package/dist/web/hooks/use-send-follow-up-message.js +6 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/mount-view.js +3 -5
- package/dist/web/mount-view.js.map +1 -1
- package/package.json +4 -2
package/dist/server/server.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { McpServer as McpServerBase, } from "@modelcontextprotocol/server";
|
|
4
|
+
import { isInputRequiredResult, McpServer as McpServerBase, } from "@modelcontextprotocol/server";
|
|
5
|
+
import { z } from "zod/v4";
|
|
5
6
|
import { warnOnLargeToolOutput } from "../context-warnings.js";
|
|
6
7
|
import { authToSecuritySchemes, evaluateSecuritySchemes, inBandChallengeResult, } from "./auth/security-schemes.js";
|
|
8
|
+
import { EventRegistry, } from "./events.js";
|
|
7
9
|
import { hostFromUserAgent } from "./host.js";
|
|
8
10
|
import { captureToolError } from "./middleware.js";
|
|
9
11
|
import { resolveServerOrigin } from "./requestOrigin.js";
|
|
10
12
|
import { discoverSkills, registerSkills, SKILLS_EXTENSION_KEY, } from "./skills.js";
|
|
11
13
|
import { templateHelper } from "./templateHelper.js";
|
|
12
14
|
const unionOf = (base, extra) => extra ? [...new Set([...base, ...extra])] : base;
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
+
function mergeOpenAIUi(meta, values) {
|
|
16
|
+
const defined = Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
|
|
17
|
+
if (Object.keys(defined).length > 0) {
|
|
18
|
+
meta["openai/ui"] = { ...meta["openai/ui"], ...defined };
|
|
19
|
+
}
|
|
15
20
|
}
|
|
16
21
|
function acceptsEmptyInput(inputSchema) {
|
|
17
22
|
if (inputSchema === undefined) {
|
|
@@ -36,6 +41,12 @@ function normalizeForwardedPrefix(raw) {
|
|
|
36
41
|
}
|
|
37
42
|
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
38
43
|
}
|
|
44
|
+
const fileEntrypointInput = {
|
|
45
|
+
file: z.object({
|
|
46
|
+
name: z.string().min(1),
|
|
47
|
+
resourceUri: z.string().trim().min(1),
|
|
48
|
+
}),
|
|
49
|
+
};
|
|
39
50
|
/**
|
|
40
51
|
* Drop the query string from a `ui://` view URI, leaving the bare path. The
|
|
41
52
|
* `?v=` cache key is the only query we append, so a plain split is enough and
|
|
@@ -150,6 +161,7 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
150
161
|
resolveResourceMetadataUrl;
|
|
151
162
|
toolSecuritySchemes = new Map();
|
|
152
163
|
userMiddlewareEntries = [];
|
|
164
|
+
eventRegistry;
|
|
153
165
|
constructor(serverInfo, options, skybridgeOptions) {
|
|
154
166
|
super(serverInfo, withSkillsCapability(options, skybridgeOptions));
|
|
155
167
|
this.oauthEnabled = Boolean(skybridgeOptions?.oauth);
|
|
@@ -343,7 +355,7 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
343
355
|
}
|
|
344
356
|
return { serverUrl, assetsBasePath, connectDomains, contentMetaOverrides };
|
|
345
357
|
}
|
|
346
|
-
registerViewResources(toolName, view, toolMeta,
|
|
358
|
+
registerViewResources(toolName, view, toolMeta, openai) {
|
|
347
359
|
// Append a content-derived version param so hosts (e.g. ChatGPT) bust
|
|
348
360
|
// their cache when the bundle changes, but keep the URI stable across
|
|
349
361
|
// `tools/list` calls when the bundle hasn't changed.
|
|
@@ -379,12 +391,10 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
379
391
|
}),
|
|
380
392
|
};
|
|
381
393
|
const meta = { ...base, ...view._meta };
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
387
|
-
}
|
|
394
|
+
mergeOpenAIUi(meta, {
|
|
395
|
+
availableDisplayModes: openai?.availableDisplayModes,
|
|
396
|
+
preferredDisplayMode: openai?.preferredDisplayMode,
|
|
397
|
+
});
|
|
388
398
|
return meta;
|
|
389
399
|
},
|
|
390
400
|
};
|
|
@@ -423,6 +433,7 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
423
433
|
serverUrl: viewBase,
|
|
424
434
|
viewFile: this.lookupViewFile(view.component),
|
|
425
435
|
styleFile: this.lookupDistFile("style.css") ?? "",
|
|
436
|
+
preloadFiles: this.lookupViewPreloadFiles(view.component),
|
|
426
437
|
})
|
|
427
438
|
: templateHelper.renderDevelopment({
|
|
428
439
|
serverUrl: viewBase,
|
|
@@ -457,6 +468,9 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
457
468
|
captureToolError(extra, error);
|
|
458
469
|
throw error;
|
|
459
470
|
}
|
|
471
|
+
if (isInputRequiredResult(result)) {
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
460
474
|
warnOnLargeToolOutput(result, toolName);
|
|
461
475
|
return {
|
|
462
476
|
...result,
|
|
@@ -499,6 +513,21 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
499
513
|
}
|
|
500
514
|
throw new Error(`View "${viewName}" not found in Vite manifest. Did the build complete successfully? Look for an entry with name "${viewName}" in dist/assets/.vite/manifest.json.`);
|
|
501
515
|
}
|
|
516
|
+
lookupViewPreloadFiles(viewName) {
|
|
517
|
+
const manifest = this.readManifest();
|
|
518
|
+
const entryKey = Object.keys(manifest).find((key) => manifest[key]?.isEntry && manifest[key]?.name === viewName);
|
|
519
|
+
const files = new Set();
|
|
520
|
+
const visit = (key) => {
|
|
521
|
+
const chunk = key ? manifest[key] : undefined;
|
|
522
|
+
if (!chunk || files.has(chunk.file)) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
files.add(chunk.file);
|
|
526
|
+
chunk.imports?.forEach(visit);
|
|
527
|
+
};
|
|
528
|
+
visit(entryKey);
|
|
529
|
+
return [...files];
|
|
530
|
+
}
|
|
502
531
|
lookupDistFile(key) {
|
|
503
532
|
const manifest = this.readManifest();
|
|
504
533
|
return manifest[key]?.file;
|
|
@@ -520,6 +549,211 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
520
549
|
cachedDiskManifest ??= JSON.parse(readFileSync(path.join(process.cwd(), "dist", "assets", ".vite", "manifest.json"), "utf-8"));
|
|
521
550
|
return cachedDiskManifest ?? {};
|
|
522
551
|
}
|
|
552
|
+
/**
|
|
553
|
+
* @experimental Register an MCP Events type that hosts can subscribe to, per
|
|
554
|
+
* the Triggers & Events working group draft. Skybridge serves `events/list`,
|
|
555
|
+
* `events/subscribe` and `events/unsubscribe`, checks auth and arguments,
|
|
556
|
+
* verifies the callback URL, then hands each subscription to `onSubscribe`.
|
|
557
|
+
* Delivering the events is up to you, for example with {@link deliverEvent}.
|
|
558
|
+
* API may change.
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* server.registerEvent(
|
|
563
|
+
* {
|
|
564
|
+
* name: "comment.created",
|
|
565
|
+
* description: "Fires when someone comments on a document",
|
|
566
|
+
* inputSchema: { documentId: z.string() },
|
|
567
|
+
* payloadSchema: { documentId: z.string(), excerpt: z.string() },
|
|
568
|
+
* },
|
|
569
|
+
* {
|
|
570
|
+
* onSubscribe: ({ documentId }, { subscription }) =>
|
|
571
|
+
* webhooks.upsert({ ...subscription, filter: { documentId } }),
|
|
572
|
+
* onUnsubscribe: (_args, { subscription }) =>
|
|
573
|
+
* webhooks.remove(subscription.id),
|
|
574
|
+
* },
|
|
575
|
+
* );
|
|
576
|
+
* ```
|
|
577
|
+
*
|
|
578
|
+
* @see https://docs.skybridge.tech/api-reference/register-event
|
|
579
|
+
*/
|
|
580
|
+
registerEvent(config, hooks) {
|
|
581
|
+
if (!this.eventRegistry) {
|
|
582
|
+
this.server.registerCapabilities({ events: {} });
|
|
583
|
+
this.eventRegistry = new EventRegistry(this.server);
|
|
584
|
+
}
|
|
585
|
+
this.eventRegistry.add(config, hooks);
|
|
586
|
+
return this;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Register a ChatGPT file viewer, from the OpenAI MCP extensions: a tool
|
|
590
|
+
* with a file entrypoint for `extensions` and a view. ChatGPT desktop calls
|
|
591
|
+
* it with the file the user opened, as `{ file: { name, resourceUri } }`.
|
|
592
|
+
* Read and save the file from the view with `useFileResource`.
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```ts
|
|
596
|
+
* server.registerFileViewer(
|
|
597
|
+
* {
|
|
598
|
+
* name: "part-viewer",
|
|
599
|
+
* title: "Part Viewer",
|
|
600
|
+
* extensions: [".stl", ".step"],
|
|
601
|
+
* view: { component: "part-viewer" },
|
|
602
|
+
* },
|
|
603
|
+
* async ({ file }) => ({ structuredContent: { name: file.name } }),
|
|
604
|
+
* );
|
|
605
|
+
* ```
|
|
606
|
+
*
|
|
607
|
+
* @see https://docs.skybridge.tech/api-reference/register-file-viewer
|
|
608
|
+
*/
|
|
609
|
+
registerFileViewer(config, cb) {
|
|
610
|
+
const { extensions, openai, ...toolConfig } = config;
|
|
611
|
+
return this.registerTool({
|
|
612
|
+
...toolConfig,
|
|
613
|
+
inputSchema: fileEntrypointInput,
|
|
614
|
+
openai: { ...openai, entrypoints: [{ type: "file", extensions }] },
|
|
615
|
+
}, cb);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Register the composer at-mention search for ChatGPT, from the OpenAI MCP
|
|
619
|
+
* extensions. When the user types `@` and your app name, ChatGPT calls
|
|
620
|
+
* `handler` with the typed text and lists the returned resource links.
|
|
621
|
+
* Skybridge registers it as a tool hidden from the model.
|
|
622
|
+
*
|
|
623
|
+
* @example
|
|
624
|
+
* ```ts
|
|
625
|
+
* server.registerMentions({
|
|
626
|
+
* name: "search_parts",
|
|
627
|
+
* handler: async ({ query }) => ({
|
|
628
|
+
* items: findParts(query).map((part) => ({
|
|
629
|
+
* type: "resource_link",
|
|
630
|
+
* uri: `parts://${part.id}`,
|
|
631
|
+
* name: part.name,
|
|
632
|
+
* })),
|
|
633
|
+
* }),
|
|
634
|
+
* });
|
|
635
|
+
* ```
|
|
636
|
+
*
|
|
637
|
+
* @see https://docs.skybridge.tech/api-reference/register-mentions
|
|
638
|
+
*/
|
|
639
|
+
registerMentions(config) {
|
|
640
|
+
this.registerTool({
|
|
641
|
+
name: config.name,
|
|
642
|
+
description: config.description ?? "Search items to mention.",
|
|
643
|
+
annotations: { readOnlyHint: true },
|
|
644
|
+
inputSchema: { query: z.string() },
|
|
645
|
+
_meta: {
|
|
646
|
+
"openai/extensions": { "mentions/search": {} },
|
|
647
|
+
ui: { visibility: ["app"] },
|
|
648
|
+
},
|
|
649
|
+
}, async ({ query }, extra) => ({
|
|
650
|
+
content: [],
|
|
651
|
+
structuredContent: await config.handler({ query }, extra),
|
|
652
|
+
}));
|
|
653
|
+
return this;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Register app settings for ChatGPT, from the OpenAI MCP extensions.
|
|
657
|
+
* Skybridge registers a read tool and an update tool from `fields`, and
|
|
658
|
+
* advertises them in the `openai/settings` server capability. ChatGPT shows
|
|
659
|
+
* the settings page and calls `update` with the changed fields only. Store
|
|
660
|
+
* the values yourself, for example per user.
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```ts
|
|
664
|
+
* server.registerSettings({
|
|
665
|
+
* fields: {
|
|
666
|
+
* units: { title: "Units", schema: z.enum(["mm", "in"]) },
|
|
667
|
+
* showGrid: { title: "Show grid", schema: z.boolean() },
|
|
668
|
+
* },
|
|
669
|
+
* read: (extra) => loadSettings(extra),
|
|
670
|
+
* update: (set, extra) => saveSettings(set, extra),
|
|
671
|
+
* });
|
|
672
|
+
* ```
|
|
673
|
+
*
|
|
674
|
+
* @see https://docs.skybridge.tech/api-reference/register-settings
|
|
675
|
+
*/
|
|
676
|
+
registerSettings(config) {
|
|
677
|
+
const { fields, layout, readTool = "settings.read", updateTool = "settings.update", } = config;
|
|
678
|
+
const values = z
|
|
679
|
+
.strictObject(Object.fromEntries(Object.entries(fields).map(([name, { schema, title, description }]) => [
|
|
680
|
+
name,
|
|
681
|
+
schema.meta({
|
|
682
|
+
title,
|
|
683
|
+
...(description !== undefined && { description }),
|
|
684
|
+
}),
|
|
685
|
+
])))
|
|
686
|
+
.required();
|
|
687
|
+
const { $schema: _, ...schema } = z.toJSONSchema(values);
|
|
688
|
+
for (const [name, property] of Object.entries(schema.properties ?? {})) {
|
|
689
|
+
if (fields[name]?.schema.safeParse(undefined).data !== undefined) {
|
|
690
|
+
throw new Error(`Setting "${name}" declares a default: return current values from \`read\` instead.`);
|
|
691
|
+
}
|
|
692
|
+
if (typeof property === "boolean" ||
|
|
693
|
+
!["boolean", "string", "number", "integer"].includes(String(property.type)) ||
|
|
694
|
+
(property.enum !== undefined && property.type !== "string")) {
|
|
695
|
+
throw new Error(`Setting "${name}" must be a boolean, string, string enum, number or integer.`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const placed = new Set();
|
|
699
|
+
for (const group of layout ?? []) {
|
|
700
|
+
for (const item of group.items) {
|
|
701
|
+
if (item.kind !== "property") {
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (!Object.hasOwn(fields, item.property) ||
|
|
705
|
+
placed.has(item.property)) {
|
|
706
|
+
throw new Error(`Settings layout names an unknown or duplicate field "${item.property}".`);
|
|
707
|
+
}
|
|
708
|
+
placed.add(item.property);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
this.registerTool({
|
|
712
|
+
name: readTool,
|
|
713
|
+
description: "Read the app settings.",
|
|
714
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
715
|
+
inputSchema: {},
|
|
716
|
+
outputSchema: {
|
|
717
|
+
schema: z.record(z.string(), z.unknown()),
|
|
718
|
+
values,
|
|
719
|
+
layout: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
720
|
+
},
|
|
721
|
+
}, async (_args, extra) => ({
|
|
722
|
+
content: [],
|
|
723
|
+
structuredContent: {
|
|
724
|
+
schema,
|
|
725
|
+
values: values.parse(await config.read(extra)),
|
|
726
|
+
layout,
|
|
727
|
+
},
|
|
728
|
+
}));
|
|
729
|
+
this.registerTool({
|
|
730
|
+
name: updateTool,
|
|
731
|
+
description: "Update the app settings.",
|
|
732
|
+
annotations: {
|
|
733
|
+
destructiveHint: false,
|
|
734
|
+
idempotentHint: true,
|
|
735
|
+
openWorldHint: false,
|
|
736
|
+
},
|
|
737
|
+
inputSchema: {
|
|
738
|
+
set: values
|
|
739
|
+
.partial()
|
|
740
|
+
.refine((set) => Object.keys(set).length > 0)
|
|
741
|
+
.meta({ minProperties: 1 }),
|
|
742
|
+
},
|
|
743
|
+
outputSchema: { values },
|
|
744
|
+
}, async ({ set }, extra) => ({
|
|
745
|
+
content: [],
|
|
746
|
+
structuredContent: {
|
|
747
|
+
values: values.parse(await config.update(set, extra)),
|
|
748
|
+
},
|
|
749
|
+
}));
|
|
750
|
+
const capability = { readTool, updateTool };
|
|
751
|
+
this.server.registerCapabilities({
|
|
752
|
+
extensions: { "openai/settings": capability },
|
|
753
|
+
experimental: { "openai/settings": capability },
|
|
754
|
+
});
|
|
755
|
+
return this;
|
|
756
|
+
}
|
|
523
757
|
registerTool(rawConfig, rawCb) {
|
|
524
758
|
const baseFn = McpServerBase.prototype.registerTool;
|
|
525
759
|
const config = rawConfig;
|
|
@@ -541,7 +775,7 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
541
775
|
// descriptor, but the SDK's `registerTool` drops unknown top-level
|
|
542
776
|
// fields, so the canonical spot isn't reachable without intercepting
|
|
543
777
|
// `tools/list`. Use the `_meta` back-compat mirror documented in the
|
|
544
|
-
//
|
|
778
|
+
// ChatGPT plugin reference until SEP-1488 lands in the spec.
|
|
545
779
|
toolMeta.securitySchemes = securitySchemes;
|
|
546
780
|
}
|
|
547
781
|
if (openai) {
|
|
@@ -558,24 +792,14 @@ export class McpServer extends McpServerBaseOmitted {
|
|
|
558
792
|
if (badExtension !== undefined) {
|
|
559
793
|
throw new Error(`skybridge: tool "${name}" has a file entrypoint extension "${badExtension}" that doesn't start with ".".`);
|
|
560
794
|
}
|
|
561
|
-
|
|
795
|
+
mergeOpenAIUi(toolMeta, {
|
|
562
796
|
entrypoints: openai.entrypoints,
|
|
563
797
|
preferredModelDisplayMode: openai.preferredModelDisplayMode,
|
|
564
798
|
});
|
|
565
|
-
if (Object.keys(openaiUi).length > 0) {
|
|
566
|
-
toolMeta["openai/ui"] = {
|
|
567
|
-
...userToolMeta?.["openai/ui"],
|
|
568
|
-
...openaiUi,
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
799
|
}
|
|
572
800
|
if (view) {
|
|
573
801
|
this.enforceOneToolPerView(view.component, name);
|
|
574
|
-
this.registerViewResources(name, view, toolMeta, openai
|
|
575
|
-
definedEntries({
|
|
576
|
-
availableDisplayModes: openai.availableDisplayModes,
|
|
577
|
-
preferredDisplayMode: openai.preferredDisplayMode,
|
|
578
|
-
}));
|
|
802
|
+
this.registerViewResources(name, view, toolMeta, openai);
|
|
579
803
|
}
|
|
580
804
|
const wrappedCb = this.decorateToolHandler(cb, {
|
|
581
805
|
attachViewUUID: Boolean(view),
|