@every-app/sdk 0.3.0-rc.11 → 0.3.0-rc.12

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.
Files changed (66) hide show
  1. package/README.md +6 -4
  2. package/dist/cloudflare/worker.d.ts +1 -1
  3. package/dist/cloudflare/worker.d.ts.map +1 -1
  4. package/dist/cloudflare/worker.js +1 -2
  5. package/dist/internal/aiDefault.d.ts +32 -76
  6. package/dist/internal/aiDefault.d.ts.map +1 -1
  7. package/dist/internal/aiDefault.js +41 -144
  8. package/dist/internal/appSettings.d.ts +14 -17
  9. package/dist/internal/appSettings.d.ts.map +1 -1
  10. package/dist/internal/appSettings.js +2 -16
  11. package/dist/internal/capabilities.d.ts +9 -8
  12. package/dist/internal/capabilities.d.ts.map +1 -1
  13. package/dist/internal/capabilities.js +2 -2
  14. package/dist/internal/catalog/commerce.js +1 -1
  15. package/dist/internal/gatewaySettings.d.ts +2 -2
  16. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  17. package/dist/internal/gatewaySettings.js +0 -6
  18. package/dist/internal/protocol.d.ts +5 -24
  19. package/dist/internal/protocol.d.ts.map +1 -1
  20. package/dist/internal/protocol.js +0 -19
  21. package/dist/internal/remoteMcp.d.ts +4 -6
  22. package/dist/internal/remoteMcp.d.ts.map +1 -1
  23. package/dist/internal/remoteMcp.js +7 -10
  24. package/dist/internal/tools.d.ts +11 -7
  25. package/dist/internal/tools.d.ts.map +1 -1
  26. package/dist/internal/tools.js +8 -8
  27. package/dist/manifest.d.ts +9 -25
  28. package/dist/manifest.d.ts.map +1 -1
  29. package/dist/server/ai.d.ts +1 -93
  30. package/dist/server/ai.d.ts.map +1 -1
  31. package/dist/server/ai.js +17 -111
  32. package/dist/server/attribution.d.ts +1 -1
  33. package/dist/server/context.d.ts +12 -14
  34. package/dist/server/context.d.ts.map +1 -1
  35. package/dist/server/context.js +23 -25
  36. package/dist/server/everyApp.d.ts +4 -23
  37. package/dist/server/everyApp.d.ts.map +1 -1
  38. package/dist/server/everyApp.js +13 -46
  39. package/dist/server/gatewayFetch.d.ts +1 -1
  40. package/dist/server/gatewayFetch.js +1 -1
  41. package/dist/server/index.d.ts +3 -7
  42. package/dist/server/index.d.ts.map +1 -1
  43. package/dist/server/index.js +3 -7
  44. package/dist/server/integrationFetch.d.ts +1 -1
  45. package/dist/server/refusal.d.ts +0 -9
  46. package/dist/server/refusal.d.ts.map +1 -1
  47. package/dist/server/refusal.js +0 -22
  48. package/dist/server/tools.d.ts +11 -8
  49. package/dist/server/tools.d.ts.map +1 -1
  50. package/dist/server/tools.js +10 -4
  51. package/dist/server/toolsEndpoint.d.ts +2 -3
  52. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  53. package/dist/server/toolsEndpoint.js +6 -11
  54. package/dist/testing/index.d.ts +2 -3
  55. package/dist/testing/index.d.ts.map +1 -1
  56. package/dist/testing/index.js +2 -6
  57. package/package.json +1 -1
  58. package/dist/server/aiBinding.d.ts +0 -68
  59. package/dist/server/aiBinding.d.ts.map +0 -1
  60. package/dist/server/aiBinding.js +0 -294
  61. package/dist/server/emailBinding.d.ts +0 -72
  62. package/dist/server/emailBinding.d.ts.map +0 -1
  63. package/dist/server/emailBinding.js +0 -124
  64. package/dist/server/fromMcpTools.d.ts +0 -89
  65. package/dist/server/fromMcpTools.d.ts.map +0 -1
  66. package/dist/server/fromMcpTools.js +0 -192
