@decocms/runtime 0.0.1-testing-beta.1

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 (85) hide show
  1. package/config-schema.json +553 -0
  2. package/dist/admin.d.ts +5 -0
  3. package/dist/admin.js +21 -0
  4. package/dist/admin.js.map +1 -0
  5. package/dist/bindings/deconfig/index.d.ts +9 -0
  6. package/dist/bindings/deconfig/index.js +9 -0
  7. package/dist/bindings/deconfig/index.js.map +1 -0
  8. package/dist/bindings/index.d.ts +1053 -0
  9. package/dist/bindings/index.js +132 -0
  10. package/dist/bindings/index.js.map +1 -0
  11. package/dist/chunk-4XSQKJLU.js +105 -0
  12. package/dist/chunk-4XSQKJLU.js.map +1 -0
  13. package/dist/chunk-AOFOWQXY.js +27 -0
  14. package/dist/chunk-AOFOWQXY.js.map +1 -0
  15. package/dist/chunk-F6XZPFWM.js +127 -0
  16. package/dist/chunk-F6XZPFWM.js.map +1 -0
  17. package/dist/chunk-IB3KGSMB.js +150 -0
  18. package/dist/chunk-IB3KGSMB.js.map +1 -0
  19. package/dist/chunk-NKUMVYKI.js +128 -0
  20. package/dist/chunk-NKUMVYKI.js.map +1 -0
  21. package/dist/chunk-NMXOC7PT.js +763 -0
  22. package/dist/chunk-NMXOC7PT.js.map +1 -0
  23. package/dist/chunk-OSSKGDAG.js +395 -0
  24. package/dist/chunk-OSSKGDAG.js.map +1 -0
  25. package/dist/chunk-UHR3BLMF.js +92 -0
  26. package/dist/chunk-UHR3BLMF.js.map +1 -0
  27. package/dist/client.d.ts +28 -0
  28. package/dist/client.js +4 -0
  29. package/dist/client.js.map +1 -0
  30. package/dist/connection-DDtQYrea.d.ts +30 -0
  31. package/dist/drizzle.d.ts +47 -0
  32. package/dist/drizzle.js +121 -0
  33. package/dist/drizzle.js.map +1 -0
  34. package/dist/index-AKVjfH4b.d.ts +336 -0
  35. package/dist/index-kMsI0ELb.d.ts +530 -0
  36. package/dist/index.d.ts +8 -0
  37. package/dist/index.js +507 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mastra.d.ts +8 -0
  40. package/dist/mastra.js +5 -0
  41. package/dist/mastra.js.map +1 -0
  42. package/dist/mcp-Bv7IAgWX.d.ts +109 -0
  43. package/dist/mcp-client.d.ts +236 -0
  44. package/dist/mcp-client.js +3 -0
  45. package/dist/mcp-client.js.map +1 -0
  46. package/dist/proxy.d.ts +10 -0
  47. package/dist/proxy.js +4 -0
  48. package/dist/proxy.js.map +1 -0
  49. package/dist/resources.d.ts +362 -0
  50. package/dist/resources.js +3 -0
  51. package/dist/resources.js.map +1 -0
  52. package/dist/views.d.ts +72 -0
  53. package/dist/views.js +3 -0
  54. package/dist/views.js.map +1 -0
  55. package/package.json +98 -0
  56. package/src/admin.ts +16 -0
  57. package/src/auth.ts +233 -0
  58. package/src/bindings/README.md +132 -0
  59. package/src/bindings/binder.ts +143 -0
  60. package/src/bindings/channels.ts +54 -0
  61. package/src/bindings/deconfig/helpers.ts +107 -0
  62. package/src/bindings/deconfig/index.ts +1 -0
  63. package/src/bindings/deconfig/resources.ts +659 -0
  64. package/src/bindings/deconfig/types.ts +106 -0
  65. package/src/bindings/index.ts +61 -0
  66. package/src/bindings/resources/bindings.ts +99 -0
  67. package/src/bindings/resources/helpers.ts +95 -0
  68. package/src/bindings/resources/schemas.ts +265 -0
  69. package/src/bindings/utils.ts +22 -0
  70. package/src/bindings/views.ts +14 -0
  71. package/src/bindings.ts +179 -0
  72. package/src/client.ts +201 -0
  73. package/src/connection.ts +53 -0
  74. package/src/drizzle.ts +201 -0
  75. package/src/http-client-transport.ts +66 -0
  76. package/src/index.ts +394 -0
  77. package/src/mastra.ts +666 -0
  78. package/src/mcp-client.ts +119 -0
  79. package/src/mcp.ts +171 -0
  80. package/src/proxy.ts +204 -0
  81. package/src/resources.ts +168 -0
  82. package/src/state.ts +44 -0
  83. package/src/views.ts +26 -0
  84. package/src/well-known.ts +20 -0
  85. package/src/wrangler.ts +146 -0
