@decocms/runtime 0.28.0 → 1.0.0-alpha-candy.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 (97) hide show
  1. package/package.json +11 -77
  2. package/scripts/generate-json-schema.ts +24 -0
  3. package/src/asset-server/dev-server-proxy.ts +16 -0
  4. package/src/asset-server/index.ts +44 -0
  5. package/src/bindings/README.md +1 -1
  6. package/src/bindings/binder.ts +2 -5
  7. package/src/bindings/channels.ts +1 -1
  8. package/src/bindings/index.ts +1 -32
  9. package/src/bindings/language-model/utils.ts +0 -91
  10. package/src/bindings.ts +30 -108
  11. package/src/client.ts +1 -145
  12. package/src/index.ts +46 -175
  13. package/src/mcp.ts +8 -165
  14. package/src/proxy.ts +3 -62
  15. package/src/state.ts +1 -30
  16. package/src/tools.ts +336 -0
  17. package/src/wrangler.ts +5 -5
  18. package/tsconfig.json +8 -0
  19. package/dist/admin.d.ts +0 -5
  20. package/dist/admin.js +0 -21
  21. package/dist/admin.js.map +0 -1
  22. package/dist/bindings/deconfig/index.d.ts +0 -12
  23. package/dist/bindings/deconfig/index.js +0 -10
  24. package/dist/bindings/deconfig/index.js.map +0 -1
  25. package/dist/bindings/index.d.ts +0 -2312
  26. package/dist/bindings/index.js +0 -135
  27. package/dist/bindings/index.js.map +0 -1
  28. package/dist/chunk-3AWMDSOH.js +0 -96
  29. package/dist/chunk-3AWMDSOH.js.map +0 -1
  30. package/dist/chunk-4XSQKJLU.js +0 -105
  31. package/dist/chunk-4XSQKJLU.js.map +0 -1
  32. package/dist/chunk-5EYZ2LVM.js +0 -158
  33. package/dist/chunk-5EYZ2LVM.js.map +0 -1
  34. package/dist/chunk-7ITSLORK.js +0 -128
  35. package/dist/chunk-7ITSLORK.js.map +0 -1
  36. package/dist/chunk-I7BWSAN6.js +0 -49
  37. package/dist/chunk-I7BWSAN6.js.map +0 -1
  38. package/dist/chunk-L4OT2YDO.js +0 -27
  39. package/dist/chunk-L4OT2YDO.js.map +0 -1
  40. package/dist/chunk-SHQSNOFL.js +0 -769
  41. package/dist/chunk-SHQSNOFL.js.map +0 -1
  42. package/dist/chunk-UHR3BLMF.js +0 -92
  43. package/dist/chunk-UHR3BLMF.js.map +0 -1
  44. package/dist/chunk-UIJGM3NV.js +0 -518
  45. package/dist/chunk-UIJGM3NV.js.map +0 -1
  46. package/dist/chunk-ZPUT6RN6.js +0 -32
  47. package/dist/chunk-ZPUT6RN6.js.map +0 -1
  48. package/dist/client.d.ts +0 -28
  49. package/dist/client.js +0 -5
  50. package/dist/client.js.map +0 -1
  51. package/dist/d1-store.d.ts +0 -9
  52. package/dist/d1-store.js +0 -4
  53. package/dist/d1-store.js.map +0 -1
  54. package/dist/drizzle.d.ts +0 -49
  55. package/dist/drizzle.js +0 -121
  56. package/dist/drizzle.js.map +0 -1
  57. package/dist/index-B7U9jXW4.d.ts +0 -530
  58. package/dist/index-uCMd27hU.d.ts +0 -471
  59. package/dist/index.d.ts +0 -10
  60. package/dist/index.js +0 -637
  61. package/dist/index.js.map +0 -1
  62. package/dist/mastra.d.ts +0 -10
  63. package/dist/mastra.js +0 -6
  64. package/dist/mastra.js.map +0 -1
  65. package/dist/mcp-DYmQ2RQf.d.ts +0 -105
  66. package/dist/mcp-client.d.ts +0 -232
  67. package/dist/mcp-client.js +0 -4
  68. package/dist/mcp-client.js.map +0 -1
  69. package/dist/proxy.d.ts +0 -11
  70. package/dist/proxy.js +0 -5
  71. package/dist/proxy.js.map +0 -1
  72. package/dist/resources.d.ts +0 -362
  73. package/dist/resources.js +0 -4
  74. package/dist/resources.js.map +0 -1
  75. package/dist/views.d.ts +0 -72
  76. package/dist/views.js +0 -4
  77. package/dist/views.js.map +0 -1
  78. package/src/admin.ts +0 -16
  79. package/src/auth.ts +0 -233
  80. package/src/bindings/deconfig/helpers.ts +0 -107
  81. package/src/bindings/deconfig/index.ts +0 -1
  82. package/src/bindings/deconfig/resources.ts +0 -659
  83. package/src/bindings/deconfig/types.ts +0 -106
  84. package/src/bindings/language-model/ai-sdk.ts +0 -87
  85. package/src/bindings/language-model/index.ts +0 -4
  86. package/src/bindings/resources/bindings.ts +0 -99
  87. package/src/bindings/resources/helpers.ts +0 -95
  88. package/src/bindings/resources/schemas.ts +0 -265
  89. package/src/bindings/views.ts +0 -14
  90. package/src/cf-imports.ts +0 -1
  91. package/src/d1-store.ts +0 -34
  92. package/src/deprecated.ts +0 -59
  93. package/src/drizzle.ts +0 -201
  94. package/src/mastra.ts +0 -898
  95. package/src/resources.ts +0 -168
  96. package/src/views.ts +0 -26
  97. package/src/workflow.ts +0 -193