@@ -1,294 +0,0 @@
1
- /**
2
- * `gatewayAiBinding(env)`: the Gateway's AI providers as a Workers AI
3
- * binding, for code written against `env.AI.run(model, inputs, options)`.
4
- *
5
- * An app on Every App needs no `ai` binding of its own to call models: the
6
- * Gateway holds the keys, counts the calls and keeps the app to `ai.models`.
7
- * Code that already calls `env.AI.run()` (upstream code a port keeps, the
8
- * Agents SDK, `workers-ai-provider`) gets this object in its place:
9
- *
10
- * const AI = gatewayAiBinding(env, { for: request });
11
- * await AI.run("@cf/meta/llama-3.1-8b-instruct-fast", { messages });
12
- *
13
- * Each `run()` becomes an OpenAI-compatible call on the provider's route
14
- * through the Gateway (`createGatewayFetch`): text generation (`messages`
15
- * or `prompt`) on its chat completions route, text embeddings (`text`) on
16
- * its embeddings route, answered in Workers AI's shapes (`{ response,
17
- * tool_calls, usage }`, `{ shape, data }`, or a stream of `data:
18
- * {"response": …}` events with `stream: true`). In local development
19
- * without a Gateway, the developer's own key in `.dev.vars` is used, as
20
- * `createGatewayFetch` does (WORKERS_AI_API_TOKEN and CLOUDFLARE_ACCOUNT_ID
21
- * for Workers AI).
22
- */
23
- import { AI_DEFAULT_MODEL, AI_DEFAULT_PROVIDER, AI_DEFAULT_ROUTES, ConfigurationError, devAiProvider, } from "../internal/index.js";
24
- import { BINDING_ORIGIN, createGatewayFetch } from "./gatewayFetch.js";
25
- import { GatewayRefusalError, gatewayRefusal } from "./refusal.js";
26
- /**
27
- * A Workers AI binding (`Ai`) over the Gateway: pass it where code takes
28
- * `env.AI`. `run()` covers text generation (messages or prompt; tools;
29
- * `stream: true`; `returnRawResponse`, which returns the provider's
30
- * OpenAI-shaped `Response`) and text embeddings; the binding's other
31
- * methods (`gateway()`, `autorag()`, `models()`, `toMarkdown()`, …) and
32
- * other tasks (images, audio, batches, websockets) throw
33
- * `ConfigurationError`. `options.gateway` is ignored: the Gateway's
34
- * operator decides whether calls go through Cloudflare AI Gateway
35
- * (`everyapp ai gateway on`).
36
- *
37
- * Errors are thrown as a binding throws them: `GatewayRefusalError` when
38
- * the Gateway refused (`ai_provider_not_configured`, `model_not_allowed`,
39
- * …), `ConfigurationError` where createGatewayFetch throws one (no binding
40
- * and no dev key), and an `Error` naming the status when the provider
41
- * answered with an error. Nothing is checked when the binding is made, so
42
- * making one per request costs nothing.
43
- */
44
- export function gatewayAiBinding(env, options = {}) {
45
- const provider = options.provider ?? "workers-ai";
46
- async function run(model, inputs, runOptions = {}) {
47
- if (runOptions.websocket) {
48
- throw unsupported("run() with websocket: true");
49
- }
50
- if (!inputs || typeof inputs !== "object") {
51
- throw unsupported("run() without an inputs object");
52
- }
53
- if (Array.isArray(inputs["requests"])) {
54
- throw unsupported("run() with a batch of requests");
55
- }
56
- const embedding = !("messages" in inputs) && !("prompt" in inputs) && "text" in inputs;
57
- if (!embedding && !("messages" in inputs) && !("prompt" in inputs)) {
58
- throw unsupported("run() for anything but text generation (messages or prompt) and text embeddings (text)");
59
- }
60
- const url = `${BINDING_ORIGIN}/api/ai/${provider}${routeOf(provider, embedding ? "embeddings" : "chat")}`;
61
- const sent = modelToSend(provider, model, options.models);
62
- const body = embedding
63
- ? { model: sent, input: inputs["text"] }
64
- : chatBody(sent, inputs);
65
- const gatewayFetch = createGatewayFetch({
66
- env,
67
- for: options.for,
68
- });
69
- const response = await gatewayFetch(url, {
70
- method: "POST",
71
- headers: { "content-type": "application/json" },
72
- body: JSON.stringify(body),
73
- ...(runOptions.signal ? { signal: runOptions.signal } : {}),
74
- });
75
- if (runOptions.returnRawResponse)
76
- return response;
77
- await throwIfFailed(response, provider);
78
- if (embedding)
79
- return embeddingOutput(await response.json());
80
- if (inputs["stream"] === true) {
81
- if (!response.body)
82
- return new ReadableStream();
83
- return response.body.pipeThrough(toWorkersAiStream());
84
- }
85
- return textOutput(await response.json());
86
- }
87
- const notHere = (method) => () => {
88
- throw unsupported(`env.AI.${method}()`);
89
- };
90
- const binding = {
91
- aiGatewayLogId: null,
92
- run,
93
- gateway: notHere("gateway"),
94
- autorag: notHere("autorag"),
95
- aiSearch: notHere("aiSearch"),
96
- websearch: notHere("websearch"),
97
- models: notHere("models"),
98
- toMarkdown: notHere("toMarkdown"),
99
- };
100
- // `Ai` is the app's own Workers types' class (it changes with each
101
- // version); the object above is its working subset, the rest throwing.
102
- return binding;
103
- }
104
- function unsupported(what) {
105
- return new ConfigurationError(`${what} isn't available through the Every App gateway; call the provider's routes with createGatewayFetch() instead.`);
106
- }
107
- /** The provider's path for a chat or an embeddings call, under `/api/ai/<provider>`. */
108
- function routeOf(provider, kind) {
109
- if (provider === AI_DEFAULT_PROVIDER)
110
- return AI_DEFAULT_ROUTES[kind];
111
- const spec = devAiProvider(provider);
112
- if (!spec) {
113
- throw new ConfigurationError(`gatewayAiBinding: ${JSON.stringify(provider)} is not an AI provider of the catalog.`);
114
- }
115
- const route = spec[kind];
116
- if (!route) {
117
- throw new ConfigurationError(`gatewayAiBinding: ${provider} has no OpenAI-compatible ${kind === "chat" ? "chat completions" : "embeddings"} route.`);
118
- }
119
- return route.path;
120
- }
121
- function modelToSend(provider, model, models) {
122
- const mapped = models && Object.hasOwn(models, model) ? models[model] : null;
123
- if (mapped)
124
- return mapped;
125
- if (provider === AI_DEFAULT_PROVIDER && model.startsWith("@cf/")) {
126
- return AI_DEFAULT_MODEL;
127
- }
128
- return model;
129
- }
130
- /**
131
- * Workers AI's text generation inputs as a chat completions body: `prompt`
132
- * as one user message, legacy tools (`{ name, description, parameters }`)
133
- * as OpenAI's, everything else (max_tokens, temperature, response_format,
134
- * stream, …) as it is. Never adds `stream_options` (Workers AI refuses it
135
- * on a call that doesn't stream).
136
- */
137
- function chatBody(model, inputs) {
138
- const { prompt, messages, tools, raw: _raw, lora: _lora, ...rest } = inputs;
139
- const body = {
140
- ...rest,
141
- model,
142
- messages: messages ??
143
- (typeof prompt === "string" ? [{ role: "user", content: prompt }] : []),
144
- };
145
- if (Array.isArray(tools))
146
- body["tools"] = tools.map(openAiTool);
147
- return body;
148
- }
149
- function openAiTool(tool) {
150
- if (!isRecord(tool) || "function" in tool)
151
- return tool;
152
- const { name, description, parameters } = tool;
153
- return {
154
- type: "function",
155
- function: {
156
- name,
157
- ...(description === undefined ? {} : { description }),
158
- ...(parameters === undefined ? {} : { parameters }),
159
- },
160
- };
161
- }
162
- async function throwIfFailed(response, provider) {
163
- if (response.ok)
164
- return;
165
- const refusal = await gatewayRefusal(response.clone());
166
- if (refusal)
167
- throw new GatewayRefusalError(refusal);
168
- const text = await response.text().catch(() => "");
169
- throw new Error(`AI (${provider}) answered ${response.status}${text ? `: ${text.slice(0, 500)}` : ""}`);
170
- }
171
- /** A chat completion as Workers AI answers text generation. */
172
- function textOutput(body) {
173
- const choice = firstChoice(body);
174
- const message = isRecord(choice?.["message"]) ? choice["message"] : {};
175
- const toolCalls = workersAiToolCalls(message["tool_calls"]);
176
- const usage = isRecord(body) ? body["usage"] : undefined;
177
- return {
178
- // Workers AI may send an all-digit reply as a JSON number.
179
- response: textOf(message["content"]),
180
- ...(toolCalls ? { tool_calls: toolCalls } : {}),
181
- ...(isRecord(usage) ? { usage } : {}),
182
- };
183
- }
184
- /**
185
- * OpenAI tool calls as both forms Workers AI answers with: the legacy
186
- * `{ name, arguments }` (arguments parsed) and the OpenAI-shaped
187
- * `{ id, type, function }`.
188
- */
189
- function workersAiToolCalls(calls) {
190
- if (!Array.isArray(calls) || calls.length === 0)
191
- return undefined;
192
- return calls.filter(isRecord).map((call) => {
193
- const fn = isRecord(call["function"]) ? call["function"] : {};
194
- const args = fn["arguments"];
195
- return {
196
- ...(call["id"] === undefined ? {} : { id: call["id"] }),
197
- type: "function",
198
- function: { name: fn["name"], arguments: args },
199
- name: fn["name"],
200
- arguments: typeof args === "string" ? parsedOr(args) : args,
201
- };
202
- });
203
- }
204
- /** Embeddings as Workers AI answers them: `{ shape: [rows, dimensions], data }`. */
205
- function embeddingOutput(body) {
206
- const rows = isRecord(body) && Array.isArray(body["data"]) ? body["data"] : [];
207
- const data = rows
208
- .filter(isRecord)
209
- .sort((a, b) => Number(a["index"] ?? 0) - Number(b["index"] ?? 0))
210
- .map((row) => Array.isArray(row["embedding"]) ? row["embedding"] : []);
211
- return { shape: [data.length, data[0]?.length ?? 0], data };
212
- }
213
- /**
214
- * An OpenAI chat completions event stream as Workers AI streams text:
215
- * one `data: {"response": "<token>"}` event per chunk with text (tool call
216
- * deltas and usage alongside), then `data: [DONE]`.
217
- */
218
- function toWorkersAiStream() {
219
- const decoder = new TextDecoder();
220
- const encoder = new TextEncoder();
221
- let pending = "";
222
- const convert = (line) => {
223
- const match = /^data:[ ]?(.*?)\r?$/s.exec(line);
224
- if (!match)
225
- return "";
226
- const data = match[1].trim();
227
- if (data === "[DONE]")
228
- return "data: [DONE]\n\n";
229
- let chunk;
230
- try {
231
- chunk = JSON.parse(data);
232
- }
233
- catch {
234
- return "";
235
- }
236
- const choice = firstChoice(chunk);
237
- const delta = isRecord(choice?.["delta"]) ? choice["delta"] : {};
238
- const usage = isRecord(chunk) ? chunk["usage"] : undefined;
239
- const toolCalls = Array.isArray(delta["tool_calls"])
240
- ? delta["tool_calls"]
241
- : undefined;
242
- const event = {
243
- response: textOf(delta["content"]),
244
- ...(toolCalls ? { tool_calls: toolCalls } : {}),
245
- ...(isRecord(usage) ? { usage } : {}),
246
- };
247
- if (!event.response && !toolCalls && !isRecord(usage))
248
- return "";
249
- return `data: ${JSON.stringify(event)}\n\n`;
250
- };
251
- return new TransformStream({
252
- transform(bytes, controller) {
253
- pending += decoder.decode(bytes, { stream: true });
254
- const end = pending.lastIndexOf("\n");
255
- if (end === -1)
256
- return;
257
- const lines = pending.slice(0, end).split("\n");
258
- pending = pending.slice(end + 1);
259
- const out = lines.map(convert).join("");
260
- if (out)
261
- controller.enqueue(encoder.encode(out));
262
- },
263
- flush(controller) {
264
- pending += decoder.decode();
265
- const out = convert(pending);
266
- if (out)
267
- controller.enqueue(encoder.encode(out));
268
- },
269
- });
270
- }
271
- function firstChoice(body) {
272
- if (!isRecord(body) || !Array.isArray(body["choices"]))
273
- return undefined;
274
- const choice = body["choices"][0];
275
- return isRecord(choice) ? choice : undefined;
276
- }
277
- function textOf(content) {
278
- if (typeof content === "string")
279
- return content;
280
- if (typeof content === "number")
281
- return String(content);
282
- return "";
283
- }
284
- function parsedOr(text) {
285
- try {
286
- return JSON.parse(text);
287
- }
288
- catch {
289
- return text;
290
- }
291
- }
292
- function isRecord(value) {
293
- return typeof value === "object" && value !== null && !Array.isArray(value);
294
- }
@@ -1,72 +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 { type SendEmailMessage } from "../internal/index.js";
19
- import type { GatewayCallFor } from "./attribution.js";
20
- export interface GatewayEmailBindingOptions {
21
- /**
22
- * Whom the mail is for: the inbound request, or `{ background: true,
23
- * organizationId }` (sendEmail's `for`).
24
- */
25
- for?: GatewayCallFor;
26
- }
27
- /** `name <email>` or a bare address, as the binding takes them. */
28
- type BindingAddress = string | {
29
- email: string;
30
- name?: string;
31
- };
32
- type BindingRecipients = BindingAddress | BindingAddress[];
33
- /** The structured message `send_email`'s `send()` takes (`EmailMessageBuilder`). */
34
- export interface GatewayEmailBindingMessage {
35
- from: BindingAddress;
36
- to?: BindingRecipients;
37
- cc?: BindingRecipients;
38
- bcc?: BindingRecipients;
39
- subject: string;
40
- replyTo?: BindingAddress;
41
- headers?: Record<string, string>;
42
- text?: string;
43
- html?: string;
44
- attachments?: {
45
- disposition?: "inline" | "attachment";
46
- contentId?: string;
47
- filename: string;
48
- type: string;
49
- content: string | ArrayBuffer | ArrayBufferView;
50
- }[];
51
- }
52
- /**
53
- * A `send_email` binding (`SendEmail`) over the Gateway: pass it where code
54
- * takes `env.EMAIL`. `send()` takes the structured message (`from`, `to`,
55
- * `cc`, `bcc`, `subject`, `text`, `html`, `replyTo`, `headers`,
56
- * `attachments`) and resolves `{ messageId }`. A raw MIME `EmailMessage`
57
- * (`new EmailMessage(from, to, raw)`) is refused with `GatewayRefusalError`
58
- * `email_invalid_message`: the Gateway takes structured mail only.
59
- *
60
- * Errors are sendEmail()'s: `GatewayRefusalError` with the Gateway's code
61
- * (`email_sender_not_allowed`, `email_budget_exhausted`,
62
- * `integration_not_configured`, …) and `ConfigurationError` with neither a
63
- * binding nor `everyapp dev`. Nothing is checked when the binding is made.
64
- */
65
- export declare function gatewayEmailBinding(env: object, options?: GatewayEmailBindingOptions): SendEmail;
66
- /**
67
- * The binding's message as sendEmail()'s: recipients as bare addresses,
68
- * `from` and `replyTo` with their display names, attachments base64.
69
- */
70
- export declare function toSendEmailMessage(message: unknown): SendEmailMessage;
71
- export {};
72
- //# sourceMappingURL=emailBinding.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"emailBinding.d.ts","sourceRoot":"","sources":["../../src/server/emailBinding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAED,mEAAmE;AACnE,KAAK,cAAc,GAAG,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,KAAK,iBAAiB,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAE3D,oFAAoF;AACpF,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE;QACZ,WAAW,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;QACtC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,CAAC;KACjD,EAAE,CAAC;CACL;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,0BAA+B,GACvC,SAAS,CAcX;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA6BrE"}
@@ -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"}