@every-app/sdk 0.3.0-rc.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -74
- package/dist/cloudflare/catalogSettings.generated.d.ts +20 -6
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
- package/dist/cloudflare/catalogSettings.generated.js +45 -14
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.d.ts +3 -0
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +10 -0
- package/dist/cloudflare/worker.d.ts +67 -23
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +68 -32
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/aiDefault.d.ts +36 -79
- package/dist/internal/aiDefault.d.ts.map +1 -1
- package/dist/internal/aiDefault.js +47 -149
- package/dist/internal/aiProviders.d.ts +1 -1
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +2 -2
- package/dist/internal/appSettings.d.ts +140 -89
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +264 -156
- package/dist/internal/capabilities.d.ts +145 -503
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +29 -550
- package/dist/internal/catalog/commerce.js +1 -1
- package/dist/internal/catalog/comms.d.ts +37 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -1
- package/dist/internal/catalog/comms.js +43 -0
- package/dist/internal/catalog/index.d.ts +72 -0
- package/dist/internal/catalog/index.d.ts.map +1 -1
- package/dist/internal/catalog/infra.d.ts +35 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -1
- package/dist/internal/catalog/infra.js +65 -0
- package/dist/internal/devProviders.d.ts +1 -19
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +7 -1
- package/dist/internal/events.d.ts +52 -1
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +66 -2
- package/dist/internal/gatewaySettings.d.ts +6 -16
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +6 -24
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -0
- package/dist/internal/mcpOAuth.d.ts +210 -0
- package/dist/internal/mcpOAuth.d.ts.map +1 -0
- package/dist/internal/mcpOAuth.js +735 -0
- package/dist/internal/oauthClient.d.ts +7 -47
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +1 -115
- package/dist/internal/oauthPresets.d.ts +356 -20
- package/dist/internal/oauthPresets.d.ts.map +1 -1
- package/dist/internal/oauthPresets.js +183 -861
- package/dist/internal/oauthScopes.d.ts +43 -155
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +82 -475
- package/dist/internal/oauthSpec.d.ts +28 -71
- package/dist/internal/oauthSpec.d.ts.map +1 -1
- package/dist/internal/oauthSpec.js +172 -448
- package/dist/internal/protocol.d.ts +39 -15
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +40 -7
- package/dist/internal/providerApps.d.ts +2 -2
- package/dist/internal/providerApps.js +2 -2
- package/dist/internal/providerRegistry.d.ts +5 -64
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +19 -118
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +14 -36
- package/dist/internal/remoteMcp.d.ts +191 -47
- package/dist/internal/remoteMcp.d.ts.map +1 -1
- package/dist/internal/remoteMcp.js +338 -133
- package/dist/internal/signInProviders.d.ts +1 -1
- package/dist/internal/signInProviders.d.ts.map +1 -1
- package/dist/internal/tools.d.ts +15 -8
- package/dist/internal/tools.d.ts.map +1 -1
- package/dist/internal/tools.js +9 -8
- package/dist/manifest.d.ts +96 -28
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +1 -1
- package/dist/server/ai.d.ts +1 -93
- package/dist/server/ai.d.ts.map +1 -1
- package/dist/server/ai.js +17 -111
- package/dist/server/attribution.d.ts +10 -1
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +7 -1
- package/dist/server/connectUrl.d.ts +4 -9
- package/dist/server/connectUrl.d.ts.map +1 -1
- package/dist/server/connectUrl.js +17 -14
- package/dist/server/context.d.ts +12 -14
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +23 -25
- package/dist/server/emit.d.ts.map +1 -1
- package/dist/server/emit.js +2 -2
- package/dist/server/env.d.ts +3 -0
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/eventsEndpoint.d.ts +48 -0
- package/dist/server/eventsEndpoint.d.ts.map +1 -0
- package/dist/server/eventsEndpoint.js +145 -0
- package/dist/server/everyApp.d.ts +16 -26
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +22 -46
- package/dist/server/gatewayFetch.d.ts +19 -1
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +45 -3
- package/dist/server/index.d.ts +6 -9
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -8
- package/dist/server/integrationFetch.d.ts +60 -72
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +124 -397
- package/dist/server/listMembers.d.ts.map +1 -1
- package/dist/server/listMembers.js +2 -2
- package/dist/server/refusal.d.ts +0 -9
- package/dist/server/refusal.d.ts.map +1 -1
- package/dist/server/refusal.js +0 -22
- package/dist/server/sendEmail.d.ts.map +1 -1
- package/dist/server/sendEmail.js +2 -2
- package/dist/server/tools.d.ts +17 -6
- package/dist/server/tools.d.ts.map +1 -1
- package/dist/server/tools.js +14 -4
- package/dist/server/toolsEndpoint.d.ts +2 -1
- package/dist/server/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +4 -3
- package/dist/server/webhooks.d.ts +56 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +205 -0
- package/dist/testing/index.d.ts +2 -3
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -6
- package/package.json +6 -1
- package/dist/server/aiBinding.d.ts +0 -68
- package/dist/server/aiBinding.d.ts.map +0 -1
- package/dist/server/aiBinding.js +0 -294
- package/dist/server/authorizeUrl.d.ts +0 -43
- package/dist/server/authorizeUrl.d.ts.map +0 -1
- package/dist/server/authorizeUrl.js +0 -106
- package/dist/server/emailBinding.d.ts +0 -72
- package/dist/server/emailBinding.d.ts.map +0 -1
- package/dist/server/emailBinding.js +0 -124
- package/dist/server/fromMcpTools.d.ts +0 -89
- package/dist/server/fromMcpTools.d.ts.map +0 -1
- package/dist/server/fromMcpTools.js +0 -192
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `gatewayEmailBinding(env)`: the Gateway's `email` capability as a
|
|
3
|
-
* `send_email` binding, for code written against `env.EMAIL.send(message)`.
|
|
4
|
-
*
|
|
5
|
-
* An app on Every App binds no `send_email` of its own: the Gateway sends
|
|
6
|
-
* with its sender, checks `from` against the installation's senders and
|
|
7
|
-
* keeps the mail budgets. Code that already calls `env.EMAIL.send()` gets
|
|
8
|
-
* this object in its place:
|
|
9
|
-
*
|
|
10
|
-
* const EMAIL = gatewayEmailBinding(env, { for: request });
|
|
11
|
-
* await EMAIL.send({ from, to, subject, text });
|
|
12
|
-
*
|
|
13
|
-
* Each `send()` is a `sendEmail()` (the manifest lists `capabilities: {
|
|
14
|
-
* email: { access: "write" } }`), so local development works the same way:
|
|
15
|
-
* checked and printed, or sent with the developer's own Email Sending
|
|
16
|
-
* account (EMAIL_REST_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, EMAIL_FROM).
|
|
17
|
-
*/
|
|
18
|
-
import { ConfigurationError, } from "../internal/index.js";
|
|
19
|
-
import { GatewayRefusalError } from "./refusal.js";
|
|
20
|
-
import { sendEmail } from "./sendEmail.js";
|
|
21
|
-
/**
|
|
22
|
-
* A `send_email` binding (`SendEmail`) over the Gateway: pass it where code
|
|
23
|
-
* takes `env.EMAIL`. `send()` takes the structured message (`from`, `to`,
|
|
24
|
-
* `cc`, `bcc`, `subject`, `text`, `html`, `replyTo`, `headers`,
|
|
25
|
-
* `attachments`) and resolves `{ messageId }`. A raw MIME `EmailMessage`
|
|
26
|
-
* (`new EmailMessage(from, to, raw)`) is refused with `GatewayRefusalError`
|
|
27
|
-
* `email_invalid_message`: the Gateway takes structured mail only.
|
|
28
|
-
*
|
|
29
|
-
* Errors are sendEmail()'s: `GatewayRefusalError` with the Gateway's code
|
|
30
|
-
* (`email_sender_not_allowed`, `email_budget_exhausted`,
|
|
31
|
-
* `integration_not_configured`, …) and `ConfigurationError` with neither a
|
|
32
|
-
* binding nor `everyapp dev`. Nothing is checked when the binding is made.
|
|
33
|
-
*/
|
|
34
|
-
export function gatewayEmailBinding(env, options = {}) {
|
|
35
|
-
const binding = {
|
|
36
|
-
async send(message) {
|
|
37
|
-
const result = await sendEmail({
|
|
38
|
-
env,
|
|
39
|
-
...(options.for === undefined ? {} : { for: options.for }),
|
|
40
|
-
message: toSendEmailMessage(message),
|
|
41
|
-
});
|
|
42
|
-
return { messageId: result.messageId ?? "" };
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
// `SendEmail` is the app's own Workers types' interface, whose `send()`
|
|
46
|
-
// overloads change with the version; this `send()` takes both forms.
|
|
47
|
-
return binding;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* The binding's message as sendEmail()'s: recipients as bare addresses,
|
|
51
|
-
* `from` and `replyTo` with their display names, attachments base64.
|
|
52
|
-
*/
|
|
53
|
-
export function toSendEmailMessage(message) {
|
|
54
|
-
if (!message || typeof message !== "object") {
|
|
55
|
-
throw new ConfigurationError("EMAIL.send() takes a message object.");
|
|
56
|
-
}
|
|
57
|
-
if (!("subject" in message)) {
|
|
58
|
-
throw new GatewayRefusalError({
|
|
59
|
-
status: 400,
|
|
60
|
-
code: "email_invalid_message",
|
|
61
|
-
message: "Raw MIME messages can't be sent through the Every App gateway; send a structured message ({ from, to, subject, text or html }).",
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
const m = message;
|
|
65
|
-
return {
|
|
66
|
-
to: addresses(m.to) ?? [],
|
|
67
|
-
...(m.cc === undefined ? {} : { cc: addresses(m.cc) }),
|
|
68
|
-
...(m.bcc === undefined ? {} : { bcc: addresses(m.bcc) }),
|
|
69
|
-
subject: m.subject,
|
|
70
|
-
...(m.text ? { text: m.text } : {}),
|
|
71
|
-
...(m.html ? { html: m.html } : {}),
|
|
72
|
-
from: named(m.from),
|
|
73
|
-
...(m.replyTo ? { replyTo: named(m.replyTo) } : {}),
|
|
74
|
-
...(m.headers && Object.keys(m.headers).length > 0
|
|
75
|
-
? { headers: m.headers }
|
|
76
|
-
: {}),
|
|
77
|
-
...(m.attachments?.length
|
|
78
|
-
? { attachments: m.attachments.map(attachment) }
|
|
79
|
-
: {}),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* One attachment in the Gateway's shape. An inline part without a content
|
|
84
|
-
* id can't be referenced (and the Gateway refuses it), so it goes as an
|
|
85
|
-
* ordinary attachment.
|
|
86
|
-
*/
|
|
87
|
-
function attachment(a) {
|
|
88
|
-
const contentId = a.contentId?.trim();
|
|
89
|
-
return {
|
|
90
|
-
filename: a.filename,
|
|
91
|
-
contentType: a.type,
|
|
92
|
-
content: base64(a.content),
|
|
93
|
-
...(a.disposition === "inline" && contentId
|
|
94
|
-
? { disposition: "inline", contentId }
|
|
95
|
-
: {}),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
function named(address) {
|
|
99
|
-
if (typeof address === "string")
|
|
100
|
-
return { address };
|
|
101
|
-
return {
|
|
102
|
-
address: address.email,
|
|
103
|
-
...(address.name?.trim() ? { name: address.name } : {}),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
function addresses(recipients) {
|
|
107
|
-
if (recipients === undefined)
|
|
108
|
-
return undefined;
|
|
109
|
-
const of = (a) => (typeof a === "string" ? a : a.email);
|
|
110
|
-
return Array.isArray(recipients) ? recipients.map(of) : of(recipients);
|
|
111
|
-
}
|
|
112
|
-
/** Strings are taken as base64 already, as the binding takes them; bytes are encoded. */
|
|
113
|
-
function base64(content) {
|
|
114
|
-
if (typeof content === "string")
|
|
115
|
-
return content;
|
|
116
|
-
const bytes = content instanceof ArrayBuffer
|
|
117
|
-
? new Uint8Array(content)
|
|
118
|
-
: new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
119
|
-
let binary = "";
|
|
120
|
-
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
121
|
-
binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
122
|
-
}
|
|
123
|
-
return btoa(binary);
|
|
124
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `fromMcpTools()` — an app's existing MCP tools as `defineTools()` entries.
|
|
3
|
-
*
|
|
4
|
-
* const tools: McpToolLike[] = [];
|
|
5
|
-
* registerMyTools((name, config, handler) =>
|
|
6
|
-
* tools.push({ name, ...config, handler }),
|
|
7
|
-
* );
|
|
8
|
-
* export default everyApp(handler, {
|
|
9
|
-
* tools: defineTools(fromMcpTools(tools), { instructions }),
|
|
10
|
-
* });
|
|
11
|
-
*
|
|
12
|
-
* Takes the shape `McpServer.registerTool(name, config, handler)` is called
|
|
13
|
-
* with (or what a low-level server's `tools/list` returns, plus a handler),
|
|
14
|
-
* structurally: the SDK takes no dependency on `@modelcontextprotocol/sdk`,
|
|
15
|
-
* and a live `McpServer` keeps its registry private, so the app hands over
|
|
16
|
-
* the list it registers from.
|
|
17
|
-
*/
|
|
18
|
-
import { type ToolRisk } from "../internal/index.js";
|
|
19
|
-
import { type StandardSchemaWithJSON, type ToolContext, type ToolDefinition, type ToolSchema } from "./tools.js";
|
|
20
|
-
/** MCP tool annotations (all hints); only these are read. */
|
|
21
|
-
export interface McpToolAnnotationsLike {
|
|
22
|
-
title?: string;
|
|
23
|
-
readOnlyHint?: boolean;
|
|
24
|
-
destructiveHint?: boolean;
|
|
25
|
-
idempotentHint?: boolean;
|
|
26
|
-
openWorldHint?: boolean;
|
|
27
|
-
}
|
|
28
|
-
/** An MCP `CallToolResult`, structurally. */
|
|
29
|
-
export interface McpToolResultLike {
|
|
30
|
-
content?: ReadonlyArray<{
|
|
31
|
-
readonly type: string;
|
|
32
|
-
}>;
|
|
33
|
-
structuredContent?: unknown;
|
|
34
|
-
isError?: boolean;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* A tool schema as MCP servers write one: a {@link ToolSchema} (a full zod
|
|
38
|
-
* object, `{ jsonSchema, validate }`), a raw shape of Standard Schemas
|
|
39
|
-
* (`{ query: z.string() }`, what `McpServer.registerTool` takes), or a plain
|
|
40
|
-
* JSON Schema object (a low-level server's `tools/list`), which is described
|
|
41
|
-
* but not validated: the handler checks its own arguments, as it did before.
|
|
42
|
-
*/
|
|
43
|
-
export type McpToolSchemaLike = ToolSchema | Readonly<Record<string, StandardSchemaWithJSON>> | Readonly<Record<string, unknown>>;
|
|
44
|
-
/** One MCP tool: `registerTool`'s name, config and handler, flattened. */
|
|
45
|
-
export interface McpToolLike<TExtra = ToolContext> {
|
|
46
|
-
name: string;
|
|
47
|
-
title?: string;
|
|
48
|
-
description?: string;
|
|
49
|
-
/** Omitted: the tool takes no arguments. */
|
|
50
|
-
inputSchema?: McpToolSchemaLike;
|
|
51
|
-
outputSchema?: McpToolSchemaLike;
|
|
52
|
-
annotations?: McpToolAnnotationsLike;
|
|
53
|
-
/**
|
|
54
|
-
* Called with the validated arguments and the value of
|
|
55
|
-
* {@link FromMcpToolsOptions.context} (the call's {@link ToolContext} by
|
|
56
|
-
* default). Return a `CallToolResult`; throw {@link ToolError} for a message
|
|
57
|
-
* the model may read.
|
|
58
|
-
*/
|
|
59
|
-
handler: (args: never, extra: TExtra) => McpToolResultLike | Promise<McpToolResultLike>;
|
|
60
|
-
}
|
|
61
|
-
export interface FromMcpToolsOptions<TExtra = ToolContext> {
|
|
62
|
-
/**
|
|
63
|
-
* What each handler gets as its second argument, built per call from the
|
|
64
|
-
* verified caller (`user`, `env`, `request`, `callId`). Default: the
|
|
65
|
-
* {@link ToolContext} itself.
|
|
66
|
-
*/
|
|
67
|
-
context?: (context: ToolContext) => TExtra | Promise<TExtra>;
|
|
68
|
-
/**
|
|
69
|
-
* A tool's risk, overriding the default for the tools it returns a risk
|
|
70
|
-
* for. Default: `readOnlyHint: true` is `read`, `destructiveHint: true`
|
|
71
|
-
* is `destructive`, anything else `write`.
|
|
72
|
-
*/
|
|
73
|
-
risk?: (tool: McpToolLike<TExtra>) => ToolRisk | undefined;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Adapt MCP tools into `defineTools()` entries, keyed by tool name, to spread
|
|
77
|
-
* beside the app's own: `defineTools({ ...fromMcpTools(list), my_tool })`.
|
|
78
|
-
*
|
|
79
|
-
* - `title`, `description` (the name when absent), `idempotentHint` and
|
|
80
|
-
* `openWorldHint` carry over; risk as {@link FromMcpToolsOptions.risk}.
|
|
81
|
-
* - A result's text blocks, joined by a blank line, are what the model
|
|
82
|
-
* reads; `structuredContent` is sent as structured content (checked
|
|
83
|
-
* against `outputSchema` when there is one). Other blocks are dropped.
|
|
84
|
-
* - `isError: true` becomes a {@link ToolError} with the result's text.
|
|
85
|
-
*
|
|
86
|
-
* Names must already be lowercase snake_case; `defineTools()` checks them.
|
|
87
|
-
*/
|
|
88
|
-
export declare function fromMcpTools<TExtra = ToolContext>(tools: Iterable<McpToolLike<TExtra>>, options?: FromMcpToolsOptions<TExtra>): Record<string, ToolDefinition>;
|
|
89
|
-
//# sourceMappingURL=fromMcpTools.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fromMcpTools.d.ts","sourceRoot":"","sources":["../../src/server/fromMcpTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAqB,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAIpB,6DAA6D;AAC7D,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GACzB,UAAU,GACV,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,GAChD,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEtC,0EAA0E;AAC1E,MAAM,WAAW,WAAW,CAAC,MAAM,GAAG,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC;;;;;OAKG;IACH,OAAO,EAAE,CACP,IAAI,EAAE,KAAK,EACX,KAAK,EAAE,MAAM,KACV,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,mBAAmB,CAAC,MAAM,GAAG,WAAW;IACvD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D;;;;OAIG;IACH,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,QAAQ,GAAG,SAAS,CAAC;CAC5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAG,WAAW,EAC/C,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EACpC,OAAO,GAAE,mBAAmB,CAAC,MAAM,CAAM,GACxC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA+ChC"}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `fromMcpTools()` — an app's existing MCP tools as `defineTools()` entries.
|
|
3
|
-
*
|
|
4
|
-
* const tools: McpToolLike[] = [];
|
|
5
|
-
* registerMyTools((name, config, handler) =>
|
|
6
|
-
* tools.push({ name, ...config, handler }),
|
|
7
|
-
* );
|
|
8
|
-
* export default everyApp(handler, {
|
|
9
|
-
* tools: defineTools(fromMcpTools(tools), { instructions }),
|
|
10
|
-
* });
|
|
11
|
-
*
|
|
12
|
-
* Takes the shape `McpServer.registerTool(name, config, handler)` is called
|
|
13
|
-
* with (or what a low-level server's `tools/list` returns, plus a handler),
|
|
14
|
-
* structurally: the SDK takes no dependency on `@modelcontextprotocol/sdk`,
|
|
15
|
-
* and a live `McpServer` keeps its registry private, so the app hands over
|
|
16
|
-
* the list it registers from.
|
|
17
|
-
*/
|
|
18
|
-
import { TOOL_NAME_PATTERN } from "../internal/index.js";
|
|
19
|
-
import { ToolError, toolResult, } from "./tools.js";
|
|
20
|
-
const JSON_SCHEMA_TARGET = "draft-2020-12";
|
|
21
|
-
/**
|
|
22
|
-
* Adapt MCP tools into `defineTools()` entries, keyed by tool name, to spread
|
|
23
|
-
* beside the app's own: `defineTools({ ...fromMcpTools(list), my_tool })`.
|
|
24
|
-
*
|
|
25
|
-
* - `title`, `description` (the name when absent), `idempotentHint` and
|
|
26
|
-
* `openWorldHint` carry over; risk as {@link FromMcpToolsOptions.risk}.
|
|
27
|
-
* - A result's text blocks, joined by a blank line, are what the model
|
|
28
|
-
* reads; `structuredContent` is sent as structured content (checked
|
|
29
|
-
* against `outputSchema` when there is one). Other blocks are dropped.
|
|
30
|
-
* - `isError: true` becomes a {@link ToolError} with the result's text.
|
|
31
|
-
*
|
|
32
|
-
* Names must already be lowercase snake_case; `defineTools()` checks them.
|
|
33
|
-
*/
|
|
34
|
-
export function fromMcpTools(tools, options = {}) {
|
|
35
|
-
const out = {};
|
|
36
|
-
for (const tool of tools) {
|
|
37
|
-
const problem = (message) => new TypeError(`fromMcpTools: tool "${tool?.name}" ${message}`);
|
|
38
|
-
if (!tool || typeof tool.name !== "string") {
|
|
39
|
-
throw new TypeError("fromMcpTools: every tool needs a name");
|
|
40
|
-
}
|
|
41
|
-
if (!TOOL_NAME_PATTERN.test(tool.name)) {
|
|
42
|
-
throw problem("must be named in lowercase snake_case: a letter, then letters, digits or _ (at most 48 characters); rename it in the list");
|
|
43
|
-
}
|
|
44
|
-
if (Object.prototype.hasOwnProperty.call(out, tool.name)) {
|
|
45
|
-
throw problem("is listed twice");
|
|
46
|
-
}
|
|
47
|
-
if (typeof tool.handler !== "function") {
|
|
48
|
-
throw problem("needs a handler function");
|
|
49
|
-
}
|
|
50
|
-
const output = tool.outputSchema === undefined
|
|
51
|
-
? undefined
|
|
52
|
-
: toToolSchema(tool.outputSchema, problem);
|
|
53
|
-
const annotations = tool.annotations ?? {};
|
|
54
|
-
const title = tool.title ?? annotations.title;
|
|
55
|
-
out[tool.name] = {
|
|
56
|
-
...(title !== undefined ? { title } : {}),
|
|
57
|
-
description: tool.description?.trim() ? tool.description : tool.name,
|
|
58
|
-
risk: options.risk?.(tool) ?? defaultRisk(annotations),
|
|
59
|
-
input: toToolSchema(tool.inputSchema ?? {}, problem),
|
|
60
|
-
...(output ? { output } : {}),
|
|
61
|
-
...(annotations.idempotentHint !== undefined
|
|
62
|
-
? { idempotent: annotations.idempotentHint }
|
|
63
|
-
: {}),
|
|
64
|
-
...(annotations.openWorldHint !== undefined
|
|
65
|
-
? { openWorld: annotations.openWorldHint }
|
|
66
|
-
: {}),
|
|
67
|
-
handler: async (args, context) => {
|
|
68
|
-
const extra = options.context
|
|
69
|
-
? await options.context(context)
|
|
70
|
-
: context;
|
|
71
|
-
const result = await tool.handler(args, extra);
|
|
72
|
-
return fromCallToolResult(result);
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
return out;
|
|
77
|
-
}
|
|
78
|
-
function defaultRisk(annotations) {
|
|
79
|
-
if (annotations.readOnlyHint === true)
|
|
80
|
-
return "read";
|
|
81
|
-
return annotations.destructiveHint === true ? "destructive" : "write";
|
|
82
|
-
}
|
|
83
|
-
function fromCallToolResult(result) {
|
|
84
|
-
if (!isRecord(result)) {
|
|
85
|
-
throw new Error("the MCP tool did not return a CallToolResult");
|
|
86
|
-
}
|
|
87
|
-
const text = (Array.isArray(result.content) ? result.content : [])
|
|
88
|
-
.flatMap((part) => isRecord(part) && part.type === "text" && typeof part.text === "string"
|
|
89
|
-
? [part.text]
|
|
90
|
-
: [])
|
|
91
|
-
.join("\n\n");
|
|
92
|
-
if (result.isError === true)
|
|
93
|
-
throw new ToolError(text || "The tool failed.");
|
|
94
|
-
const structured = result.structuredContent;
|
|
95
|
-
if (isRecord(structured) && !Array.isArray(structured)) {
|
|
96
|
-
return toolResult({ text, structured });
|
|
97
|
-
}
|
|
98
|
-
return text;
|
|
99
|
-
}
|
|
100
|
-
function isRecord(value) {
|
|
101
|
-
return typeof value === "object" && value !== null;
|
|
102
|
-
}
|
|
103
|
-
function isStandardWithJson(value) {
|
|
104
|
-
if (!isRecord(value))
|
|
105
|
-
return false;
|
|
106
|
-
const standard = value["~standard"];
|
|
107
|
-
return (isRecord(standard) &&
|
|
108
|
-
typeof standard.validate === "function" &&
|
|
109
|
-
isRecord(standard.jsonSchema));
|
|
110
|
-
}
|
|
111
|
-
function isPlainToolSchema(value) {
|
|
112
|
-
return isRecord(value.jsonSchema) && typeof value.validate === "function";
|
|
113
|
-
}
|
|
114
|
-
function toToolSchema(schema, problem) {
|
|
115
|
-
if (!isRecord(schema) || Array.isArray(schema)) {
|
|
116
|
-
throw problem("has a schema that is not an object");
|
|
117
|
-
}
|
|
118
|
-
if (isStandardWithJson(schema) || isPlainToolSchema(schema)) {
|
|
119
|
-
return schema;
|
|
120
|
-
}
|
|
121
|
-
if ("~standard" in schema) {
|
|
122
|
-
throw problem("has a schema that doesn't convert to JSON Schema (zod >= 4.2 does)");
|
|
123
|
-
}
|
|
124
|
-
const values = Object.values(schema);
|
|
125
|
-
if (values.every((value) => isRecord(value) && "~standard" in value)) {
|
|
126
|
-
if (!values.every(isStandardWithJson)) {
|
|
127
|
-
throw problem("has a raw shape whose fields don't convert to JSON Schema (zod >= 4.2 does)");
|
|
128
|
-
}
|
|
129
|
-
return rawShapeSchema(schema);
|
|
130
|
-
}
|
|
131
|
-
return jsonSchemaOnly(schema);
|
|
132
|
-
}
|
|
133
|
-
/** A plain JSON Schema: described as is; any object passes. */
|
|
134
|
-
function jsonSchemaOnly(jsonSchema) {
|
|
135
|
-
return {
|
|
136
|
-
jsonSchema,
|
|
137
|
-
validate: (value) => isRecord(value) && !Array.isArray(value)
|
|
138
|
-
? { value }
|
|
139
|
-
: { issues: [{ message: "Expected an object" }] },
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* `{ key: schema }` as an object schema, the way `McpServer` wraps one in
|
|
144
|
-
* `z.object()`: unknown keys are dropped, and a field is optional when its
|
|
145
|
-
* schema accepts `undefined`.
|
|
146
|
-
*/
|
|
147
|
-
function rawShapeSchema(shape) {
|
|
148
|
-
const properties = {};
|
|
149
|
-
const required = [];
|
|
150
|
-
for (const [key, field] of Object.entries(shape)) {
|
|
151
|
-
const { $schema: _, ...property } = field["~standard"].jsonSchema.input({
|
|
152
|
-
target: JSON_SCHEMA_TARGET,
|
|
153
|
-
});
|
|
154
|
-
properties[key] = property;
|
|
155
|
-
const absent = field["~standard"].validate(undefined);
|
|
156
|
-
// An async validator can't be asked here; call the field required.
|
|
157
|
-
if (absent instanceof Promise || absent.issues)
|
|
158
|
-
required.push(key);
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
jsonSchema: {
|
|
162
|
-
type: "object",
|
|
163
|
-
properties,
|
|
164
|
-
...(required.length > 0 ? { required } : {}),
|
|
165
|
-
},
|
|
166
|
-
validate: async (value) => {
|
|
167
|
-
if (!isRecord(value) || Array.isArray(value)) {
|
|
168
|
-
return { issues: [{ message: "Expected an object" }] };
|
|
169
|
-
}
|
|
170
|
-
const parsed = {};
|
|
171
|
-
const issues = [];
|
|
172
|
-
for (const [key, field] of Object.entries(shape)) {
|
|
173
|
-
const result = await field["~standard"].validate(value[key]);
|
|
174
|
-
if (result.issues) {
|
|
175
|
-
for (const issue of result.issues) {
|
|
176
|
-
issues.push({
|
|
177
|
-
message: issue.message,
|
|
178
|
-
path: [
|
|
179
|
-
key,
|
|
180
|
-
...(issue.path ?? []).map((segment) => typeof segment === "object" ? segment.key : segment),
|
|
181
|
-
],
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else if (key in value || result.value !== undefined) {
|
|
186
|
-
parsed[key] = result.value;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return issues.length > 0 ? { issues } : { value: parsed };
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
}
|