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

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
@@ -1,56 +1,31 @@
1
1
  /* oxlint-disable no-explicit-any */
2
- import type { ExecutionContext } from "@cloudflare/workers-types";
3
2
  import { decodeJwt } from "jose";
4
3
  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";
4
+ import { createContractBinding, createIntegrationBinding } from "./bindings.ts";
5
+ import { type CORSOptions, handlePreflight, withCORS } from "./cors.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";
26
13
  export { proxyConnectionForId } from "./bindings.ts";
14
+ export { type CORSOptions, type CORSOrigin } from "./cors.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
- }
38
20
 
39
21
  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
- };
22
+ MESH_REQUEST_CONTEXT: RequestContext<TSchema>;
23
+ MESH_BINDINGS: string;
24
+ MESH_APP_DEPLOYMENT_ID: string;
53
25
  IS_LOCAL: boolean;
26
+ MESH_URL?: string;
27
+ MESH_RUNTIME_TOKEN?: string;
28
+ MESH_APP_NAME?: string;
54
29
  [key: string]: unknown;
55
30
  }
56
31
 
@@ -58,7 +33,7 @@ export interface BindingsObject {
58
33
  bindings?: Binding[];
59
34
  }
60
35
 
61
- export const WorkersMCPBindings = {
36
+ export const MCPBindings = {
62
37
  parse: (bindings?: string): Binding[] => {
63
38
  if (!bindings) return [];
64
39
  try {
@@ -77,11 +52,12 @@ export interface UserDefaultExport<
77
52
  TSchema extends z.ZodTypeAny = never,
78
53
  TEnv = TUserEnv & DefaultEnv<TSchema>,
79
54
  > extends CreateMCPServerOptions<TEnv, TSchema> {
80
- fetch?: (
81
- req: Request,
82
- env: TEnv,
83
- ctx: ExecutionContext,
84
- ) => Promise<Response> | Response;
55
+ fetch?: (req: Request, env: TEnv, ctx: any) => Promise<Response> | Response;
56
+ /**
57
+ * CORS configuration options.
58
+ * Set to `false` to disable CORS handling entirely.
59
+ */
60
+ cors?: CORSOptions | false;
85
61
  }
86
62
 
87
63
  // 1. Map binding type to its interface
@@ -104,14 +80,13 @@ export interface User {
104
80
 
105
81
  export interface RequestContext<TSchema extends z.ZodTypeAny = any> {
106
82
  state: z.infer<TSchema>;
107
- branch?: string;
108
83
  token: string;
109
- workspace: string;
84
+ meshUrl: string;
110
85
  ensureAuthenticated: (options?: {
111
86
  workspaceHint?: string;
112
87
  }) => User | undefined;
113
88
  callerApp?: string;
114
- integrationId?: string;
89
+ connectionId?: string;
115
90
  }
116
91
 
117
92
  // 2. Map binding type to its creator function
@@ -131,26 +106,12 @@ const creatorByType: CreatorByType = {
131
106
  const withDefaultBindings = ({
132
107
  env,
133
108
  server,
134
- ctx,
135
109
  url,
136
110
  }: {
137
111
  env: DefaultEnv;
138
112
  server: MCPServer<any, any>;
139
- ctx: RequestContext;
140
113
  url?: string;
141
114
  }) => {
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
115
  env["SELF"] = new Proxy(
155
116
  {},
156
117
  {
@@ -169,15 +130,6 @@ const withDefaultBindings = ({
169
130
  },
170
131
  );
171
132
 
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
133
  env["IS_LOCAL"] =
182
134
  (url?.startsWith("http://localhost") ||
183
135
  url?.startsWith("http://127.0.0.1")) ??
@@ -194,13 +146,9 @@ export class UnauthorizedError extends Error {
194
146
  }
195
147
  }
196
148
 
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) => () => {
149
+ const AUTHENTICATED = (user?: unknown) => () => {
201
150
  return {
202
151
  ...((user as User) ?? {}),
203
- workspace,
204
152
  } as User;
205
153
  };
206
154
 
@@ -208,66 +156,64 @@ export const withBindings = <TEnv>({
208
156
  env: _env,
209
157
  server,
210
158
  tokenOrContext,
211
- origin,
212
159
  url,
213
- branch,
160
+ bindings: inlineBindings,
214
161
  }: {
215
162
  env: TEnv;
216
163
  server: MCPServer<TEnv, any>;
217
164
  tokenOrContext?: string | RequestContext;
218
- origin?: string | null;
219
165
  url?: string;
220
- branch?: string | null;
166
+ bindings?: Binding[];
221
167
  }): TEnv => {
222
- branch ??= undefined;
223
168
  const env = _env as DefaultEnv<any>;
224
169
 
225
- const apiUrl = env.DECO_API_URL ?? "https://api.decocms.com";
226
170
  let context;
227
171
  if (typeof tokenOrContext === "string") {
228
172
  const decoded = decodeJwt(tokenOrContext);
229
- const workspace = decoded.aud as string;
173
+ // Support both new JWT format (fields directly on payload) and legacy format (nested in metadata)
174
+ const metadata =
175
+ (decoded.metadata as {
176
+ state?: Record<string, unknown>;
177
+ meshUrl?: string;
178
+ connectionId?: string;
179
+ }) ?? {};
230
180
 
231
181
  context = {
232
- state: decoded.state as Record<string, unknown>,
182
+ state: decoded.state ?? metadata.state,
233
183
  token: tokenOrContext,
234
- integrationId: decoded.integrationId as string,
235
- workspace,
236
- ensureAuthenticated: AUTHENTICATED(decoded.user, workspace),
237
- branch,
184
+ meshUrl: (decoded.meshUrl as string) ?? metadata.meshUrl,
185
+ connectionId: (decoded.connectionId as string) ?? metadata.connectionId,
186
+ ensureAuthenticated: AUTHENTICATED(decoded.user ?? decoded.sub),
238
187
  } as RequestContext<any>;
239
188
  } else if (typeof tokenOrContext === "object") {
240
189
  context = tokenOrContext;
241
190
  const decoded = decodeJwt(tokenOrContext.token);
242
- const workspace = decoded.aud as string;
191
+ // Support both new JWT format (fields directly on payload) and legacy format (nested in metadata)
192
+ const metadata =
193
+ (decoded.metadata as {
194
+ state?: Record<string, unknown>;
195
+ meshUrl?: string;
196
+ connectionId?: string;
197
+ }) ?? {};
243
198
  const appName = decoded.appName as string | undefined;
244
199
  context.callerApp = appName;
245
- context.integrationId ??= decoded.integrationId as string;
246
- context.ensureAuthenticated = AUTHENTICATED(decoded.user, workspace);
200
+ context.connectionId ??=
201
+ (decoded.connectionId as string) ?? metadata.connectionId;
202
+ context.ensureAuthenticated = AUTHENTICATED(decoded.user ?? decoded.sub);
247
203
  } else {
248
204
  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);
205
+ state: {},
206
+ token: undefined,
207
+ meshUrl: undefined,
208
+ connectionId: undefined,
209
+ ensureAuthenticated: () => {
210
+ throw new Error("Unauthorized");
265
211
  },
266
- };
212
+ } as unknown as RequestContext<any>;
267
213
  }
268
214
 
269
- env.DECO_REQUEST_CONTEXT = context;
270
- const bindings = WorkersMCPBindings.parse(env.DECO_BINDINGS);
215
+ env.MESH_REQUEST_CONTEXT = context;
216
+ const bindings = inlineBindings ?? MCPBindings.parse(env.MESH_BINDINGS);
271
217
 
272
218
  for (const binding of bindings) {
273
219
  env[binding.name] = creatorByType[binding.type](binding as any, env);
@@ -276,7 +222,6 @@ export const withBindings = <TEnv>({
276
222
  withDefaultBindings({
277
223
  env,
278
224
  server,
279
- ctx: env.DECO_REQUEST_CONTEXT,
280
225
  url,
281
226
  });
282
227
 
@@ -285,29 +230,16 @@ export const withBindings = <TEnv>({
285
230
 
286
231
  export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
287
232
  userFns: UserDefaultExport<TEnv, TSchema>,
288
- ): ExportedHandler<TEnv & DefaultEnv<TSchema>> => {
233
+ ) => {
289
234
  const server = createMCPServer<TEnv, TSchema>(userFns);
235
+ const corsOptions = userFns.cors;
236
+
290
237
  const fetcher = async (
291
238
  req: Request,
292
239
  env: TEnv & DefaultEnv<TSchema>,
293
- ctx: ExecutionContext,
240
+ ctx: any,
294
241
  ) => {
295
242
  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
243
  if (url.pathname === "/mcp") {
312
244
  return server.fetch(req, env, ctx);
313
245
  }
@@ -334,55 +266,40 @@ export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
334
266
  });
335
267
  }
336
268
 
337
- if (url.pathname.startsWith(DeconfigResource.WatchPathNameBase)) {
338
- return DeconfigResource.watchAPI(req, env);
339
- }
340
269
  return (
341
270
  userFns.fetch?.(req, env, ctx) ||
342
271
  new Response("Not found", { status: 404 })
343
272
  );
344
273
  };
274
+
345
275
  return {
346
- fetch: async (
347
- req: Request,
348
- env: TEnv & DefaultEnv<TSchema>,
349
- ctx: ExecutionContext,
350
- ) => {
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;
276
+ fetch: async (req: Request, env: TEnv & DefaultEnv<TSchema>, ctx: any) => {
277
+ // Handle CORS preflight (OPTIONS) requests
278
+ if (corsOptions !== false && req.method === "OPTIONS") {
279
+ const options = corsOptions ?? {};
280
+ return handlePreflight(req, options);
385
281
  }
282
+
283
+ const bindings = withBindings({
284
+ env,
285
+ server,
286
+ bindings: userFns.bindings,
287
+ tokenOrContext: req.headers.get("x-mesh-token") ?? undefined,
288
+ url: req.url,
289
+ });
290
+
291
+ const response = await State.run(
292
+ { req, env: bindings, ctx },
293
+ async () => await fetcher(req, bindings, ctx),
294
+ );
295
+
296
+ // Add CORS headers to response
297
+ if (corsOptions !== false) {
298
+ const options = corsOptions ?? {};
299
+ return withCORS(response, req, options);
300
+ }
301
+
302
+ return response;
386
303
  },
387
304
  };
388
305
  };
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
- };