@decocms/runtime 1.0.0-alpha.2 → 1.0.0-alpha.20

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/src/index.ts CHANGED
@@ -2,55 +2,31 @@
2
2
  import type { ExecutionContext } from "@cloudflare/workers-types";
3
3
  import { decodeJwt } from "jose";
4
4
  import type { z } from "zod";
5
- import {
6
- getReqToken,
7
- handleAuthCallback,
8
- handleLogout,
9
- StateParser,
10
- } from "./auth.ts";
11
- import {
12
- createContractBinding,
13
- createIntegrationBinding,
14
- workspaceClient,
15
- } from "./bindings.ts";
16
- import { DeconfigResource } from "./bindings/deconfig/index.ts";
17
- import { DECO_MCP_CLIENT_HEADER } from "./client.ts";
5
+ import { createContractBinding, createIntegrationBinding } from "./bindings.ts";
6
+ import { State } from "./state.ts";
18
7
  import {
19
8
  createMCPServer,
20
9
  type CreateMCPServerOptions,
21
10
  MCPServer,
22
- } from "./mastra.ts";
23
- import { MCPClient, type QueryResult } from "./mcp.ts";
24
- import { State } from "./state.ts";
11
+ } from "./tools.ts";
25
12
  import type { Binding, ContractBinding, MCPBinding } from "./wrangler.ts";
13
+ import { type CORSOptions, handlePreflight, withCORS } from "./cors.ts";
26
14
  export { proxyConnectionForId } from "./bindings.ts";
27
15
  export {
28
16
  createMCPFetchStub,
29
17
  type CreateStubAPIOptions,
30
18
  type ToolBinder,
31
19
  } from "./mcp.ts";
32
- export interface WorkspaceDB {
33
- query: (params: {
34
- sql: string;
35
- params: string[];
36
- }) => Promise<{ result: QueryResult[] }>;
37
- }
20
+ export { type CORSOptions, type CORSOrigin } from "./cors.ts";
38
21
 
39
22
  export interface DefaultEnv<TSchema extends z.ZodTypeAny = any> {
40
- DECO_REQUEST_CONTEXT: RequestContext<TSchema>;
41
- DECO_APP_NAME: string;
42
- DECO_APP_SLUG: string;
43
- DECO_APP_ENTRYPOINT: string;
44
- DECO_API_URL?: string;
45
- DECO_WORKSPACE: string;
46
- DECO_API_JWT_PUBLIC_KEY: string;
47
- DECO_APP_DEPLOYMENT_ID: string;
48
- DECO_BINDINGS: string;
49
- DECO_API_TOKEN: string;
50
- DECO_WORKSPACE_DB: WorkspaceDB & {
51
- forContext: (ctx: RequestContext) => WorkspaceDB;
52
- };
23
+ MESH_REQUEST_CONTEXT: RequestContext<TSchema>;
24
+ MESH_BINDINGS: string;
25
+ MESH_APP_DEPLOYMENT_ID: string;
53
26
  IS_LOCAL: boolean;
27
+ MESH_URL?: string;
28
+ MESH_RUNTIME_TOKEN?: string;
29
+ MESH_APP_NAME?: string;
54
30
  [key: string]: unknown;
55
31
  }
56
32
 
@@ -58,7 +34,7 @@ export interface BindingsObject {
58
34
  bindings?: Binding[];
59
35
  }
60
36
 