package/src/mastra.ts ADDED
@@ -0,0 +1,666 @@
1
+ /* oxlint-disable no-explicit-any */
2
+ /* oxlint-disable ban-types */
3
+ import { HttpServerTransport } from "@deco/mcp/http";
4
+ import {
5
+ createTool as mastraCreateTool,
6
+ Tool,
7
+ type ToolAction,
8
+ type ToolExecutionContext,
9
+ } from "@mastra/core";
10
+ import { RuntimeContext } from "@mastra/core/di";
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { z } from "zod";
13
+ import { zodToJsonSchema } from "zod-to-json-schema";
14
+ import type { DefaultEnv } from "./index.ts";
15
+ import {
16
+ ResourceCreateInputSchema,
17
+ ResourceCreateOutputSchema,
18
+ ResourceDeleteInputSchema,
19
+ ResourceDeleteOutputSchema,
20
+ ResourceSearchInputSchema,
21
+ ResourceSearchOutputSchema,
22
+ ResourcesListOutputSchema,
23
+ ResourcesReadInputSchema,
24
+ ResourcesReadOutputSchema,
25
+ ResourceUpdateInputSchema,
26
+ ResourceUpdateOutputSchema,
27
+ } from "./resources.ts";
28
+ import { createStateValidationTool, State } from "./state.ts";
29
+ import { ViewsListOutputSchema } from "./views.ts";
30
+
31
+ export const createRuntimeContext = (prev?: RuntimeContext<AppContext>) => {
32
+ const runtimeContext = new RuntimeContext<AppContext>();
33
+ const store = State.getStore();
34
+ if (!store) {
35
+ if (prev) {
36
+ return prev;
37
+ }
38
+ throw new Error("Missing context, did you forget to call State.bind?");
39
+ }
40
+ const { env, ctx, req } = store;
41
+ runtimeContext.set("env", env);
42
+ runtimeContext.set("ctx", ctx);
43
+ runtimeContext.set("req", req);
44
+ return runtimeContext;
45
+ };
46
+
47
+ /**
48
+ * creates a private tool that always ensure for athentication before being executed
49
+ */
50
+ export function createPrivateTool<
51
+ TSchemaIn extends z.ZodSchema = z.ZodSchema,
52
+ TSchemaOut extends z.ZodSchema | undefined = undefined,
53
+ TSuspendSchema extends z.ZodSchema = z.ZodSchema,
54
+ TResumeSchema extends z.ZodSchema = z.ZodSchema,
55
+ TContext extends
56
+ ToolExecutionContext<TSchemaIn> = ToolExecutionContext<TSchemaIn>,
57
+ TExecute extends ToolAction<
58
+ TSchemaIn,
59
+ TSchemaOut,
60
+ any,
61
+ any,
62
+ TContext
63
+ >["execute"] = ToolAction<
64
+ TSchemaIn,
65
+ TSchemaOut,
66
+ any,
67
+ any,
68
+ TContext
69
+ >["execute"],
70
+ >(
71
+ opts: ToolAction<
72
+ TSchemaIn,
73
+ TSchemaOut,
74
+ TSuspendSchema,
75
+ TResumeSchema,
76
+ TContext
77
+ > & {
78
+ execute?: TExecute;
79
+ },
80
+ ): [TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TExecute] extends [
81
+ z.ZodSchema,
82
+ z.ZodSchema,
83
+ z.ZodSchema,
84
+ z.ZodSchema,
85
+ Function,
86
+ ]
87
+ ? Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> & {
88
+ inputSchema: TSchemaIn;
89
+ outputSchema: TSchemaOut;
90
+ execute: (context: TContext) => Promise<any>;
91
+ }
92
+ : Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> {
93
+ const execute = opts.execute;
94
+ if (typeof execute === "function") {
95
+ opts.execute = ((input, options) => {
96
+ const env = input.runtimeContext.get("env") as DefaultEnv;
97
+ if (env) {
98
+ env.DECO_REQUEST_CONTEXT.ensureAuthenticated();
99
+ }
100
+ return execute(input, options);
101
+ }) as TExecute;
102
+ }
103
+ return createTool(opts);
104
+ }
105
+
106
+ export interface StreamableTool<TSchemaIn extends z.ZodSchema = z.ZodSchema> {
107
+ id: string;
108
+ inputSchema: TSchemaIn;
109
+ streamable?: true;
110
+ description?: string;
111
+ execute: (input: ToolExecutionContext<TSchemaIn>) => Promise<Response>;
112
+ }
113
+
114
+ export function createStreamableTool<
115
+ TSchemaIn extends z.ZodSchema = z.ZodSchema,
116
+ >(streamableTool: StreamableTool<TSchemaIn>): StreamableTool<TSchemaIn> {
117
+ return {
118
+ ...streamableTool,
119
+ execute: (input: ToolExecutionContext<TSchemaIn>) => {
120
+ return streamableTool.execute({
121
+ ...input,
122
+ runtimeContext: createRuntimeContext(input.runtimeContext),
123
+ });
124
+ },
125
+ };
126
+ }
127
+
128
+ export function createTool<
129
+ TSchemaIn extends z.ZodSchema | undefined = undefined,
130
+ TSchemaOut extends z.ZodSchema | undefined = undefined,
131
+ TSuspendSchema extends z.ZodSchema = z.ZodSchema,
132
+ TResumeSchema extends z.ZodSchema = z.ZodSchema,
133
+ TContext extends ToolExecutionContext<
134
+ TSchemaIn,
135
+ TSuspendSchema,
136
+ TResumeSchema
137
+ > = ToolExecutionContext<TSchemaIn, TSuspendSchema, TResumeSchema>,
138
+ TExecute extends ToolAction<
139
+ TSchemaIn,
140
+ TSchemaOut,
141
+ TSuspendSchema,
142
+ TResumeSchema,
143
+ TContext
144
+ >["execute"] = ToolAction<
145
+ TSchemaIn,
146
+ TSchemaOut,
147
+ TSuspendSchema,
148
+ TResumeSchema,
149
+ TContext
150
+ >["execute"],
151
+ >(
152
+ opts: ToolAction<
153
+ TSchemaIn,
154
+ TSchemaOut,
155
+ TSuspendSchema,
156
+ TResumeSchema,
157
+ TContext
158
+ > & {
159
+ execute?: TExecute;
160
+ },
161
+ ): [TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TExecute] extends [
162
+ z.ZodSchema,
163
+ z.ZodSchema,
164
+ z.ZodSchema,
165
+ z.ZodSchema,
166
+ Function,
167
+ ]
168
+ ? Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> & {
169
+ inputSchema: TSchemaIn;
170
+ outputSchema: TSchemaOut;
171
+ execute: (context: TContext) => Promise<any>;
172
+ }
173
+ : Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> {
174
+ // @ts-expect-error - TSchemaIn is not a ZodType
175
+ return mastraCreateTool({
176
+ ...opts,
177
+ execute:
178
+ typeof opts?.execute === "function"
179
+ ? (((input) => {
180
+ return opts.execute!({
181
+ ...input,
182
+ runtimeContext: createRuntimeContext(input.runtimeContext),
183
+ });
184
+ }) as TExecute)
185
+ : opts.execute,
186
+ });
187
+ }
188
+
189
+ export type ExecWithContext<TF extends (...args: any[]) => any> = (
190
+ input: Omit<Parameters<TF>[0], "runtimeContext"> & {
191
+ runtimeContext: RuntimeContext<AppContext>;
192
+ },
193
+ ) => ReturnType<TF>;
194
+
195
+ export interface ViewExport {
196
+ title: string;
197
+ icon: string;
198
+ url: string;
199
+ tools?: string[];
200
+ rules?: string[];
201
+ installBehavior?: "none" | "open" | "autoPin";
202
+ }
203
+
204
+ export type Resources<Env = any, TSchema extends z.ZodTypeAny = never> = Array<
205
+ (env: Env & DefaultEnv<TSchema>) => {
206
+ name: string;
207
+ icon: string;
208
+ title: string;
209
+ description?: string;
210
+ tools: {
211
+ read: (args: { uri: string }) => Promise<unknown>;
212
+ search: (args: {
213
+ term: string;
214
+ cursor?: string;
215
+ limit?: number;
216
+ }) => Promise<unknown>;
217
+ create?: (
218
+ args: z.infer<typeof ResourceCreateInputSchema>,
219
+ ) => Promise<unknown>;
220
+ update?: (
221
+ args: z.infer<typeof ResourceUpdateInputSchema>,
222
+ ) => Promise<unknown>;
223
+ delete?: (
224
+ args: z.infer<typeof ResourceDeleteInputSchema>,
225
+ ) => Promise<unknown>;
226
+ };
227
+ views?: {
228
+ list?: { url?: string; tools?: string[]; rules?: string[] };
229
+ detail?: {
230
+ url?: string;
231
+ mimeTypePattern?: string;
232
+ resourceName?: string;
233
+ tools?: string[];
234
+ rules?: string[];
235
+ };
236
+ };
237
+ }
238
+ >;
239
+ export interface Integration {
240
+ id: string;
241
+ appId: string;
242
+ }
243
+ export type CreatedTool =
244
+ | ReturnType<typeof createTool>
245
+ | ReturnType<typeof createStreamableTool>;
246
+ export function isStreamableTool(tool: CreatedTool): tool is StreamableTool {
247
+ return tool && "streamable" in tool && tool.streamable === true;
248
+ }
249
+ export interface CreateMCPServerOptions<
250
+ Env = any,
251
+ TSchema extends z.ZodTypeAny = never,
252
+ > {
253
+ before?: (env: Env & DefaultEnv<TSchema>) => Promise<void> | void;
254
+ oauth?: {
255
+ state?: TSchema;
256
+ scopes?: string[];
257
+ };
258
+ views?: (
259
+ env: Env & DefaultEnv<TSchema>,
260
+ ) => Promise<ViewExport[]> | ViewExport[];
261
+ resources?: Resources<Env, TSchema>;
262
+ tools?:
263
+ | Array<
264
+ (
265
+ env: Env & DefaultEnv<TSchema>,
266
+ ) =>
267
+ | Promise<CreatedTool>
268
+ | CreatedTool
269
+ | CreatedTool[]
270
+ | Promise<CreatedTool[]>
271
+ >
272
+ | ((
273
+ env: Env & DefaultEnv<TSchema>,
274
+ ) => CreatedTool[] | Promise<CreatedTool[]>);
275
+ }
276
+
277
+ export type Fetch<TEnv = any> = (
278
+ req: Request,
279
+ env: TEnv,
280
+ ctx: ExecutionContext,
281
+ ) => Promise<Response> | Response;
282
+
283
+ export interface AppContext<TEnv = any> {
284
+ env: TEnv;
285
+ ctx: { waitUntil: (promise: Promise<any>) => void };
286
+ req?: Request;
287
+ }
288
+
289
+ const decoChatOAuthToolsFor = <TSchema extends z.ZodTypeAny = never>({
290
+ state: schema,
291
+ scopes,
292
+ }: CreateMCPServerOptions<any, TSchema>["oauth"] = {}) => {
293
+ const jsonSchema = schema
294
+ ? zodToJsonSchema(schema)
295
+ : { type: "object", properties: {} };
296
+ return [
297
+ createTool({
298
+ id: "DECO_CHAT_OAUTH_START",
299
+ description: "OAuth for Deco Chat",
300
+ inputSchema: z.object({
301
+ returnUrl: z.string(),
302
+ }),
303
+ outputSchema: z.object({
304
+ stateSchema: z.any(),
305
+ scopes: z.array(z.string()).optional(),
306
+ }),
307
+ execute: () => {
308
+ return Promise.resolve({
309
+ stateSchema: jsonSchema,
310
+ scopes,
311
+ });
312
+ },
313
+ }),
314
+ ];
315
+ };
316
+
317
+ type CallTool = (opts: {
318
+ toolCallId: string;
319
+ toolCallInput: any;
320
+ }) => Promise<any>;
321
+
322
+ export type MCPServer<TEnv = any, TSchema extends z.ZodTypeAny = never> = {
323
+ fetch: Fetch<TEnv & DefaultEnv<TSchema>>;
324
+ callTool: CallTool;
325
+ };
326
+
327
+ export const createMCPServer = <
328
+ TEnv = any,
329
+ TSchema extends z.ZodTypeAny = never,
330
+ >(
331
+ options: CreateMCPServerOptions<TEnv, TSchema>,
332
+ ): MCPServer<TEnv, TSchema> => {
333
+ const createServer = async (bindings: TEnv & DefaultEnv<TSchema>) => {
334
+ await options.before?.(bindings);
335
+
336
+ const server = new McpServer(
337
+ { name: "@deco/mcp-api", version: "1.0.0" },
338
+ { capabilities: { tools: {} } },
339
+ );
340
+
341
+ // Resolve resources first; build resource tools; append later
342
+ const resolvedResources = await Promise.all(
343
+ options.resources?.map((r) => r(bindings)) ?? [],
344
+ );
345
+ const readHandlers = new Map<
346
+ string,
347
+ (a: { uri: string }) => Promise<any>
348
+ >();
349
+ const searchHandlers = new Map<
350
+ string,
351
+ (a: { term: string; cursor?: string; limit?: number }) => Promise<any>
352
+ >();
353
+ const createHandlers = new Map<string, (a: any) => Promise<any>>();
354
+ const updateHandlers = new Map<string, (a: any) => Promise<any>>();
355
+ const deleteHandlers = new Map<string, (a: any) => Promise<any>>();
356
+ for (const r of resolvedResources) {
357
+ if (r?.tools?.read) readHandlers.set(r.name, r.tools.read);
358
+ if (r?.tools?.search) searchHandlers.set(r.name, r.tools.search);
359
+ if (r?.tools?.create) createHandlers.set(r.name, r.tools.create);
360
+ if (r?.tools?.update) updateHandlers.set(r.name, r.tools.update);
361
+ if (r?.tools?.delete) deleteHandlers.set(r.name, r.tools.delete);
362
+ }
363
+ const resourceTools: ReturnType<typeof createTool>[] = [];
364
+ if (resolvedResources.length > 0) {
365
+ resourceTools.push(
366
+ createTool({
367
+ id: "DECO_CHAT_RESOURCES_READ",
368
+ description: "Read a resource by uri (name + uri)",
369
+ inputSchema: ResourcesReadInputSchema,
370
+ outputSchema: ResourcesReadOutputSchema,
371
+ execute: (input) => {
372
+ // @ts-expect-error - input.name is not a string
373
+ const fn = readHandlers.get(input.name);
374
+ if (!fn) {
375
+ // @ts-expect-error - input.name is not a string
376
+ throw new Error(`READ not implemented for ${input.name}`);
377
+ }
378
+ // @ts-expect-error - input.name is not a string
379
+ return fn({ uri: input.uri });
380
+ },
381
+ }),
382
+ );
383
+ resourceTools.push(
384
+ createTool({
385
+ id: "DECO_CHAT_RESOURCES_SEARCH",
386
+ description: "Search resources (name + term)",
387
+ inputSchema: ResourceSearchInputSchema,
388
+ outputSchema: ResourceSearchOutputSchema,
389
+ execute: (input) => {
390
+ // @ts-expect-error - input.name is not a string
391
+ const fn = searchHandlers.get(input.name);
392
+ if (!fn) {
393
+ // @ts-expect-error - input.name is not a string
394
+ throw new Error(`SEARCH not implemented for ${input.name}`);
395
+ }
396
+ // @ts-expect-error - input.name is not a string
397
+ const { term, cursor, limit } = input;
398
+ return fn({ term, cursor, limit });
399
+ },
400
+ }),
401
+ );
402
+ resourceTools.push(
403
+ createTool({
404
+ id: "DECO_CHAT_RESOURCES_CREATE",
405
+ description: "Create a resource (name + content)",
406
+ inputSchema: ResourceCreateInputSchema,
407
+ outputSchema: ResourceCreateOutputSchema,
408
+ execute: (input) => {
409
+ // @ts-expect-error - input.name is not a string
410
+ const fn = createHandlers.get(input.name);
411
+ if (!fn) {
412
+ // @ts-expect-error - input.name is not a string
413
+ throw new Error(`CREATE not implemented for ${input.name}`);
414
+ }
415
+ return fn(input);
416
+ },
417
+ }),
418
+ );
419
+ resourceTools.push(
420
+ createTool({
421
+ id: "DECO_CHAT_RESOURCES_UPDATE",
422
+ description: "Update a resource (name + uri)",
423
+ inputSchema: ResourceUpdateInputSchema,
424
+ outputSchema: ResourceUpdateOutputSchema,
425
+ execute: (input) => {
426
+ // @ts-expect-error - input.name is not a string
427
+ const fn = updateHandlers.get(input.name);
428
+ if (!fn) {
429
+ // @ts-expect-error - input.name is not a string
430
+ throw new Error(`UPDATE not implemented for ${input.name}`);
431
+ }
432
+ return fn(input);
433
+ },
434
+ }),
435
+ );
436
+ resourceTools.push(
437
+ createTool({
438
+ id: "DECO_CHAT_RESOURCES_DELETE",
439
+ description: "Delete a resource (name + uri)",
440
+ inputSchema: ResourceDeleteInputSchema,
441
+ outputSchema: ResourceDeleteOutputSchema,
442
+ execute: (input) => {
443
+ // @ts-expect-error - input.name is not a string
444
+ const fn = deleteHandlers.get(input.name);
445
+ if (!fn) {
446
+ // @ts-expect-error - input.name is not a string
447
+ throw new Error(`DELETE not implemented for ${input.name}`);
448
+ }
449
+ return fn(input);
450
+ },
451
+ }),
452
+ );
453
+ resourceTools.push(
454
+ createTool({
455
+ id: "DECO_CHAT_RESOURCES_LIST",
456
+ description: "List resource types",
457
+ inputSchema: z.object({}),
458
+ outputSchema: ResourcesListOutputSchema,
459
+ execute: () =>
460
+ Promise.resolve({
461
+ resources: resolvedResources.map((r) => ({
462
+ name: r.name,
463
+ icon: r.icon,
464
+ title: r.title,
465
+ description: r.description ?? "",
466
+ hasCreate: Boolean(createHandlers.get(r.name)),
467
+ hasUpdate: Boolean(updateHandlers.get(r.name)),
468
+ hasDelete: Boolean(deleteHandlers.get(r.name)),
469
+ })),
470
+ }),
471
+ }),
472
+ );
473
+ }
474
+
475
+ const toolsFn =
476
+ typeof options.tools === "function"
477
+ ? options.tools
478
+ : async (bindings: TEnv & DefaultEnv<TSchema>) => {
479
+ if (typeof options.tools === "function") {
480
+ return await options.tools(bindings);
481
+ }
482
+ return await Promise.all(
483
+ options.tools?.flatMap(async (tool) => {
484
+ const toolResult = tool(bindings);
485
+ const awaited = await toolResult;
486
+ if (Array.isArray(awaited)) {
487
+ return awaited;
488
+ }
489
+ return [awaited];
490
+ }) ?? [],
491
+ ).then((t) => t.flat());
492
+ };
493
+ const tools = await toolsFn(bindings);
494
+
495
+ tools.push(...decoChatOAuthToolsFor<TSchema>(options.oauth));
496
+ tools.push(createStateValidationTool(options.oauth?.state));
497
+
498
+ tools.push(
499
+ createTool({
500
+ id: `DECO_CHAT_VIEWS_LIST`,
501
+ description: "List views exposed by this MCP",
502
+ inputSchema: z.any(),
503
+ outputSchema: ViewsListOutputSchema,
504
+ execute: async () => {
505
+ const base = ((await options.views?.(bindings)) ?? []).map((v) => ({
506
+ id: undefined,
507
+ // Stable machine name for routing: UPPERCASE + underscores
508
+ name: v.title.toUpperCase().replace(/[^A-Z0-9]/g, "_"),
509
+ title: v.title,
510
+ description: undefined,
511
+ icon: v.icon,
512
+ url: v.url,
513
+ tools: v.tools ?? [],
514
+ rules: v.rules ?? [],
515
+ installBehavior: v.installBehavior ?? "none",
516
+ }));
517
+ const resourceViews = resolvedResources
518
+ .map((r) => {
519
+ const listUrl =
520
+ r.views?.list?.url ??
521
+ `internal://resource/list?name=${encodeURIComponent(r.name)}`;
522
+
523
+ // Default CRUD tool ids for resources
524
+ const defaultListTools: string[] = (() => {
525
+ const base = [
526
+ "DECO_CHAT_RESOURCES_LIST",
527
+ "DECO_CHAT_RESOURCES_READ",
528
+ "DECO_CHAT_RESOURCES_SEARCH",
529
+ ];
530
+ const canCreate = Boolean(createHandlers.get(r.name));
531
+ const canUpdate = Boolean(updateHandlers.get(r.name));
532
+ const canDelete = Boolean(deleteHandlers.get(r.name));
533
+ if (canCreate) base.push("DECO_CHAT_RESOURCES_CREATE");
534
+ if (canUpdate) base.push("DECO_CHAT_RESOURCES_UPDATE");
535
+ if (canDelete) base.push("DECO_CHAT_RESOURCES_DELETE");
536
+ return base;
537
+ })();
538
+
539
+ const defaultListRules: string[] = [
540
+ `You are viewing the ${
541
+ r.title ?? r.name
542
+ } resources list. Resources are changeable via Resource tools (DECO_CHAT_RESOURCES_*). Use the appropriate tools to read, search, create, update, or delete items; do not fabricate data.`,
543
+ ];
544
+
545
+ const list = [
546
+ {
547
+ name: `${r.name.toUpperCase()}_LIST`,
548
+ title: `${r.name} List`,
549
+ description: r.description,
550
+ icon: r.icon,
551
+ url: listUrl,
552
+ tools: r.views?.list?.tools ?? defaultListTools,
553
+ rules: r.views?.list?.rules ?? defaultListRules,
554
+ },
555
+ ];
556
+ const detailUrl =
557
+ r.views?.detail?.url ??
558
+ `internal://resource/detail?name=${encodeURIComponent(r.name)}`;
559
+ const detail = [
560
+ {
561
+ name: `${r.name.toUpperCase()}_DETAIL`,
562
+ title: `${r.name} Detail`,
563
+ description: r.description,
564
+ icon: r.icon,
565
+ url: detailUrl,
566
+ mimeTypePattern: r.views?.detail?.mimeTypePattern,
567
+ resourceName: r.views?.detail?.resourceName ?? r.name,
568
+ tools: r.views?.detail?.tools ?? [],
569
+ rules: r.views?.detail?.rules ?? [],
570
+ },
571
+ ];
572
+ return [...list, ...detail];
573
+ })
574
+ .flat();
575
+
576
+ return { views: [...base, ...resourceViews] };
577
+ },
578
+ }),
579
+ );
580
+
581
+ for (const tool of tools) {
582
+ server.registerTool(
583
+ tool.id,
584
+ {
585
+ _meta: {
586
+ streamable: isStreamableTool(tool),
587
+ },
588
+ description: tool.description,
589
+ inputSchema:
590
+ tool.inputSchema && "shape" in tool.inputSchema
591
+ ? (tool.inputSchema.shape as z.ZodRawShape)
592
+ : z.object({}).shape,
593
+ outputSchema: isStreamableTool(tool)
594
+ ? z.object({ bytes: z.record(z.string(), z.number()) }).shape
595
+ : tool.outputSchema &&
596
+ typeof tool.outputSchema === "object" &&
597
+ "shape" in tool.outputSchema
598
+ ? (tool.outputSchema.shape as z.ZodRawShape)
599
+ : z.object({}).shape,
600
+ },
601
+ async (args) => {
602
+ let result = await tool.execute!({
603
+ context: args,
604
+ runId: crypto.randomUUID(),
605
+ runtimeContext: createRuntimeContext(),
606
+ });
607
+
608
+ if (isStreamableTool(tool) && result instanceof Response) {
609
+ result = { bytes: await result.bytes() };
610
+ }
611
+ return {
612
+ structuredContent: result,
613
+ content: [
614
+ {
615
+ type: "text",
616
+ text: JSON.stringify(result),
617
+ },
618
+ ],
619
+ };
620
+ },
621
+ );
622
+ }
623
+
624
+ return { server, tools };
625
+ };
626
+
627
+ const fetch = async (
628
+ req: Request,
629
+ env: TEnv & DefaultEnv<TSchema>,
630
+ _ctx: ExecutionContext,
631
+ ) => {
632
+ const { server } = await createServer(env);
633
+ const transport = new HttpServerTransport();
634
+
635
+ await server.connect(transport);
636
+
637
+ return await transport.handleMessage(req);
638
+ };
639
+
640
+ const callTool: CallTool = async ({ toolCallId, toolCallInput }) => {
641
+ const currentState = State.getStore();
642
+ if (!currentState) {
643
+ throw new Error("Missing state, did you forget to call State.bind?");
644
+ }
645
+ const env = currentState?.env;
646
+ const { tools } = await createServer(env);
647
+ const tool = tools.find((t) => t.id === toolCallId);
648
+ const execute = tool?.execute;
649
+ if (!execute) {
650
+ throw new Error(
651
+ `Tool ${toolCallId} not found or does not have an execute function`,
652
+ );
653
+ }
654
+
655
+ return execute({
656
+ context: toolCallInput,
657
+ runId: crypto.randomUUID(),
658
+ runtimeContext: createRuntimeContext(),
659
+ });
660
+ };
661
+
662
+ return {
663
+ fetch,
664
+ callTool,
665
+ };
666
+ };