@decocms/runtime 0.28.1 → 1.0.0-alpha-candy.2
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/package.json +12 -78
- package/scripts/generate-json-schema.ts +24 -0
- package/src/asset-server/dev-server-proxy.ts +16 -0
- package/src/asset-server/index.ts +44 -0
- package/src/bindings/README.md +1 -1
- package/src/bindings/binder.ts +2 -5
- package/src/bindings/channels.ts +1 -1
- package/src/bindings/index.ts +0 -33
- package/src/bindings/language-model/utils.ts +0 -91
- package/src/bindings.ts +30 -108
- package/src/client.ts +1 -145
- package/src/index.ts +46 -175
- package/src/mcp.ts +8 -165
- package/src/proxy.ts +3 -62
- package/src/state.ts +1 -30
- package/src/tools.ts +336 -0
- package/src/wrangler.ts +5 -5
- package/tsconfig.json +8 -0
- package/dist/admin.d.ts +0 -5
- package/dist/admin.js +0 -21
- package/dist/admin.js.map +0 -1
- package/dist/bindings/deconfig/index.d.ts +0 -12
- package/dist/bindings/deconfig/index.js +0 -10
- package/dist/bindings/deconfig/index.js.map +0 -1
- package/dist/bindings/index.d.ts +0 -2315
- package/dist/bindings/index.js +0 -156
- package/dist/bindings/index.js.map +0 -1
- package/dist/chunk-3AWMDSOH.js +0 -96
- package/dist/chunk-3AWMDSOH.js.map +0 -1
- package/dist/chunk-4XSQKJLU.js +0 -105
- package/dist/chunk-4XSQKJLU.js.map +0 -1
- package/dist/chunk-5EYZ2LVM.js +0 -158
- package/dist/chunk-5EYZ2LVM.js.map +0 -1
- package/dist/chunk-7ITSLORK.js +0 -128
- package/dist/chunk-7ITSLORK.js.map +0 -1
- package/dist/chunk-I7BWSAN6.js +0 -49
- package/dist/chunk-I7BWSAN6.js.map +0 -1
- package/dist/chunk-L4OT2YDO.js +0 -27
- package/dist/chunk-L4OT2YDO.js.map +0 -1
- package/dist/chunk-SHQSNOFL.js +0 -769
- package/dist/chunk-SHQSNOFL.js.map +0 -1
- package/dist/chunk-UHR3BLMF.js +0 -92
- package/dist/chunk-UHR3BLMF.js.map +0 -1
- package/dist/chunk-UIJGM3NV.js +0 -518
- package/dist/chunk-UIJGM3NV.js.map +0 -1
- package/dist/chunk-ZPUT6RN6.js +0 -32
- package/dist/chunk-ZPUT6RN6.js.map +0 -1
- package/dist/client.d.ts +0 -28
- package/dist/client.js +0 -5
- package/dist/client.js.map +0 -1
- package/dist/d1-store.d.ts +0 -9
- package/dist/d1-store.js +0 -4
- package/dist/d1-store.js.map +0 -1
- package/dist/drizzle.d.ts +0 -49
- package/dist/drizzle.js +0 -121
- package/dist/drizzle.js.map +0 -1
- package/dist/index-LOfgE9a_.d.ts +0 -471
- package/dist/index-xKtm7A7B.d.ts +0 -530
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -637
- package/dist/index.js.map +0 -1
- package/dist/mastra.d.ts +0 -10
- package/dist/mastra.js +0 -6
- package/dist/mastra.js.map +0 -1
- package/dist/mcp-87iLaW9V.d.ts +0 -105
- package/dist/mcp-client.d.ts +0 -232
- package/dist/mcp-client.js +0 -4
- package/dist/mcp-client.js.map +0 -1
- package/dist/proxy.d.ts +0 -11
- package/dist/proxy.js +0 -5
- package/dist/proxy.js.map +0 -1
- package/dist/resources.d.ts +0 -362
- package/dist/resources.js +0 -4
- package/dist/resources.js.map +0 -1
- package/dist/views.d.ts +0 -72
- package/dist/views.js +0 -4
- package/dist/views.js.map +0 -1
- package/src/admin.ts +0 -16
- package/src/auth.ts +0 -233
- package/src/bindings/deconfig/helpers.ts +0 -107
- package/src/bindings/deconfig/index.ts +0 -1
- package/src/bindings/deconfig/resources.ts +0 -659
- package/src/bindings/deconfig/types.ts +0 -106
- package/src/bindings/language-model/ai-sdk.ts +0 -87
- package/src/bindings/language-model/index.ts +0 -4
- package/src/bindings/resources/bindings.ts +0 -99
- package/src/bindings/resources/helpers.ts +0 -95
- package/src/bindings/resources/schemas.ts +0 -265
- package/src/bindings/views.ts +0 -14
- package/src/cf-imports.ts +0 -1
- package/src/d1-store.ts +0 -34
- package/src/deprecated.ts +0 -59
- package/src/drizzle.ts +0 -201
- package/src/mastra.ts +0 -898
- package/src/resources.ts +0 -168
- package/src/views.ts +0 -26
- package/src/workflow.ts +0 -193
package/src/client.ts
CHANGED
|
@@ -1,35 +1,3 @@
|
|
|
1
|
-
import { toAsyncIterator } from "./bindings/deconfig/helpers.ts";
|
|
2
|
-
// Extract resource name from DECO_RESOURCE_${NAME}_READ pattern
|
|
3
|
-
type ExtractResourceName<K> = K extends `DECO_RESOURCE_${infer Name}_READ`
|
|
4
|
-
? Name
|
|
5
|
-
: never;
|
|
6
|
-
|
|
7
|
-
// Generate SUBSCRIBE method name from resource name
|
|
8
|
-
type SubscribeMethodName<Name extends string> =
|
|
9
|
-
`DECO_RESOURCE_${Name}_SUBSCRIBE`;
|
|
10
|
-
|
|
11
|
-
// Extract data type from READ method return type
|
|
12
|
-
type ExtractReadData<T> = T extends Promise<{ data: infer D }>
|
|
13
|
-
? D
|
|
14
|
-
: T extends { data: infer D }
|
|
15
|
-
? D
|
|
16
|
-
: never;
|
|
17
|
-
|
|
18
|
-
// Generate all SUBSCRIBE method names for a given type
|
|
19
|
-
type SubscribeMethods<T> = {
|
|
20
|
-
[K in keyof T as K extends `DECO_RESOURCE_${string}_READ`
|
|
21
|
-
? SubscribeMethodName<ExtractResourceName<K>>
|
|
22
|
-
: never]: K extends `DECO_RESOURCE_${string}_READ`
|
|
23
|
-
? // oxlint-disable-next-line no-explicit-any
|
|
24
|
-
T[K] extends (...args: any) => any
|
|
25
|
-
? (args: { id: string } | { uri: string }) => AsyncIterableIterator<{
|
|
26
|
-
uri: string;
|
|
27
|
-
data: ExtractReadData<Awaited<ReturnType<T[K]>>>;
|
|
28
|
-
}>
|
|
29
|
-
: never
|
|
30
|
-
: never;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
1
|
export type MCPClient<T> = {
|
|
34
2
|
// oxlint-disable-next-line no-explicit-any
|
|
35
3
|
[K in keyof T]: T[K] extends (...args: any) => any
|
|
@@ -38,7 +6,7 @@ export type MCPClient<T> = {
|
|
|
38
6
|
init?: CustomInit,
|
|
39
7
|
) => Promise<Awaited<ReturnType<T[K]>>>
|
|
40
8
|
: never;
|
|
41
|
-
}
|
|
9
|
+
};
|
|
42
10
|
|
|
43
11
|
export type CustomInit = RequestInit & {
|
|
44
12
|
handleResponse?: (response: Response) => Promise<unknown>;
|
|
@@ -53,123 +21,11 @@ export const DEFAULT_INIT: CustomInit = {
|
|
|
53
21
|
},
|
|
54
22
|
};
|
|
55
23
|
|
|
56
|
-
/**
|
|
57
|
-
* Helper function to call an MCP tool via fetch
|
|
58
|
-
*/
|
|
59
|
-
async function callMCPTool<T = unknown>(
|
|
60
|
-
methodName: string,
|
|
61
|
-
args: unknown,
|
|
62
|
-
init?: CustomInit,
|
|
63
|
-
): Promise<T> {
|
|
64
|
-
const mergedInit: CustomInit = {
|
|
65
|
-
...init,
|
|
66
|
-
headers: {
|
|
67
|
-
...DEFAULT_INIT.headers,
|
|
68
|
-
...init?.headers,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const response = await fetch(`/mcp/call-tool/${methodName}`, {
|
|
73
|
-
method: "POST",
|
|
74
|
-
body: JSON.stringify(args),
|
|
75
|
-
credentials: "include",
|
|
76
|
-
...mergedInit,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (!response.ok) {
|
|
80
|
-
throw new Error(`Failed to call ${methodName}: ${response.statusText}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return response.json() as Promise<T>;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Creates a subscribe method for a resource that returns an async iterator
|
|
88
|
-
* yielding {uri, data} objects as resources are updated.
|
|
89
|
-
*/
|
|
90
|
-
function createSubscribeMethod(
|
|
91
|
-
resourceName: string,
|
|
92
|
-
init?: CustomInit,
|
|
93
|
-
): (args: { id: string }) => AsyncIterableIterator<{
|
|
94
|
-
uri: string;
|
|
95
|
-
data: unknown;
|
|
96
|
-
}> {
|
|
97
|
-
return async function* (args: { id: string } | { uri: string }) {
|
|
98
|
-
// Step 1: Call DESCRIBE to get watch endpoint configuration and URI template
|
|
99
|
-
const describeMethodName = `DECO_RESOURCE_${resourceName}_DESCRIBE`;
|
|
100
|
-
const readMethodName = `DECO_RESOURCE_${resourceName}_READ`;
|
|
101
|
-
|
|
102
|
-
// Get describe information
|
|
103
|
-
const describeData = await callMCPTool<{
|
|
104
|
-
uriTemplate?: string;
|
|
105
|
-
features?: {
|
|
106
|
-
watch?: {
|
|
107
|
-
pathname?: string;
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
}>(describeMethodName, {}, init);
|
|
111
|
-
|
|
112
|
-
const watchPathname = describeData?.features?.watch?.pathname;
|
|
113
|
-
const uriTemplate = describeData?.uriTemplate;
|
|
114
|
-
|
|
115
|
-
if (!watchPathname) {
|
|
116
|
-
throw new Error(
|
|
117
|
-
`Resource ${resourceName} does not support watch functionality`,
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (!uriTemplate) {
|
|
122
|
-
throw new Error(`Resource ${resourceName} does not provide uriTemplate`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Step 2: Construct URI from template by replacing * with id
|
|
126
|
-
const resourceUri =
|
|
127
|
-
"uri" in args ? args.uri : uriTemplate.replace("*", args.id);
|
|
128
|
-
|
|
129
|
-
// Step 3: Construct watch URL and create EventSource
|
|
130
|
-
const watchUrl = new URL(watchPathname, globalThis.location.origin);
|
|
131
|
-
watchUrl.searchParams.set("uri", resourceUri);
|
|
132
|
-
|
|
133
|
-
const eventSource = new EventSource(watchUrl.href);
|
|
134
|
-
|
|
135
|
-
// Step 4: Use toAsyncIterator to consume SSE events and enrich with READ data
|
|
136
|
-
const eventStream = toAsyncIterator<{ uri: string }>(
|
|
137
|
-
eventSource,
|
|
138
|
-
"message",
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
// Iterate over SSE events and enrich with full data
|
|
142
|
-
for await (const event of eventStream) {
|
|
143
|
-
const uri = event.uri;
|
|
144
|
-
|
|
145
|
-
if (uri) {
|
|
146
|
-
// Call READ to get full resource data
|
|
147
|
-
const readData = await callMCPTool<{ data: unknown }>(
|
|
148
|
-
readMethodName,
|
|
149
|
-
{ uri },
|
|
150
|
-
init,
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
yield { uri, data: readData.data };
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
24
|
export const createClient = <T>(init?: CustomInit): MCPClient<T> => {
|
|
160
25
|
return new Proxy(
|
|
161
26
|
{},
|
|
162
27
|
{
|
|
163
28
|
get: (_, prop) => {
|
|
164
|
-
const propStr = String(prop);
|
|
165
|
-
|
|
166
|
-
// Check if this is a SUBSCRIBE method call
|
|
167
|
-
const subscribeMatch = propStr.match(/^DECO_RESOURCE_(.+)_SUBSCRIBE$/);
|
|
168
|
-
if (subscribeMatch) {
|
|
169
|
-
const resourceName = subscribeMatch[1];
|
|
170
|
-
return createSubscribeMethod(resourceName, init);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
29
|
// Regular method call
|
|
174
30
|
return async (args: unknown, innerInit?: CustomInit) => {
|
|
175
31
|
const mergedInit: CustomInit = {
|
package/src/index.ts
CHANGED
|
@@ -1,30 +1,14 @@
|
|
|
1
1
|
/* oxlint-disable no-explicit-any */
|
|
2
2
|
import type { ExecutionContext } from "@cloudflare/workers-types";
|
|
3
3
|
import { decodeJwt } from "jose";
|
|
4
|
-
import type { z } from "zod
|
|
5
|
-
import {
|
|
6
|
-
|
|
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";
|
|
18
|
-
import { DeprecatedEnv } from "./deprecated.ts";
|
|
4
|
+
import type { z } from "zod";
|
|
5
|
+
import { createContractBinding, createIntegrationBinding } from "./bindings.ts";
|
|
6
|
+
import { State } from "./state.ts";
|
|
19
7
|
import {
|
|
20
8
|
createMCPServer,
|
|
21
9
|
type CreateMCPServerOptions,
|
|
22
10
|
MCPServer,
|
|
23
|
-
} from "./
|
|
24
|
-
import { MCPClient, type QueryResult } from "./mcp.ts";
|
|
25
|
-
import { State } from "./state.ts";
|
|
26
|
-
import type { WorkflowDO } from "./workflow.ts";
|
|
27
|
-
import { Workflow } from "./workflow.ts";
|
|
11
|
+
} from "./tools.ts";
|
|
28
12
|
import type { Binding, ContractBinding, MCPBinding } from "./wrangler.ts";
|
|
29
13
|
export { proxyConnectionForId } from "./bindings.ts";
|
|
30
14
|
export {
|
|
@@ -32,30 +16,15 @@ export {
|
|
|
32
16
|
type CreateStubAPIOptions,
|
|
33
17
|
type ToolBinder,
|
|
34
18
|
} from "./mcp.ts";
|
|
35
|
-
export interface WorkspaceDB {
|
|
36
|
-
query: (params: {
|
|
37
|
-
sql: string;
|
|
38
|
-
params: string[];
|
|
39
|
-
}) => Promise<{ result: QueryResult[] }>;
|
|
40
|
-
}
|
|
41
19
|
|
|
42
|
-
export interface DefaultEnv<TSchema extends z.ZodTypeAny = any>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
DECO_APP_SLUG: string;
|
|
47
|
-
DECO_APP_ENTRYPOINT: string;
|
|
48
|
-
DECO_API_URL?: string;
|
|
49
|
-
DECO_WORKSPACE: string;
|
|
50
|
-
DECO_API_JWT_PUBLIC_KEY: string;
|
|
51
|
-
DECO_APP_DEPLOYMENT_ID: string;
|
|
52
|
-
DECO_BINDINGS: string;
|
|
53
|
-
DECO_API_TOKEN: string;
|
|
54
|
-
DECO_WORKFLOW_DO: DurableObjectNamespace<WorkflowDO>;
|
|
55
|
-
DECO_WORKSPACE_DB: WorkspaceDB & {
|
|
56
|
-
forContext: (ctx: RequestContext) => WorkspaceDB;
|
|
57
|
-
};
|
|
20
|
+
export interface DefaultEnv<TSchema extends z.ZodTypeAny = any> {
|
|
21
|
+
MESH_REQUEST_CONTEXT: RequestContext<TSchema>;
|
|
22
|
+
MESH_BINDINGS: string;
|
|
23
|
+
MESH_APP_DEPLOYMENT_ID: string;
|
|
58
24
|
IS_LOCAL: boolean;
|
|
25
|
+
MESH_URL?: string;
|
|
26
|
+
MESH_RUNTIME_TOKEN?: string;
|
|
27
|
+
MESH_APP_NAME?: string;
|
|
59
28
|
[key: string]: unknown;
|
|
60
29
|
}
|
|
61
30
|
|
|
@@ -63,7 +32,7 @@ export interface BindingsObject {
|
|
|
63
32
|
bindings?: Binding[];
|
|
64
33
|
}
|
|
65
34
|
|
|
66
|
-
export const
|
|
35
|
+
export const MCPBindings = {
|
|
67
36
|
parse: (bindings?: string): Binding[] => {
|
|
68
37
|
if (!bindings) return [];
|
|
69
38
|
try {
|
|
@@ -109,14 +78,13 @@ export interface User {
|
|
|
109
78
|
|
|
110
79
|
export interface RequestContext<TSchema extends z.ZodTypeAny = any> {
|
|
111
80
|
state: z.infer<TSchema>;
|
|
112
|
-
branch?: string;
|
|
113
81
|
token: string;
|
|
114
|
-
|
|
82
|
+
meshUrl: string;
|
|
115
83
|
ensureAuthenticated: (options?: {
|
|
116
84
|
workspaceHint?: string;
|
|
117
85
|
}) => User | undefined;
|
|
118
86
|
callerApp?: string;
|
|
119
|
-
|
|
87
|
+
connectionId?: string;
|
|
120
88
|
}
|
|
121
89
|
|
|
122
90
|
// 2. Map binding type to its creator function
|
|
@@ -136,26 +104,12 @@ const creatorByType: CreatorByType = {
|
|
|
136
104
|
const withDefaultBindings = ({
|
|
137
105
|
env,
|
|
138
106
|
server,
|
|
139
|
-
ctx,
|
|
140
107
|
url,
|
|
141
108
|
}: {
|
|
142
109
|
env: DefaultEnv;
|
|
143
110
|
server: MCPServer<any, any>;
|
|
144
|
-
ctx: RequestContext;
|
|
145
111
|
url?: string;
|
|
146
112
|
}) => {
|
|
147
|
-
const client = workspaceClient(ctx);
|
|
148
|
-
const createWorkspaceDB = (ctx: RequestContext): WorkspaceDB => {
|
|
149
|
-
const client = workspaceClient(ctx);
|
|
150
|
-
return {
|
|
151
|
-
query: ({ sql, params }) => {
|
|
152
|
-
return client.DATABASES_RUN_SQL({
|
|
153
|
-
sql,
|
|
154
|
-
params,
|
|
155
|
-
});
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
113
|
env["SELF"] = new Proxy(
|
|
160
114
|
{},
|
|
161
115
|
{
|
|
@@ -174,20 +128,6 @@ const withDefaultBindings = ({
|
|
|
174
128
|
},
|
|
175
129
|
);
|
|
176
130
|
|
|
177
|
-
const workspaceDbBinding = {
|
|
178
|
-
...createWorkspaceDB(ctx),
|
|
179
|
-
forContext: createWorkspaceDB,
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
env["DECO_API"] = MCPClient;
|
|
183
|
-
env["DECO_WORKSPACE_API"] = client;
|
|
184
|
-
env["DECO_WORKSPACE_DB"] = workspaceDbBinding;
|
|
185
|
-
|
|
186
|
-
// Backwards compatibility
|
|
187
|
-
env["DECO_CHAT_API"] = MCPClient;
|
|
188
|
-
env["DECO_CHAT_WORKSPACE_API"] = client;
|
|
189
|
-
env["DECO_CHAT_WORKSPACE_DB"] = workspaceDbBinding;
|
|
190
|
-
|
|
191
131
|
env["IS_LOCAL"] =
|
|
192
132
|
(url?.startsWith("http://localhost") ||
|
|
193
133
|
url?.startsWith("http://127.0.0.1")) ??
|
|
@@ -204,13 +144,9 @@ export class UnauthorizedError extends Error {
|
|
|
204
144
|
}
|
|
205
145
|
}
|
|
206
146
|
|
|
207
|
-
const
|
|
208
|
-
const AUTH_START_ENDPOINT = "/oauth/start";
|
|
209
|
-
const AUTH_LOGOUT_ENDPOINT = "/oauth/logout";
|
|
210
|
-
const AUTHENTICATED = (user?: unknown, workspace?: string) => () => {
|
|
147
|
+
const AUTHENTICATED = (user?: unknown) => () => {
|
|
211
148
|
return {
|
|
212
149
|
...((user as User) ?? {}),
|
|
213
|
-
workspace,
|
|
214
150
|
} as User;
|
|
215
151
|
};
|
|
216
152
|
|
|
@@ -218,68 +154,50 @@ export const withBindings = <TEnv>({
|
|
|
218
154
|
env: _env,
|
|
219
155
|
server,
|
|
220
156
|
tokenOrContext,
|
|
221
|
-
origin,
|
|
222
157
|
url,
|
|
223
|
-
branch,
|
|
224
158
|
}: {
|
|
225
159
|
env: TEnv;
|
|
226
160
|
server: MCPServer<TEnv, any>;
|
|
227
161
|
tokenOrContext?: string | RequestContext;
|
|
228
|
-
origin?: string | null;
|
|
229
162
|
url?: string;
|
|
230
|
-
branch?: string | null;
|
|
231
163
|
}): TEnv => {
|
|
232
|
-
branch ??= undefined;
|
|
233
164
|
const env = _env as DefaultEnv<any>;
|
|
234
165
|
|
|
235
|
-
const apiUrl = env.DECO_API_URL ?? "https://api.decocms.com";
|
|
236
166
|
let context;
|
|
237
167
|
if (typeof tokenOrContext === "string") {
|
|
238
168
|
const decoded = decodeJwt(tokenOrContext);
|
|
239
|
-
const
|
|
169
|
+
const metadata = decoded.metadata as {
|
|
170
|
+
state: Record<string, unknown>;
|
|
171
|
+
meshUrl: string;
|
|
172
|
+
connectionId: string;
|
|
173
|
+
};
|
|
240
174
|
|
|
241
175
|
context = {
|
|
242
|
-
state:
|
|
176
|
+
state: metadata.state,
|
|
243
177
|
token: tokenOrContext,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
ensureAuthenticated: AUTHENTICATED(decoded.user
|
|
247
|
-
branch,
|
|
178
|
+
meshUrl: metadata.meshUrl,
|
|
179
|
+
connectionId: metadata.connectionId,
|
|
180
|
+
ensureAuthenticated: AUTHENTICATED(decoded.user),
|
|
248
181
|
} as RequestContext<any>;
|
|
249
182
|
} else if (typeof tokenOrContext === "object") {
|
|
250
183
|
context = tokenOrContext;
|
|
251
184
|
const decoded = decodeJwt(tokenOrContext.token);
|
|
252
|
-
const
|
|
185
|
+
const metadata = decoded.metadata as {
|
|
186
|
+
state: Record<string, unknown>;
|
|
187
|
+
meshUrl: string;
|
|
188
|
+
connectionId: string;
|
|
189
|
+
};
|
|
253
190
|
const appName = decoded.appName as string | undefined;
|
|
254
191
|
context.callerApp = appName;
|
|
255
|
-
context.
|
|
256
|
-
context.ensureAuthenticated = AUTHENTICATED(decoded.user
|
|
192
|
+
context.connectionId ??= metadata.connectionId;
|
|
193
|
+
context.ensureAuthenticated = AUTHENTICATED(decoded.user);
|
|
257
194
|
} else {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
token: env.DECO_API_TOKEN,
|
|
261
|
-
workspace: env.DECO_WORKSPACE,
|
|
262
|
-
branch,
|
|
263
|
-
ensureAuthenticated: (options?: { workspaceHint?: string }) => {
|
|
264
|
-
const workspaceHint = options?.workspaceHint ?? env.DECO_WORKSPACE;
|
|
265
|
-
const authUri = new URL("/apps/oauth", apiUrl);
|
|
266
|
-
authUri.searchParams.set("client_id", env.DECO_APP_NAME);
|
|
267
|
-
authUri.searchParams.set(
|
|
268
|
-
"redirect_uri",
|
|
269
|
-
new URL(AUTH_CALLBACK_ENDPOINT, origin ?? env.DECO_APP_ENTRYPOINT)
|
|
270
|
-
.href,
|
|
271
|
-
);
|
|
272
|
-
workspaceHint &&
|
|
273
|
-
authUri.searchParams.set("workspace_hint", workspaceHint);
|
|
274
|
-
throw new UnauthorizedError("Unauthorized", authUri);
|
|
275
|
-
},
|
|
276
|
-
};
|
|
195
|
+
// should not reach here
|
|
196
|
+
throw new Error("Invalid token or context");
|
|
277
197
|
}
|
|
278
198
|
|
|
279
|
-
env.
|
|
280
|
-
|
|
281
|
-
env.DECO_CHAT_REQUEST_CONTEXT = context;
|
|
282
|
-
const bindings = WorkersMCPBindings.parse(env.DECO_BINDINGS);
|
|
199
|
+
env.MESH_REQUEST_CONTEXT = context;
|
|
200
|
+
const bindings = MCPBindings.parse(env.MESH_BINDINGS);
|
|
283
201
|
|
|
284
202
|
for (const binding of bindings) {
|
|
285
203
|
env[binding.name] = creatorByType[binding.type](binding as any, env);
|
|
@@ -288,7 +206,6 @@ export const withBindings = <TEnv>({
|
|
|
288
206
|
withDefaultBindings({
|
|
289
207
|
env,
|
|
290
208
|
server,
|
|
291
|
-
ctx: env.DECO_REQUEST_CONTEXT,
|
|
292
209
|
url,
|
|
293
210
|
});
|
|
294
211
|
|
|
@@ -297,9 +214,7 @@ export const withBindings = <TEnv>({
|
|
|
297
214
|
|
|
298
215
|
export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
|
|
299
216
|
userFns: UserDefaultExport<TEnv, TSchema>,
|
|
300
|
-
): ExportedHandler<TEnv & DefaultEnv<TSchema>>
|
|
301
|
-
Workflow: ReturnType<typeof Workflow>;
|
|
302
|
-
} => {
|
|
217
|
+
): ExportedHandler<TEnv & DefaultEnv<TSchema>> => {
|
|
303
218
|
const server = createMCPServer<TEnv, TSchema>(userFns);
|
|
304
219
|
const fetcher = async (
|
|
305
220
|
req: Request,
|
|
@@ -307,21 +222,6 @@ export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
|
|
|
307
222
|
ctx: ExecutionContext,
|
|
308
223
|
) => {
|
|
309
224
|
const url = new URL(req.url);
|
|
310
|
-
if (url.pathname === AUTH_CALLBACK_ENDPOINT) {
|
|
311
|
-
return handleAuthCallback(req, {
|
|
312
|
-
apiUrl: env.DECO_API_URL,
|
|
313
|
-
appName: env.DECO_APP_NAME,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
if (url.pathname === AUTH_START_ENDPOINT) {
|
|
317
|
-
env.DECO_REQUEST_CONTEXT.ensureAuthenticated();
|
|
318
|
-
const redirectTo = new URL("/", url);
|
|
319
|
-
const next = url.searchParams.get("next");
|
|
320
|
-
return Response.redirect(next ?? redirectTo, 302);
|
|
321
|
-
}
|
|
322
|
-
if (url.pathname === AUTH_LOGOUT_ENDPOINT) {
|
|
323
|
-
return handleLogout(req);
|
|
324
|
-
}
|
|
325
225
|
if (url.pathname === "/mcp") {
|
|
326
226
|
return server.fetch(req, env, ctx);
|
|
327
227
|
}
|
|
@@ -348,56 +248,27 @@ export const withRuntime = <TEnv, TSchema extends z.ZodTypeAny = never>(
|
|
|
348
248
|
});
|
|
349
249
|
}
|
|
350
250
|
|
|
351
|
-
if (url.pathname.startsWith(DeconfigResource.WatchPathNameBase)) {
|
|
352
|
-
return DeconfigResource.watchAPI(req, env);
|
|
353
|
-
}
|
|
354
251
|
return (
|
|
355
252
|
userFns.fetch?.(req, env, ctx) ||
|
|
356
253
|
new Response("Not found", { status: 404 })
|
|
357
254
|
);
|
|
358
255
|
};
|
|
359
256
|
return {
|
|
360
|
-
Workflow: Workflow(server, userFns.workflows),
|
|
361
257
|
fetch: async (
|
|
362
258
|
req: Request,
|
|
363
259
|
env: TEnv & DefaultEnv<TSchema>,
|
|
364
260
|
ctx: ExecutionContext,
|
|
365
261
|
) => {
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
tokenOrContext: await getReqToken(req, env),
|
|
377
|
-
origin:
|
|
378
|
-
referer ?? req.headers.get("origin") ?? new URL(req.url).origin,
|
|
379
|
-
url: req.url,
|
|
380
|
-
});
|
|
381
|
-
return await State.run(
|
|
382
|
-
{ req, env: bindings, ctx },
|
|
383
|
-
async () => await fetcher(req, bindings, ctx),
|
|
384
|
-
);
|
|
385
|
-
} catch (error) {
|
|
386
|
-
if (error instanceof UnauthorizedError) {
|
|
387
|
-
if (!isFetchRequest) {
|
|
388
|
-
const url = new URL(req.url);
|
|
389
|
-
error.redirectTo.searchParams.set(
|
|
390
|
-
"state",
|
|
391
|
-
StateParser.stringify({
|
|
392
|
-
next: url.searchParams.get("next") ?? referer ?? req.url,
|
|
393
|
-
}),
|
|
394
|
-
);
|
|
395
|
-
return Response.redirect(error.redirectTo, 302);
|
|
396
|
-
}
|
|
397
|
-
return new Response(null, { status: 401 });
|
|
398
|
-
}
|
|
399
|
-
throw error;
|
|
400
|
-
}
|
|
262
|
+
const bindings = withBindings({
|
|
263
|
+
env,
|
|
264
|
+
server,
|
|
265
|
+
tokenOrContext: req.headers.get("x-mesh-token") ?? undefined,
|
|
266
|
+
url: req.url,
|
|
267
|
+
});
|
|
268
|
+
return await State.run(
|
|
269
|
+
{ req, env: bindings, ctx },
|
|
270
|
+
async () => await fetcher(req, bindings, ctx),
|
|
271
|
+
);
|
|
401
272
|
},
|
|
402
273
|
};
|
|
403
274
|
};
|
package/src/mcp.ts
CHANGED
|
@@ -1,169 +1,12 @@
|
|
|
1
1
|
/* oxlint-disable no-explicit-any */
|
|
2
|
-
import { env } from "cloudflare:workers";
|
|
3
|
-
import { z } from "zod/v3";
|
|
4
|
-
import type { MCPConnection } from "./connection.ts";
|
|
5
|
-
import type { DefaultEnv } from "./index.ts";
|
|
6
|
-
import { createMCPClientProxy } from "./proxy.ts";
|
|
7
2
|
import type { ToolBinder } from "@decocms/bindings";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const Meta = z.object({
|
|
19
|
-
changed_db: z.boolean().optional(),
|
|
20
|
-
changes: z.number().optional(),
|
|
21
|
-
duration: z.number().optional(),
|
|
22
|
-
last_row_id: z.number().optional(),
|
|
23
|
-
rows_read: z.number().optional(),
|
|
24
|
-
rows_written: z.number().optional(),
|
|
25
|
-
served_by_primary: z.boolean().optional(),
|
|
26
|
-
served_by_region: z
|
|
27
|
-
.enum(["WNAM", "ENAM", "WEUR", "EEUR", "APAC", "OC"])
|
|
28
|
-
.optional(),
|
|
29
|
-
size_after: z.number().optional(),
|
|
30
|
-
timings: Timings.optional(),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const QueryResult = z.object({
|
|
34
|
-
meta: Meta.optional(),
|
|
35
|
-
results: z.array(z.unknown()).optional(),
|
|
36
|
-
success: z.boolean().optional(),
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export type QueryResult = z.infer<typeof QueryResult>;
|
|
40
|
-
|
|
41
|
-
const workspaceTools = [
|
|
42
|
-
{
|
|
43
|
-
name: "INTEGRATIONS_GET" as const,
|
|
44
|
-
inputSchema: z.object({
|
|
45
|
-
id: z.string(),
|
|
46
|
-
}),
|
|
47
|
-
outputSchema: z.object({
|
|
48
|
-
connection: z.object({}),
|
|
49
|
-
}),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: "DATABASES_RUN_SQL" as const,
|
|
53
|
-
inputSchema: z.object({
|
|
54
|
-
sql: z.string().describe("The SQL query to run"),
|
|
55
|
-
params: z
|
|
56
|
-
.array(z.string())
|
|
57
|
-
.describe("The parameters to pass to the SQL query"),
|
|
58
|
-
}),
|
|
59
|
-
outputSchema: z.object({
|
|
60
|
-
result: z.array(QueryResult),
|
|
61
|
-
}),
|
|
62
|
-
},
|
|
63
|
-
] satisfies ToolBinder<string, unknown, object>[];
|
|
64
|
-
|
|
65
|
-
// Default fetcher instance with API_SERVER_URL and API_HEADERS
|
|
66
|
-
const global = createMCPFetchStub<[]>({});
|
|
67
|
-
export const MCPClient = new Proxy(
|
|
68
|
-
{} as typeof global & {
|
|
69
|
-
forWorkspace: (
|
|
70
|
-
workspace: string,
|
|
71
|
-
token?: string,
|
|
72
|
-
) => MCPClientFetchStub<typeof workspaceTools>;
|
|
73
|
-
forConnection: <TDefinition extends readonly ToolBinder[]>(
|
|
74
|
-
connection: MCPConnectionProvider,
|
|
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) =>
|
|
85
|
-
createMCPFetchStub<[]>({
|
|
86
|
-
workspace,
|
|
87
|
-
token,
|
|
88
|
-
decoCmsApiUrl: (env as DefaultEnv).DECO_API_URL,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
if (name === "forConnection") {
|
|
92
|
-
return <TDefinition extends readonly ToolBinder[]>(
|
|
93
|
-
connection: MCPConnectionProvider,
|
|
94
|
-
) =>
|
|
95
|
-
createMCPFetchStub<TDefinition>({
|
|
96
|
-
connection,
|
|
97
|
-
decoCmsApiUrl: (env as DefaultEnv).DECO_API_URL,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return global[name as keyof typeof global];
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
);
|
|
3
|
+
export {
|
|
4
|
+
createMCPFetchStub,
|
|
5
|
+
isStreamableToolBinder,
|
|
6
|
+
MCPClient,
|
|
7
|
+
type CreateStubAPIOptions,
|
|
8
|
+
type MCPClientFetchStub,
|
|
9
|
+
type MCPClientStub,
|
|
10
|
+
} from "@decocms/bindings/client"; // Default fetcher instance with API_SERVER_URL and API_HEADERS
|
|
104
11
|
|
|
105
12
|
export type { ToolBinder };
|
|
106
|
-
|
|
107
|
-
export const isStreamableToolBinder = (
|
|
108
|
-
toolBinder: ToolBinder,
|
|
109
|
-
): toolBinder is ToolBinder<string, any, any, true> => {
|
|
110
|
-
return toolBinder.streamable === true;
|
|
111
|
-
};
|
|
112
|
-
export type MCPClientStub<TDefinition extends readonly ToolBinder[]> = {
|
|
113
|
-
[K in TDefinition[number] as K["name"]]: K extends ToolBinder<
|
|
114
|
-
string,
|
|
115
|
-
infer TInput,
|
|
116
|
-
infer TReturn
|
|
117
|
-
>
|
|
118
|
-
? (params: TInput, init?: RequestInit) => Promise<TReturn>
|
|
119
|
-
: never;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export type MCPClientFetchStub<TDefinition extends readonly ToolBinder[]> = {
|
|
123
|
-
[K in TDefinition[number] as K["name"]]: K["streamable"] extends true
|
|
124
|
-
? K extends ToolBinder<string, infer TInput, any, true>
|
|
125
|
-
? (params: TInput, init?: RequestInit) => Promise<Response>
|
|
126
|
-
: never
|
|
127
|
-
: K extends ToolBinder<string, infer TInput, infer TReturn, any>
|
|
128
|
-
? (params: TInput, init?: RequestInit) => Promise<Awaited<TReturn>>
|
|
129
|
-
: never;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export type MCPConnectionProvider = MCPConnection;
|
|
133
|
-
|
|
134
|
-
export interface MCPClientRaw {
|
|
135
|
-
callTool: (tool: string, args: unknown) => Promise<unknown>;
|
|
136
|
-
listTools: () => Promise<
|
|
137
|
-
{
|
|
138
|
-
name: string;
|
|
139
|
-
inputSchema: any;
|
|
140
|
-
outputSchema?: any;
|
|
141
|
-
description: string;
|
|
142
|
-
}[]
|
|
143
|
-
>;
|
|
144
|
-
}
|
|
145
|
-
export type JSONSchemaToZodConverter = (jsonSchema: any) => z.ZodTypeAny;
|
|
146
|
-
export interface CreateStubAPIOptions {
|
|
147
|
-
mcpPath?: string;
|
|
148
|
-
decoCmsApiUrl?: string;
|
|
149
|
-
workspace?: string;
|
|
150
|
-
token?: string;
|
|
151
|
-
connection?: MCPConnectionProvider;
|
|
152
|
-
streamable?: Record<string, boolean>;
|
|
153
|
-
debugId?: () => string;
|
|
154
|
-
getErrorByStatusCode?: (
|
|
155
|
-
statusCode: number,
|
|
156
|
-
message?: string,
|
|
157
|
-
traceId?: string,
|
|
158
|
-
errorObject?: unknown,
|
|
159
|
-
) => Error;
|
|
160
|
-
supportsToolName?: boolean;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function createMCPFetchStub<TDefinition extends readonly ToolBinder[]>(
|
|
164
|
-
options?: CreateStubAPIOptions,
|
|
165
|
-
): MCPClientFetchStub<TDefinition> {
|
|
166
|
-
return createMCPClientProxy<MCPClientFetchStub<TDefinition>>({
|
|
167
|
-
...(options ?? {}),
|
|
168
|
-
});
|
|
169
|
-
}
|