package/src/mastra.ts DELETED
@@ -1,898 +0,0 @@
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
- type Workflow,
10
- } from "@mastra/core";
11
- import { RuntimeContext } from "@mastra/core/di";
12
- import {
13
- createWorkflow,
14
- createStep as mastraCreateStep,
15
- type DefaultEngineType,
16
- type ExecuteFunction,
17
- type Step as MastraStep,
18
- } from "@mastra/core/workflows";
19
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
20
- import { z } from "zod/v3";
21
- import { zodToJsonSchema } from "zod-to-json-schema";
22
- import type { DefaultEnv } from "./index.ts";
23
- import {
24
- ResourceCreateInputSchema,
25
- ResourceCreateOutputSchema,
26
- ResourceDeleteInputSchema,
27
- ResourceDeleteOutputSchema,
28
- ResourceSearchInputSchema,
29
- ResourceSearchOutputSchema,
30
- ResourcesListOutputSchema,
31
- ResourcesReadInputSchema,
32
- ResourcesReadOutputSchema,
33
- ResourceUpdateInputSchema,
34
- ResourceUpdateOutputSchema,
35
- } from "./resources.ts";
36
- import { createStateValidationTool, State } from "./state.ts";
37
- import { ViewsListOutputSchema } from "./views.ts";
38
- export { createWorkflow };
39
-
40
- export { cloneStep, cloneWorkflow } from "@mastra/core/workflows";
41
-
42
- export const createRuntimeContext = (prev?: RuntimeContext<AppContext>) => {
43
- const runtimeContext = new RuntimeContext<AppContext>();
44
- const store = State.getStore();
45
- if (!store) {
46
- if (prev) {
47
- return prev;
48
- }
49
- throw new Error("Missing context, did you forget to call State.bind?");
50
- }
51
- const { env, ctx, req } = store;
52
- runtimeContext.set("env", env);
53
- runtimeContext.set("ctx", ctx);
54
- runtimeContext.set("req", req);
55
- return runtimeContext;
56
- };
57
-
58
- /**
59
- * creates a private tool that always ensure for athentication before being executed
60
- */
61
- export function createPrivateTool<
62
- TSchemaIn extends z.ZodSchema = z.ZodSchema,
63
- TSchemaOut extends z.ZodSchema | undefined = undefined,
64
- TSuspendSchema extends z.ZodSchema = z.ZodSchema,
65
- TResumeSchema extends z.ZodSchema = z.ZodSchema,
66
- TContext extends
67
- ToolExecutionContext<TSchemaIn> = ToolExecutionContext<TSchemaIn>,
68
- TExecute extends ToolAction<
69
- TSchemaIn,
70
- TSchemaOut,
71
- any,
72
- any,
73
- TContext
74
- >["execute"] = ToolAction<
75
- TSchemaIn,
76
- TSchemaOut,
77
- any,
78
- any,
79
- TContext
80
- >["execute"],
81
- >(
82
- opts: ToolAction<
83
- TSchemaIn,
84
- TSchemaOut,
85
- TSuspendSchema,
86
- TResumeSchema,
87
- TContext
88
- > & {
89
- execute?: TExecute;
90
- },
91
- ): [TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TExecute] extends [
92
- z.ZodSchema,
93
- z.ZodSchema,
94
- z.ZodSchema,
95
- z.ZodSchema,
96
- Function,
97
- ]
98
- ? Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> & {
99
- inputSchema: TSchemaIn;
100
- outputSchema: TSchemaOut;
101
- execute: (context: TContext) => Promise<any>;
102
- }
103
- : Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> {
104
- const execute = opts.execute;
105
- if (typeof execute === "function") {
106
- opts.execute = ((input, options) => {
107
- const env = input.runtimeContext.get("env") as DefaultEnv;
108
- if (env) {
109
- env.DECO_REQUEST_CONTEXT.ensureAuthenticated();
110
- }
111
- return execute(input, options);
112
- }) as TExecute;
113
- }
114
- return createTool(opts);
115
- }
116
-
117
- export interface StreamableTool<TSchemaIn extends z.ZodSchema = z.ZodSchema> {
118
- id: string;
119
- inputSchema: TSchemaIn;
120
- streamable?: true;
121
- description?: string;
122
- execute: (input: ToolExecutionContext<TSchemaIn>) => Promise<Response>;
123
- }
124
-
125
- export function createStreamableTool<
126
- TSchemaIn extends z.ZodSchema = z.ZodSchema,
127
- >(streamableTool: StreamableTool<TSchemaIn>): StreamableTool<TSchemaIn> {
128
- return {
129
- ...streamableTool,
130
- execute: (input: ToolExecutionContext<TSchemaIn>) => {
131
- const env = input.runtimeContext.get("env") as DefaultEnv;
132
- if (env) {
133
- env.DECO_REQUEST_CONTEXT.ensureAuthenticated();
134
- }
135
- return streamableTool.execute({
136
- ...input,
137
- runtimeContext: createRuntimeContext(input.runtimeContext),
138
- });
139
- },
140
- };
141
- }
142
-
143
- export function createTool<
144
- TSchemaIn extends z.ZodSchema | undefined = undefined,
145
- TSchemaOut extends z.ZodSchema | undefined = undefined,
146
- TSuspendSchema extends z.ZodSchema = z.ZodSchema,
147
- TResumeSchema extends z.ZodSchema = z.ZodSchema,
148
- TContext extends ToolExecutionContext<
149
- TSchemaIn,
150
- TSuspendSchema,
151
- TResumeSchema
152
- > = ToolExecutionContext<TSchemaIn, TSuspendSchema, TResumeSchema>,
153
- TExecute extends ToolAction<
154
- TSchemaIn,
155
- TSchemaOut,
156
- TSuspendSchema,
157
- TResumeSchema,
158
- TContext
159
- >["execute"] = ToolAction<
160
- TSchemaIn,
161
- TSchemaOut,
162
- TSuspendSchema,
163
- TResumeSchema,
164
- TContext
165
- >["execute"],
166
- >(
167
- opts: ToolAction<
168
- TSchemaIn,
169
- TSchemaOut,
170
- TSuspendSchema,
171
- TResumeSchema,
172
- TContext
173
- > & {
174
- execute?: TExecute;
175
- },
176
- ): [TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TExecute] extends [
177
- z.ZodSchema,
178
- z.ZodSchema,
179
- z.ZodSchema,
180
- z.ZodSchema,
181
- Function,
182
- ]
183
- ? Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> & {
184
- inputSchema: TSchemaIn;
185
- outputSchema: TSchemaOut;
186
- execute: (context: TContext) => Promise<any>;
187
- }
188
- : Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> {
189
- // @ts-expect-error - TSchemaIn is not a ZodType
190
- return mastraCreateTool({
191
- ...opts,
192
- execute:
193
- typeof opts?.execute === "function"
194
- ? (((input) => {
195
- return opts.execute!({
196
- ...input,
197
- runtimeContext: createRuntimeContext(input.runtimeContext),
198
- });
199
- }) as TExecute)
200
- : opts.execute,
201
- });
202
- }
203
-
204
- export type ExecWithContext<TF extends (...args: any[]) => any> = (
205
- input: Omit<Parameters<TF>[0], "runtimeContext"> & {
206
- runtimeContext: RuntimeContext<AppContext>;
207
- },
208
- ) => ReturnType<TF>;
209
-
210
- export interface Step<
211
- TStepId extends string = string,
212
- // @ts-expect-error - TState is not a ZodObject
213
- TState extends z.ZodObject<any> = z.ZodObject<any, z.$strip>,
214
- TSchemaIn extends z.ZodType<any> = z.ZodType<any>,
215
- TSchemaOut extends z.ZodType<any> = z.ZodType<any>,
216
- TResumeSchema extends z.ZodType<any> = z.ZodType<any>,
217
- TSuspendSchema extends z.ZodType<any> = z.ZodType<any>,
218
- TEngineType = any,
219
- > extends Omit<
220
- MastraStep<
221
- TStepId,
222
- TState,
223
- TSchemaIn,
224
- TSchemaOut,
225
- TResumeSchema,
226
- TSuspendSchema,
227
- TEngineType
228
- >,
229
- "execute"
230
- > {
231
- execute: ExecWithContext<
232
- ExecuteFunction<
233
- z.infer<TState>,
234
- z.infer<TSchemaIn>,
235
- z.infer<TSchemaOut>,
236
- z.infer<TResumeSchema>,
237
- z.infer<TSuspendSchema>,
238
- TEngineType
239
- >
240
- >;
241
- }
242
- export function createStepFromTool<
243
- TSchemaIn extends z.ZodType<any>,
244
- TSchemaOut extends z.ZodType<any>,
245
- TSuspendSchema extends z.ZodType<any>,
246
- TResumeSchema extends z.ZodType<any>,
247
- TContext extends ToolExecutionContext<
248
- TSchemaIn,
249
- TSuspendSchema,
250
- TResumeSchema
251
- >,
252
- >(
253
- tool: Tool<TSchemaIn, TSchemaOut, TSuspendSchema, TResumeSchema, TContext> & {
254
- inputSchema: TSchemaIn;
255
- outputSchema: TSchemaOut;
256
- execute: (context: TContext) => Promise<any>;
257
- },
258
- ): Step<
259
- string,
260
- // @ts-expect-error - TSchemaIn is not a ZodType
261
- TSchemaIn,
262
- TSchemaOut,
263
- z.ZodType<any>,
264
- z.ZodType<any>,
265
- DefaultEngineType
266
- > {
267
- // @ts-expect-error - TSchemaIn is not a ZodType
268
- return mastraCreateStep(tool);
269
- }
270
-
271
- export function createStep<
272
- TStepId extends string,
273
- TStepInput extends z.ZodType<any>,
274
- TStepOutput extends z.ZodType<any>,
275
- TResumeSchema extends z.ZodType<any>,
276
- TSuspendSchema extends z.ZodType<any>,
277
- >(opts: {
278
- id: TStepId;
279
- description?: string;
280
- inputSchema: TStepInput;
281
- outputSchema: TStepOutput;
282
- resumeSchema?: TResumeSchema;
283
- suspendSchema?: TSuspendSchema;
284
- execute: ExecWithContext<
285
- // @ts-expect-error - TStepInput is not a ZodObject
286
- ExecuteFunction<
287
- z.infer<TStepInput>,
288
- z.infer<TStepOutput>,
289
- z.infer<TResumeSchema>,
290
- z.infer<TSuspendSchema>,
291
- DefaultEngineType
292
- >
293
- >;
294
- }): Step<
295
- TStepId,
296
- // @ts-expect-error - TStepInput is not a ZodObject
297
- TStepInput,
298
- TStepOutput,
299
- TResumeSchema,
300
- TSuspendSchema,
301
- DefaultEngineType
302
- > {
303
- // @ts-expect-error - TStepInput is not a ZodObject
304
- return mastraCreateStep({
305
- ...opts,
306
- execute: (input) => {
307
- return opts.execute({
308
- ...input,
309
- runtimeContext: createRuntimeContext(input.runtimeContext),
310
- });
311
- },
312
- });
313
- }
314
-
315
- export interface ViewExport {
316
- title: string;
317
- icon: string;
318
- url: string;
319
- tools?: string[];
320
- rules?: string[];
321
- installBehavior?: "none" | "open" | "autoPin";
322
- }
323
-
324
- export type Resources<Env = any, TSchema extends z.ZodTypeAny = never> = Array<
325
- (env: Env & DefaultEnv<TSchema>) => {
326
- name: string;
327
- icon: string;
328
- title: string;
329
- description?: string;
330
- tools: {
331
- read: (args: { uri: string }) => Promise<unknown>;
332
- search: (args: {
333
- term: string;
334
- cursor?: string;
335
- limit?: number;
336
- }) => Promise<unknown>;
337
- create?: (
338
- args: z.infer<typeof ResourceCreateInputSchema>,
339
- ) => Promise<unknown>;
340
- update?: (
341
- args: z.infer<typeof ResourceUpdateInputSchema>,
342
- ) => Promise<unknown>;
343
- delete?: (
344
- args: z.infer<typeof ResourceDeleteInputSchema>,
345
- ) => Promise<unknown>;
346
- };
347
- views?: {
348
- list?: { url?: string; tools?: string[]; rules?: string[] };
349
- detail?: {
350
- url?: string;
351
- mimeTypePattern?: string;
352
- resourceName?: string;
353
- tools?: string[];
354
- rules?: string[];
355
- };
356
- };
357
- }
358
- >;
359
- export interface Integration {
360
- id: string;
361
- appId: string;
362
- }
363
- export type CreatedTool =
364
- | ReturnType<typeof createTool>
365
- | ReturnType<typeof createStreamableTool>;
366
- export function isStreamableTool(tool: CreatedTool): tool is StreamableTool {
367
- return tool && "streamable" in tool && tool.streamable === true;
368
- }
369
- export interface CreateMCPServerOptions<
370
- Env = any,
371
- TSchema extends z.ZodTypeAny = never,
372
- > {
373
- before?: (env: Env & DefaultEnv<TSchema>) => Promise<void> | void;
374
- oauth?: {
375
- state?: TSchema;
376
- scopes?: string[];
377
- };
378
- views?: (
379
- env: Env & DefaultEnv<TSchema>,
380
- ) => Promise<ViewExport[]> | ViewExport[];
381
- resources?: Resources<Env, TSchema>;
382
- tools?:
383
- | Array<
384
- (
385
- env: Env & DefaultEnv<TSchema>,
386
- ) =>
387
- | Promise<CreatedTool>
388
- | CreatedTool
389
- | CreatedTool[]
390
- | Promise<CreatedTool[]>
391
- >
392
- | ((
393
- env: Env & DefaultEnv<TSchema>,
394
- ) => CreatedTool[] | Promise<CreatedTool[]>);
395
- workflows?: Array<
396
- (
397
- env: Env & DefaultEnv<TSchema>,
398
- ) => // this is a workaround to allow workflows to be thenables
399
- | Promise<{ workflow: ReturnType<typeof createWorkflow> }>
400
- | ReturnType<typeof createWorkflow>
401
- >;
402
- }
403
-
404
- export type Fetch<TEnv = any> = (
405
- req: Request,
406
- env: TEnv,
407
- ctx: ExecutionContext,
408
- ) => Promise<Response> | Response;
409
-
410
- export interface AppContext<TEnv = any> {
411
- env: TEnv;
412
- ctx: { waitUntil: (promise: Promise<any>) => void };
413
- req?: Request;
414
- }
415
-
416
- const decoChatOAuthToolsFor = <TSchema extends z.ZodTypeAny = never>({
417
- state: schema,
418
- scopes,
419
- }: CreateMCPServerOptions<any, TSchema>["oauth"] = {}) => {
420
- const jsonSchema = schema
421
- ? zodToJsonSchema(schema)
422
- : { type: "object", properties: {} };
423
- return [
424
- createTool({
425
- id: "DECO_CHAT_OAUTH_START",
426
- description: "OAuth for Deco Chat",
427
- inputSchema: z.object({
428
- returnUrl: z.string(),
429
- }),
430
- outputSchema: z.object({
431
- stateSchema: z.any(),
432
- scopes: z.array(z.string()).optional(),
433
- }),
434
- execute: () => {
435
- return Promise.resolve({
436
- stateSchema: jsonSchema,
437
- scopes,
438
- });
439
- },
440
- }),
441
- ];
442
- };
443
-
444
- const createWorkflowTools = <TEnv = any, TSchema extends z.ZodTypeAny = never>(
445
- workflow: ReturnType<typeof createWorkflow>,
446
- bindings: TEnv & DefaultEnv<TSchema>,
447
- ) => {
448
- const startTool = createTool({
449
- id: `DECO_CHAT_WORKFLOWS_START_${workflow.id}`,
450
- description: workflow.description ?? `Start workflow ${workflow.id}`,
451
- inputSchema:
452
- workflow.inputSchema && "shape" in workflow.inputSchema
453
- ? workflow.inputSchema
454
- : z.object({}),
455
- outputSchema: z.object({
456
- id: z.string(),
457
- }),
458
- execute: async (args) => {
459
- const store = State.getStore();
460
- const runId =
461
- store?.req?.headers.get("x-deco-chat-run-id") ?? crypto.randomUUID();
462
- const workflowDO = bindings.DECO_WORKFLOW_DO.get(
463
- bindings.DECO_WORKFLOW_DO.idFromName(runId),
464
- );
465
-
466
- using result = await workflowDO.start({
467
- workflowId: workflow.id,
468
- args: args.context,
469
- runId,
470
- ctx: bindings.DECO_REQUEST_CONTEXT,
471
- });
472
- return { id: result.runId };
473
- },
474
- });
475
-
476
- const cancelTool = createTool({
477
- id: `DECO_CHAT_WORKFLOWS_CANCEL_${workflow.id}`,
478
- description: `Cancel ${workflow.description ?? `workflow ${workflow.id}`}`,
479
- inputSchema: z.object({ runId: z.string() }),
480
- outputSchema: z.object({ cancelled: z.boolean() }),
481
- execute: async (args) => {
482
- const runId = args.context.runId;
483
- const workflowDO = bindings.DECO_WORKFLOW_DO.get(
484
- bindings.DECO_WORKFLOW_DO.idFromName(runId),
485
- );
486
-
487
- using _ = await workflowDO.cancel({
488
- workflowId: workflow.id,
489
- runId,
490
- ctx: bindings.DECO_REQUEST_CONTEXT,
491
- });
492
-
493
- return { cancelled: true };
494
- },
495
- });
496
-
497
- const resumeTool = createTool({
498
- id: `DECO_CHAT_WORKFLOWS_RESUME_${workflow.id}`,
499
- description: `Resume ${workflow.description ?? `workflow ${workflow.id}`}`,
500
- inputSchema: z.object({
501
- runId: z.string(),
502
- stepId: z.string(),
503
- resumeData: z.any(),
504
- }),
505
- outputSchema: z.object({ resumed: z.boolean() }),
506
- execute: async (args) => {
507
- const runId = args.context.runId;
508
- const workflowDO = bindings.DECO_WORKFLOW_DO.get(
509
- bindings.DECO_WORKFLOW_DO.idFromName(runId),
510
- );
511
-
512
- using _ = await workflowDO.resume({
513
- workflowId: workflow.id,
514
- runId,
515
- resumeData: args.context.resumeData,
516
- stepId: args.context.stepId,
517
- ctx: bindings.DECO_REQUEST_CONTEXT,
518
- });
519
-
520
- return { resumed: true };
521
- },
522
- });
523
-
524
- return [startTool, cancelTool, resumeTool];
525
- };
526
-
527
- type CallTool = (opts: {
528
- toolCallId: string;
529
- toolCallInput: any;
530
- }) => Promise<any>;
531
-
532
- export type MCPServer<TEnv = any, TSchema extends z.ZodTypeAny = never> = {
533
- fetch: Fetch<TEnv & DefaultEnv<TSchema>>;
534
- callTool: CallTool;
535
- };
536
-
537
- export const isWorkflow = (value: any): value is Workflow => {
538
- return value && !(value instanceof Promise);
539
- };
540
-
541
- export const createMCPServer = <
542
- TEnv = any,
543
- TSchema extends z.ZodTypeAny = never,
544
- >(
545
- options: CreateMCPServerOptions<TEnv, TSchema>,
546
- ): MCPServer<TEnv, TSchema> => {
547
- const createServer = async (bindings: TEnv & DefaultEnv<TSchema>) => {
548
- await options.before?.(bindings);
549
-
550
- const server = new McpServer(
551
- { name: "@deco/mcp-api", version: "1.0.0" },
552
- { capabilities: { tools: {} } },
553
- );
554
-
555
- // Resolve resources first; build resource tools; append later
556
- const resolvedResources = await Promise.all(
557
- options.resources?.map((r) => r(bindings)) ?? [],
558
- );
559
- const readHandlers = new Map<
560
- string,
561
- (a: { uri: string }) => Promise<any>
562
- >();
563
- const searchHandlers = new Map<
564
- string,
565
- (a: { term: string; cursor?: string; limit?: number }) => Promise<any>
566
- >();
567
- const createHandlers = new Map<string, (a: any) => Promise<any>>();
568
- const updateHandlers = new Map<string, (a: any) => Promise<any>>();
569
- const deleteHandlers = new Map<string, (a: any) => Promise<any>>();
570
- for (const r of resolvedResources) {
571
- if (r?.tools?.read) readHandlers.set(r.name, r.tools.read);
572
- if (r?.tools?.search) searchHandlers.set(r.name, r.tools.search);
573
- if (r?.tools?.create) createHandlers.set(r.name, r.tools.create);
574
- if (r?.tools?.update) updateHandlers.set(r.name, r.tools.update);
575
- if (r?.tools?.delete) deleteHandlers.set(r.name, r.tools.delete);
576
- }
577
- const resourceTools: ReturnType<typeof createTool>[] = [];
578
- if (resolvedResources.length > 0) {
579
- resourceTools.push(
580
- createTool({
581
- id: "DECO_CHAT_RESOURCES_READ",
582
- description: "Read a resource by uri (name + uri)",
583
- inputSchema: ResourcesReadInputSchema,
584
- outputSchema: ResourcesReadOutputSchema,
585
- execute: (input) => {
586
- // @ts-expect-error - input.name is not a string
587
- const fn = readHandlers.get(input.name);
588
- if (!fn) {
589
- // @ts-expect-error - input.name is not a string
590
- throw new Error(`READ not implemented for ${input.name}`);
591
- }
592
- // @ts-expect-error - input.name is not a string
593
- return fn({ uri: input.uri });
594
- },
595
- }),
596
- );
597
- resourceTools.push(
598
- createTool({
599
- id: "DECO_CHAT_RESOURCES_SEARCH",
600
- description: "Search resources (name + term)",
601
- inputSchema: ResourceSearchInputSchema,
602
- outputSchema: ResourceSearchOutputSchema,
603
- execute: (input) => {
604
- // @ts-expect-error - input.name is not a string
605
- const fn = searchHandlers.get(input.name);
606
- if (!fn) {
607
- // @ts-expect-error - input.name is not a string
608
- throw new Error(`SEARCH not implemented for ${input.name}`);
609
- }
610
- // @ts-expect-error - input.name is not a string
611
- const { term, cursor, limit } = input;
612
- return fn({ term, cursor, limit });
613
- },
614
- }),
615
- );
616
- resourceTools.push(
617
- createTool({
618
- id: "DECO_CHAT_RESOURCES_CREATE",
619
- description: "Create a resource (name + content)",
620
- inputSchema: ResourceCreateInputSchema,
621
- outputSchema: ResourceCreateOutputSchema,
622
- execute: (input) => {
623
- // @ts-expect-error - input.name is not a string
624
- const fn = createHandlers.get(input.name);
625
- if (!fn) {
626
- // @ts-expect-error - input.name is not a string
627
- throw new Error(`CREATE not implemented for ${input.name}`);
628
- }
629
- return fn(input);
630
- },
631
- }),
632
- );
633
- resourceTools.push(
634
- createTool({
635
- id: "DECO_CHAT_RESOURCES_UPDATE",
636
- description: "Update a resource (name + uri)",
637
- inputSchema: ResourceUpdateInputSchema,
638
- outputSchema: ResourceUpdateOutputSchema,
639
- execute: (input) => {
640
- // @ts-expect-error - input.name is not a string
641
- const fn = updateHandlers.get(input.name);
642
- if (!fn) {
643
- // @ts-expect-error - input.name is not a string
644
- throw new Error(`UPDATE not implemented for ${input.name}`);
645
- }
646
- return fn(input);
647
- },
648
- }),
649
- );
650
- resourceTools.push(
651
- createTool({
652
- id: "DECO_CHAT_RESOURCES_DELETE",
653
- description: "Delete a resource (name + uri)",
654
- inputSchema: ResourceDeleteInputSchema,
655
- outputSchema: ResourceDeleteOutputSchema,
656
- execute: (input) => {
657
- // @ts-expect-error - input.name is not a string
658
- const fn = deleteHandlers.get(input.name);
659
- if (!fn) {
660
- // @ts-expect-error - input.name is not a string
661
- throw new Error(`DELETE not implemented for ${input.name}`);
662
- }
663
- return fn(input);
664
- },
665
- }),
666
- );
667
- resourceTools.push(
668
- createTool({
669
- id: "DECO_CHAT_RESOURCES_LIST",
670
- description: "List resource types",
671
- inputSchema: z.object({}),
672
- outputSchema: ResourcesListOutputSchema,
673
- execute: () =>
674
- Promise.resolve({
675
- resources: resolvedResources.map((r) => ({
676
- name: r.name,
677
- icon: r.icon,
678
- title: r.title,
679
- description: r.description ?? "",
680
- hasCreate: Boolean(createHandlers.get(r.name)),
681
- hasUpdate: Boolean(updateHandlers.get(r.name)),
682
- hasDelete: Boolean(deleteHandlers.get(r.name)),
683
- })),
684
- }),
685
- }),
686
- );
687
- }
688
-
689
- const toolsFn =
690
- typeof options.tools === "function"
691
- ? options.tools
692
- : async (bindings: TEnv & DefaultEnv<TSchema>) => {
693
- if (typeof options.tools === "function") {
694
- return await options.tools(bindings);
695
- }
696
- return await Promise.all(
697
- options.tools?.flatMap(async (tool) => {
698
- const toolResult = tool(bindings);
699
- const awaited = await toolResult;
700
- if (Array.isArray(awaited)) {
701
- return awaited;
702
- }
703
- return [awaited];
704
- }) ?? [],
705
- ).then((t) => t.flat());
706
- };
707
- const tools = await toolsFn(bindings);
708
-
709
- // since mastra workflows are thenables, we need to await and add as a prop
710
- const workflows = await Promise.all(
711
- options.workflows?.map(async (workflow) => {
712
- const workflowResult = workflow(bindings);
713
- if (isWorkflow(workflowResult)) {
714
- return { workflow: workflowResult };
715
- }
716
-
717
- return await workflowResult;
718
- }) ?? [],
719
- ).then((w) => w.map((w) => w.workflow));
720
-
721
- const workflowTools =
722
- workflows?.flatMap((workflow) =>
723
- createWorkflowTools(workflow, bindings),
724
- ) ?? [];
725
-
726
- tools.push(...workflowTools);
727
- tools.push(...decoChatOAuthToolsFor<TSchema>(options.oauth));
728
- tools.push(createStateValidationTool(options.oauth?.state));
729
-
730
- tools.push(
731
- createTool({
732
- id: `DECO_CHAT_VIEWS_LIST`,
733
- description: "List views exposed by this MCP",
734
- inputSchema: z.any(),
735
- outputSchema: ViewsListOutputSchema,
736
- execute: async () => {
737
- const base = ((await options.views?.(bindings)) ?? []).map((v) => ({
738
- id: undefined,
739
- // Stable machine name for routing: UPPERCASE + underscores
740
- name: v.title.toUpperCase().replace(/[^A-Z0-9]/g, "_"),
741
- title: v.title,
742
- description: undefined,
743
- icon: v.icon,
744
- url: v.url,
745
- tools: v.tools ?? [],
746
- rules: v.rules ?? [],
747
- installBehavior: v.installBehavior ?? "none",
748
- }));
749
- const resourceViews = resolvedResources
750
- .map((r) => {
751
- const listUrl =
752
- r.views?.list?.url ??
753
- `internal://resource/list?name=${encodeURIComponent(r.name)}`;
754
-
755
- // Default CRUD tool ids for resources
756
- const defaultListTools: string[] = (() => {
757
- const base = [
758
- "DECO_CHAT_RESOURCES_LIST",
759
- "DECO_CHAT_RESOURCES_READ",
760
- "DECO_CHAT_RESOURCES_SEARCH",
761
- ];
762
- const canCreate = Boolean(createHandlers.get(r.name));
763
- const canUpdate = Boolean(updateHandlers.get(r.name));
764
- const canDelete = Boolean(deleteHandlers.get(r.name));
765
- if (canCreate) base.push("DECO_CHAT_RESOURCES_CREATE");
766
- if (canUpdate) base.push("DECO_CHAT_RESOURCES_UPDATE");
767
- if (canDelete) base.push("DECO_CHAT_RESOURCES_DELETE");
768
- return base;
769
- })();
770
-
771
- const defaultListRules: string[] = [
772
- `You are viewing the ${
773
- r.title ?? r.name
774
- } 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.`,
775
- ];
776
-
777
- const list = [
778
- {
779
- name: `${r.name.toUpperCase()}_LIST`,
780
- title: `${r.name} List`,
781
- description: r.description,
782
- icon: r.icon,
783
- url: listUrl,
784
- tools: r.views?.list?.tools ?? defaultListTools,
785
- rules: r.views?.list?.rules ?? defaultListRules,
786
- },
787
- ];
788
- const detailUrl =
789
- r.views?.detail?.url ??
790
- `internal://resource/detail?name=${encodeURIComponent(r.name)}`;
791
- const detail = [
792
- {
793
- name: `${r.name.toUpperCase()}_DETAIL`,
794
- title: `${r.name} Detail`,
795
- description: r.description,
796
- icon: r.icon,
797
- url: detailUrl,
798
- mimeTypePattern: r.views?.detail?.mimeTypePattern,
799
- resourceName: r.views?.detail?.resourceName ?? r.name,
800
- tools: r.views?.detail?.tools ?? [],
801
- rules: r.views?.detail?.rules ?? [],
802
- },
803
- ];
804
- return [...list, ...detail];
805
- })
806
- .flat();
807
-
808
- return { views: [...base, ...resourceViews] };
809
- },
810
- }),
811
- );
812
-
813
- for (const tool of tools) {
814
- server.registerTool(
815
- tool.id,
816
- {
817
- _meta: {
818
- streamable: isStreamableTool(tool),
819
- },
820
- description: tool.description,
821
- inputSchema:
822
- tool.inputSchema && "shape" in tool.inputSchema
823
- ? (tool.inputSchema.shape as z.ZodRawShape)
824
- : z.object({}).shape,
825
- outputSchema: isStreamableTool(tool)
826
- ? z.object({ bytes: z.record(z.string(), z.number()) }).shape
827
- : tool.outputSchema &&
828
- typeof tool.outputSchema === "object" &&
829
- "shape" in tool.outputSchema
830
- ? (tool.outputSchema.shape as z.ZodRawShape)
831
- : z.object({}).shape,
832
- },
833
- async (args) => {
834
- let result = await tool.execute!({
835
- context: args,
836
- runId: crypto.randomUUID(),
837
- runtimeContext: createRuntimeContext(),
838
- });
839
-
840
- if (isStreamableTool(tool) && result instanceof Response) {
841
- result = { bytes: await result.bytes() };
842
- }
843
- return {
844
- structuredContent: result,
845
- content: [
846
- {
847
- type: "text",
848
- text: JSON.stringify(result),
849
- },
850
- ],
851
- };
852
- },
853
- );
854
- }
855
-
856
- return { server, tools };
857
- };
858
-
859
- const fetch = async (
860
- req: Request,
861
- env: TEnv & DefaultEnv<TSchema>,
862
- _ctx: ExecutionContext,
863
- ) => {
864
- const { server } = await createServer(env);
865
- const transport = new HttpServerTransport();
866
-
867
- await server.connect(transport);
868
-
869
- return await transport.handleMessage(req);
870
- };
871
-
872
- const callTool: CallTool = async ({ toolCallId, toolCallInput }) => {
873
- const currentState = State.getStore();
874
- if (!currentState) {
875
- throw new Error("Missing state, did you forget to call State.bind?");
876
- }
877
- const env = currentState?.env;
878
- const { tools } = await createServer(env);
879
- const tool = tools.find((t) => t.id === toolCallId);
880
- const execute = tool?.execute;
881
- if (!execute) {
882
- throw new Error(
883
- `Tool ${toolCallId} not found or does not have an execute function`,
884
- );
885
- }
886
-
887
- return execute({
888
- context: toolCallInput,
889
- runId: crypto.randomUUID(),
890
- runtimeContext: createRuntimeContext(),
891
- });
892
- };
893
-
894
- return {
895
- fetch,
896
- callTool,
897
- };
898
- };