61
- export const WorkersMCPBindings = {
37
+ export const MCPBindings = {
62
38
  parse: (bindings?: string): Binding[] => {
63
39
  if (!bindings) return [];
64
40
  try {
@@ -82,6 +58,11 @@ export interface UserDefaultExport<
82
58
  env: TEnv,
83
59
  ctx: ExecutionContext,
84
60
  ) => Promise<Response> | Response;
61
+ /**
62
+ * CORS configuration options.
63
+ * Set to `false` to disable CORS handling entirely.
64
+ */
65
+ cors?: CORSOptions | false;
85
66
  }
86
67
 
87
68
  // 1. Map binding type to its interface
@@ -104,14 +85,13 @@ export interface User {
104
85
 
105
86
  export interface RequestContext<TSchema extends z.ZodTypeAny = any> {
106
87
  state: z.infer<TSchema>;
107
- branch?: string;
108
88
  token: string;
109
- workspace: string;
89
+ meshUrl: string;
110
90
  ensureAuthenticated: (options?: {
111
91
  workspaceHint?: string;
112
92
  }) => User | undefined;
113
93
  callerApp?: string;
114
- integrationId?: string;
94
+ connectionId?: string;
115
95
  }
116
96
 
117
97
  // 2. Map binding type to its creator function
@@ -131,26 +111,12 @@ const creatorByType: CreatorByType = {
131
111
  const withDefaultBindings = ({
132
112
  env,
133
113
  server,
134
- ctx,
135
114
  url,
136
115
  }: {
137
116
  env: DefaultEnv;
138
117
  server: MCPServer<any, any>;
139
- ctx: RequestContext;
140
118
  url?: string;
141
119
  }) => {
142
- const client = workspaceClient(ctx, env.DECO_API_URL);
143
- const createWorkspaceDB = (ctx: RequestContext): WorkspaceDB => {
144
- const client = workspaceClient(ctx, env.DECO_API_URL);
145
- return {
146
- query: ({ sql, params }) => {
147
- return client.DATABASES_RUN_SQL({
148
- sql,
149
- params,
150
- });
151
- },
152
- };
153
- };
154
120
  env["SELF"] = new Proxy(
155
121
  {},
156
122
  {
@@ -169,15 +135,6 @@ const withDefaultBindings = ({
169
135
  },
170
136
  );
171
137
 
172
- const workspaceDbBinding = {
173
- ...createWorkspaceDB(ctx),
174
- forContext: createWorkspaceDB,
175
- };
176
-
177
- env["DECO_API"] = MCPClient;
178
- env["DECO_WORKSPACE_API"] = client;
179
- env["DECO_WORKSPACE_DB"] = workspaceDbBinding;
180
-
181
138
  env["IS_LOCAL"] =
182
139
  (url?.startsWith("http://localhost") ||
183
140
  url?.startsWith("http://127.0.0.1")) ??
@@ -194,13 +151,9 @@ export class UnauthorizedError extends Error {
194
151
  }
195
152
  }
196
153
 
197
- const AUTH_CALLBACK_ENDPOINT = "/oauth/callback";
198
- const AUTH_START_ENDPOINT = "/oauth/start";
199
- const AUTH_LOGOUT_ENDPOINT = "/oauth/logout";
200
- const AUTHENTICATED = (user?: unknown, workspace?: string) => () => {
154
+ const AUTHENTICATED = (user?: unknown) => () => {
201
155
  return {
202
156
  ...((user as User) ?? {}),
203
- workspace,
204
157
  } as User;
205
158
  };
206
159
 
@@ -208,66 +161,64 @@ export const withBindings = <TEnv>({
208
161
  env: _env,
209
162
  server,
210
163
  tokenOrContext,
211
- origin,
212
164
  url,
213
- branch,
165
+ bindings: inlineBindings,
214
166
  }: {
215
167
  env: TEnv;
216
168
  server: MCPServer<TEnv, any>;
217
169
  tokenOrContext?: string | RequestContext;
218
- origin?: string | null;
219
170
  url?: string;
220
- branch?: string | null;
171
+ bindings?: Binding[];
221
172
  }): TEnv => {
222
- branch ??= undefined;
223
173
  const env = _env as DefaultEnv<any>;
224
174
 
225
- const apiUrl = env.DECO_API_URL ?? "https://api.decocms.com";
226
175
  let context;
227
176
  if (typeof tokenOrContext === "string") {
228
177
  const decoded = decodeJwt(tokenOrContext);
229
- const workspace = decoded.aud as string;
178
+ // Support both new JWT format (fields directly on payload) and legacy format (nested in metadata)
179
+ const metadata =
180
+ (decoded.metadata as {
181
+ state?: Record<string, unknown>;
182
+ meshUrl?: string;
183
+ connectionId?: string;
184
+ }) ?? {};
230
185
 
231
186
  context = {
232
- state: decoded.state as Record<string, unknown>,
187
+ state: decoded.state ?? metadata.state,
233
188
  token: tokenOrContext,
234
- integrationId: decoded.integrationId as string,
235
- workspace,
236
- ensureAuthenticated: AUTHENTICATED(decoded.user, workspace),
237
- branch,
189
+ meshUrl: (decoded.meshUrl as string) ?? metadata.meshUrl,
190
+ connectionId: (decoded.connectionId as string) ?? metadata.connectionId,
191
+ ensureAuthenticated: AUTHENTICATED(decoded.user ?? decoded.sub),
238
192
  } as RequestContext<any>;
239
193
  } else if (typeof tokenOrContext === "object") {
240
194
  context = tokenOrContext;
241
195
  const decoded = decodeJwt(tokenOrContext.token);
242
- const workspace = decoded.aud as string;
196
+ // Support both new JWT format (fields directly on payload) and legacy format (nested in metadata)
197
+ const metadata =
198
+ (decoded.metadata as {
199
+ state?: Record<string, unknown>;
200
+ meshUrl?: string;
201
+ connectionId?: string;
202
+ }) ?? {};
243
203
  const appName = decoded.appName as string | undefined;
244
204
  context.callerApp = appName;
245
- context.integrationId ??= decoded.integrationId as string;
246
- context.ensureAuthenticated = AUTHENTICATED(decoded.user, workspace);
205
+ context.connectionId ??=
206
+ (decoded.connectionId as string) ?? metadata.connectionId;
207
+ context.ensureAuthenticated = AUTHENTICATED(decoded.user ?? decoded.sub);
247
208
  } else {
248
209
  context = {
249
- state: undefined,
250
- token: env.DECO_API_TOKEN,
251
- workspace: env.DECO_WORKSPACE,
252
- branch,
253
- ensureAuthenticated: (options?: { workspaceHint?: string }) => {
254
- const workspaceHint = options?.workspaceHint ?? env.DECO_WORKSPACE;
255
- const authUri = new URL("/apps/oauth", apiUrl);
256
- authUri.searchParams.set("client_id", env.DECO_APP_NAME);
257
- authUri.searchParams.set(
258
- "redirect_uri",
259
- new URL(AUTH_CALLBACK_ENDPOINT, origin ?? env.DECO_APP_ENTRYPOINT)
260
- .href,
261
- );
262
- workspaceHint &&
263
- authUri.searchParams.set("workspace_hint", workspaceHint);
264
- throw new UnauthorizedError("Unauthorized", authUri);
210
+ state: {},
211
+ token: undefined,
212
+ meshUrl: undefined,
213
+ connectionId: undefined,
214
+ ensureAuthenticated: () => {
215
+ throw new Error("Unauthorized");
265
216
  },
266
- };
217
+ } as unknown as RequestContext<any>;
267
218
  }
268
219
 
269
- env.DECO_REQUEST_CONTEXT = context;
270
- const bindings = WorkersMCPBindings.parse(env.DECO_BINDINGS);
220
+ env.MESH_REQUEST_CONTEXT = context;
221
+ const bindings = inlineBindings ?? MCPBindings.parse(env.MESH_BINDINGS);
271
222
 
272
223
  for (const binding of bindings) {
273
224
  env[binding.name] = creatorByType[binding.type](binding as any, env);
@@ -276,7 +227,6 @@ export const withBindings = <TEnv>({
276
227
  withDefaultBindings({
277
228
  env,
278
229
  server,
279
- ctx: env.DECO_REQUEST_CONTEXT,
280
230
  url,
281
231
  });
282
232
 
@@ -287,27 +237,14 @@ export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
287
237
  userFns: UserDefaultExport<TEnv, TSchema>,
288
238
  ): ExportedHandler<TEnv & DefaultEnv<TSchema>> => {
289
239
  const server = createMCPServer<TEnv, TSchema>(userFns);
240
+ const corsOptions = userFns.cors;
241
+
290
242
  const fetcher = async (
291
243
  req: Request,
292
244
  env: TEnv & DefaultEnv<TSchema>,
293
245
  ctx: ExecutionContext,
294
246
  ) => {
295
247
  const url = new URL(req.url);
296
- if (url.pathname === AUTH_CALLBACK_ENDPOINT) {
297
- return handleAuthCallback(req, {
298
- apiUrl: env.DECO_API_URL,
299
- appName: env.DECO_APP_NAME,
300
- });
301
- }
302
- if (url.pathname === AUTH_START_ENDPOINT) {
303
- env.DECO_REQUEST_CONTEXT.ensureAuthenticated();
304
- const redirectTo = new URL("/", url);
305
- const next = url.searchParams.get("next");
306
- return Response.redirect(next ?? redirectTo, 302);
307
- }
308
- if (url.pathname === AUTH_LOGOUT_ENDPOINT) {
309
- return handleLogout(req);
310
- }
311
248
  if (url.pathname === "/mcp") {
312
249
  return server.fetch(req, env, ctx);
313
250
  }
@@ -334,55 +271,44 @@ export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
334
271
  });
335
272
  }
336
273
 
337
- if (url.pathname.startsWith(DeconfigResource.WatchPathNameBase)) {
338
- return DeconfigResource.watchAPI(req, env);
339
- }
340
274
  return (
341
275
  userFns.fetch?.(req, env, ctx) ||
342
276
  new Response("Not found", { status: 404 })
343
277
  );
344
278
  };
279
+
345
280
  return {
346
281
  fetch: async (
347
282
  req: Request,
348
283
  env: TEnv & DefaultEnv<TSchema>,
349
284
  ctx: ExecutionContext,
350
285
  ) => {
351
- const referer = req.headers.get("referer");
352
- const isFetchRequest = req.headers.has(DECO_MCP_CLIENT_HEADER);
353
-
354
- try {
355
- const bindings = withBindings({
356
- env,
357
- server,
358
- branch:
359
- req.headers.get("x-deco-branch") ??
360
- new URL(req.url).searchParams.get("__b"),
361
- tokenOrContext: await getReqToken(req, env),
362
- origin:
363
- referer ?? req.headers.get("origin") ?? new URL(req.url).origin,
364
- url: req.url,
365
- });
366
- return await State.run(
367
- { req, env: bindings, ctx },
368
- async () => await fetcher(req, bindings, ctx),
369
- );
370
- } catch (error) {
371
- if (error instanceof UnauthorizedError) {
372
- if (!isFetchRequest) {
373
- const url = new URL(req.url);
374
- error.redirectTo.searchParams.set(
375
- "state",
376
- StateParser.stringify({
377
- next: url.searchParams.get("next") ?? referer ?? req.url,
378
- }),
379
- );
380
- return Response.redirect(error.redirectTo, 302);
381
- }
382
- return new Response(null, { status: 401 });
383
- }
384
- throw error;
286
+ // Handle CORS preflight (OPTIONS) requests
287
+ if (corsOptions !== false && req.method === "OPTIONS") {
288
+ const options = corsOptions ?? {};
289
+ return handlePreflight(req, options);
385
290
  }
291
+
292
+ const bindings = withBindings({
293
+ env,
294
+ server,
295
+ bindings: userFns.bindings,
296
+ tokenOrContext: req.headers.get("x-mesh-token") ?? undefined,
297
+ url: req.url,
298
+ });
299
+
300
+ const response = await State.run(
301
+ { req, env: bindings, ctx },
302
+ async () => await fetcher(req, bindings, ctx),
303
+ );
304
+
305
+ // Add CORS headers to response
306
+ if (corsOptions !== false) {
307
+ const options = corsOptions ?? {};
308
+ return withCORS(response, req, options);
309
+ }
310
+
311
+ return response;
386
312
  },
387
313
  };
388
314
  };
package/src/mcp.ts CHANGED
@@ -1,170 +1,11 @@
1
1
  /* oxlint-disable no-explicit-any */
2
- import { z } from "zod";
3
- import type { MCPConnection } from "./connection.ts";
4
- import { createMCPClientProxy } from "./proxy.ts";
5
2
  import type { ToolBinder } from "@decocms/bindings";
6
-
7
- export interface FetchOptions extends RequestInit {
8
- path?: string;
9
- segments?: string[];
10
- }
11
-
12
- const Timings = z.object({
13
- sql_duration_ms: z.number().optional(),
14
- });
15
-
16
- const Meta = z.object({
17
- changed_db: z.boolean().optional(),
18
- changes: z.number().optional(),
19
- duration: z.number().optional(),
20
- last_row_id: z.number().optional(),
21
- rows_read: z.number().optional(),
22
- rows_written: z.number().optional(),
23
- served_by_primary: z.boolean().optional(),
24
- served_by_region: z
25
- .enum(["WNAM", "ENAM", "WEUR", "EEUR", "APAC", "OC"])
26
- .optional(),
27
- size_after: z.number().optional(),
28
- timings: Timings.optional(),
29
- });
30
-
31
- const QueryResult = z.object({
32
- meta: Meta.optional(),
33
- results: z.array(z.unknown()).optional(),
34
- success: z.boolean().optional(),
35
- });
36
-
37
- export type QueryResult = z.infer<typeof QueryResult>;
38
-
39
- const workspaceTools = [
40
- {
41
- name: "INTEGRATIONS_GET" as const,
42
- inputSchema: z.object({
43
- id: z.string(),
44
- }),
45
- outputSchema: z.object({
46
- connection: z.object({}),
47
- }),
48
- },
49
- {
50
- name: "DATABASES_RUN_SQL" as const,
51
- inputSchema: z.object({
52
- sql: z.string().describe("The SQL query to run"),
53
- params: z
54
- .array(z.string())
55
- .describe("The parameters to pass to the SQL query"),
56
- }),
57
- outputSchema: z.object({
58
- result: z.array(QueryResult),
59
- }),
60
- },
61
- ] satisfies ToolBinder<string, unknown, object>[];
62
-
63
- // Default fetcher instance with API_SERVER_URL and API_HEADERS
64
- const global = createMCPFetchStub<[]>({});
65
- export const MCPClient = new Proxy(
66
- {} as typeof global & {
67
- forWorkspace: (
68
- workspace: string,
69
- token?: string,
70
- decoCmsApiUrl?: string,
71
- ) => MCPClientFetchStub<typeof workspaceTools>;
72
- forConnection: <TDefinition extends readonly ToolBinder[]>(
73
- connection: MCPConnectionProvider,
74
- decoCmsApiUrl?: string,
75
- ) => MCPClientFetchStub<TDefinition>;
76
- },
77
- {
78
- get(_, name) {
79
- if (name === "toJSON") {
80
- return null;
81
- }
82
-
83
- if (name === "forWorkspace") {
84
- return (workspace: string, token?: string, decoCmsApiUrl?: string) =>
85
- createMCPFetchStub<[]>({
86
- workspace,
87
- token,
88
- decoCmsApiUrl,
89
- });
90
- }
91
- if (name === "forConnection") {
92
- return <TDefinition extends readonly ToolBinder[]>(
93
- connection: MCPConnectionProvider,
94
- decoCmsApiUrl?: string,
95
- ) =>
96
- createMCPFetchStub<TDefinition>({
97
- connection,
98
- decoCmsApiUrl,
99
- });
100
- }
101
- return global[name as keyof typeof global];
102
- },
103
- },
104
- );
3
+ export {
4
+ createMCPFetchStub,
5
+ MCPClient,
6
+ type CreateStubAPIOptions,
7
+ type MCPClientFetchStub,
8
+ type MCPClientStub,
9
+ } from "@decocms/bindings/client"; // Default fetcher instance with API_SERVER_URL and API_HEADERS
105
10
 
106
11
  export type { ToolBinder };
107
-
108
- export const isStreamableToolBinder = (
109
- toolBinder: ToolBinder,
110
- ): toolBinder is ToolBinder<string, any, any, true> => {
111
- return toolBinder.streamable === true;
112
- };
113
- export type MCPClientStub<TDefinition extends readonly ToolBinder[]> = {
114
- [K in TDefinition[number] as K["name"]]: K extends ToolBinder<
115
- string,
116
- infer TInput,
117
- infer TReturn
118
- >
119
- ? (params: TInput, init?: RequestInit) => Promise<TReturn>
120
- : never;
121
- };
122
-
123
- export type MCPClientFetchStub<TDefinition extends readonly ToolBinder[]> = {
124
- [K in TDefinition[number] as K["name"]]: K["streamable"] extends true
125
- ? K extends ToolBinder<string, infer TInput, any, true>
126
- ? (params: TInput, init?: RequestInit) => Promise<Response>
127
- : never
128
- : K extends ToolBinder<string, infer TInput, infer TReturn, any>
129
- ? (params: TInput, init?: RequestInit) => Promise<Awaited<TReturn>>
130
- : never;
131
- };
132
-
133
- export type MCPConnectionProvider = MCPConnection;
134
-
135
- export interface MCPClientRaw {
136
- callTool: (tool: string, args: unknown) => Promise<unknown>;
137
- listTools: () => Promise<
138
- {
139
- name: string;
140
- inputSchema: any;
141
- outputSchema?: any;
142
- description: string;
143
- }[]
144
- >;
145
- }
146
- export type JSONSchemaToZodConverter = (jsonSchema: any) => z.ZodTypeAny;
147
- export interface CreateStubAPIOptions {
148
- mcpPath?: string;
149
- decoCmsApiUrl?: string;
150
- workspace?: string;
151
- token?: string;
152
- connection?: MCPConnectionProvider;
153
- streamable?: Record<string, boolean>;
154
- debugId?: () => string;
155
- getErrorByStatusCode?: (
156
- statusCode: number,
157
- message?: string,
158
- traceId?: string,
159
- errorObject?: unknown,
160
- ) => Error;
161
- supportsToolName?: boolean;
162
- }
163
-
164
- export function createMCPFetchStub<TDefinition extends readonly ToolBinder[]>(
165
- options?: CreateStubAPIOptions,
166
- ): MCPClientFetchStub<TDefinition> {
167
- return createMCPClientProxy<MCPClientFetchStub<TDefinition>>({
168
- ...(options ?? {}),
169
- });
170
- }
package/src/proxy.ts CHANGED
@@ -1,17 +1,8 @@
1
1
  /* oxlint-disable no-explicit-any */
2
- import type { ToolExecutionContext as _ToolExecutionContext } from "@mastra/core";
3
2
  import { convertJsonSchemaToZod } from "zod-from-json-schema";
4
3
  import { MCPConnection } from "./connection.ts";
5
4
  import { createServerClient } from "./mcp-client.ts";
6
5
  import type { CreateStubAPIOptions } from "./mcp.ts";
7
- import { WELL_KNOWN_API_HOSTNAMES } from "./well-known.ts";
8
-
9
- const getWorkspace = (workspace?: string) => {
10
- if (workspace && workspace.length > 0 && !workspace.includes("/")) {
11
- return `/shared/${workspace}`;
12
- }
13
- return workspace ?? "";
14
- };
15
6
 
16
7
  const safeParse = (content: string) => {
17
8
  try {
@@ -33,36 +24,13 @@ const toolsMap = new Map<
33
24
  >
34
25
  >();
35
26
 
36
- /**
37
- * Determines if a given URL supports tool names in the path.
38
- * Our APIs (api.decocms.com, api.deco.chat, localhost) support /tool/${toolName} routing.
39
- * Third-party APIs typically don't support this pattern.
40
- */
41
- function supportsToolNameInPath(url: string): boolean {
42
- try {
43
- // Our main APIs that support /tool/${toolName} routing
44
- return WELL_KNOWN_API_HOSTNAMES.includes(new URL(url).hostname);
45
- } catch {
46
- return false;
47
- }
48
- }
49
-
50
27
  /**
51
28
  * The base fetcher used to fetch the MCP from API.
52
29
  */
53
30
  export function createMCPClientProxy<T extends Record<string, unknown>>(
54
- options?: CreateStubAPIOptions,
31
+ options: CreateStubAPIOptions,
55
32
  ): T {
56
- const mcpPath = options?.mcpPath ?? "/mcp";
57
-
58
- const connection: MCPConnection = options?.connection || {
59
- type: "HTTP",
60
- token: options?.token,
61
- url: new URL(
62
- `${getWorkspace(options?.workspace)}${mcpPath}`,
63
- options?.decoCmsApiUrl ?? `https://api.decocms.com`,
64
- ).href,
65
- };
33
+ const connection: MCPConnection = options.connection;
66
34
 
67
35
  return new Proxy<T>({} as T, {
68
36
  get(_, name) {
@@ -81,28 +49,9 @@ export function createMCPClientProxy<T extends Record<string, unknown>>(
81
49
  // Create a connection with the tool name in the URL path for better logging
82
50
  // Only modify connections that have a URL property (HTTP, SSE, Websocket)
83
51
  // Use automatic detection based on URL, with optional override
84
- let toolConnection = connection;
85
- const shouldAddToolName =
86
- options?.supportsToolName ??
87
- ("url" in connection &&
88
- typeof connection.url === "string" &&
89
- supportsToolNameInPath(connection.url));
90
-
91
- if (
92
- shouldAddToolName &&
93
- "url" in connection &&
94
- typeof connection.url === "string"
95
- ) {
96
- toolConnection = {
97
- ...connection,
98
- url: connection.url.endsWith("/")
99
- ? `${connection.url}tool/${String(name)}`
100
- : `${connection.url}/tool/${String(name)}`,
101
- };
102
- }
103
52
 
104
53
  const { client, callStreamableTool } = await createServerClient(
105
- { connection: toolConnection },
54
+ { connection },
106
55
  undefined,
107
56
  extraHeaders,
108
57
  );
package/src/state.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- import { z } from "zod";
3
- import type { AppContext } from "./mastra.ts";
4
- import { createTool } from "./mastra.ts";
2
+ import { DefaultEnv } from "./index.ts";
3
+ import type { AppContext } from "./tools.ts";
5
4
 
6
5
  const asyncLocalStorage = new AsyncLocalStorage<AppContext | undefined>();
7
6
 
@@ -9,36 +8,9 @@ export const State = {
9
8
  getStore: () => {
10
9
  return asyncLocalStorage.getStore();
11
10
  },
12
- run: <TEnv, R, TArgs extends unknown[]>(
11
+ run: <TEnv extends DefaultEnv, R, TArgs extends unknown[]>(
13
12
  ctx: AppContext<TEnv>,
14
13
  f: (...args: TArgs) => R,
15
14
  ...args: TArgs
16
15
  ): R => asyncLocalStorage.run(ctx, f, ...args),
17
16
  };
18
-
19
- export interface ValidationPayload {
20
- state: unknown;
21
- }
22
-
23
- export const createStateValidationTool = (stateSchema?: z.ZodTypeAny) => {
24
- return createTool({
25
- id: "DECO_CHAT_STATE_VALIDATION",
26
- description: "Validate the state of the OAuth flow",
27
- inputSchema: z.object({
28
- state: z.unknown(),
29
- }),
30
- outputSchema: z.object({
31
- valid: z.boolean(),
32
- }),
33
- execute: (ctx) => {
34
- if (!stateSchema) {
35
- return Promise.resolve({ valid: true });
36
- }
37
- const parsed = stateSchema.safeParse(ctx.context.state);
38
- return Promise.resolve({
39
- valid: parsed.success,
40
- reason: parsed.error?.message,
41
- });
42
- },
43
- });
44
- };