@zackbart/connecta 0.15.1 → 0.16.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.
- package/AGENTS.md +40 -20
- package/CHANGELOG.md +653 -0
- package/README.md +25 -5
- package/bin/connecta.mjs +44 -3
- package/dist/access-tokens.d.ts +0 -1
- package/dist/access-tokens.js +0 -1
- package/dist/activity.d.ts +50 -2
- package/dist/activity.js +34 -1
- package/dist/apps-shell.d.ts +0 -1
- package/dist/apps-shell.js +0 -1
- package/dist/auth/bearer.d.ts +0 -1
- package/dist/auth/bearer.js +0 -1
- package/dist/auth/clerk.d.ts +0 -1
- package/dist/auth/clerk.js +45 -9
- package/dist/auth/downstream-oauth.d.ts +0 -1
- package/dist/auth/downstream-oauth.js +0 -1
- package/dist/call-admission.d.ts +0 -1
- package/dist/call-admission.js +0 -1
- package/dist/catalog-drift.d.ts +99 -0
- package/dist/catalog-drift.js +251 -0
- package/dist/catalog-fingerprint.d.ts +0 -1
- package/dist/catalog-fingerprint.js +0 -1
- package/dist/catalog-limits.d.ts +0 -1
- package/dist/catalog-limits.js +0 -1
- package/dist/catalog-service.d.ts +0 -1
- package/dist/catalog-service.js +41 -5
- package/dist/catalog.d.ts +11 -1
- package/dist/catalog.js +20 -1
- package/dist/concurrency.d.ts +0 -1
- package/dist/concurrency.js +0 -1
- package/dist/connector-scope.d.ts +0 -1
- package/dist/connector-scope.js +0 -1
- package/dist/connectors/api.d.ts +29 -21
- package/dist/connectors/api.js +45 -19
- package/dist/connectors/guarded-fetch.d.ts +106 -0
- package/dist/connectors/guarded-fetch.js +289 -0
- package/dist/connectors/remote-mcp.d.ts +0 -1
- package/dist/connectors/remote-mcp.js +0 -1
- package/dist/credentials.d.ts +0 -1
- package/dist/credentials.js +0 -1
- package/dist/errors.d.ts +30 -3
- package/dist/errors.js +1 -1
- package/dist/execute.d.ts +0 -1
- package/dist/execute.js +0 -1
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +28 -1
- package/dist/executor-result.d.ts +0 -1
- package/dist/executor-result.js +0 -1
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-child.js +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -1
- package/dist/executors/quickjs-protocol.js +0 -1
- package/dist/executors/quickjs-runtime.d.ts +0 -1
- package/dist/executors/quickjs-runtime.js +0 -1
- package/dist/executors/quickjs.d.ts +0 -1
- package/dist/executors/quickjs.js +2 -1
- package/dist/favicon.d.ts +0 -1
- package/dist/favicon.js +0 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -7
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +0 -1
- package/dist/json-schema.d.ts +0 -1
- package/dist/json-schema.js +0 -1
- package/dist/mcp-result.d.ts +0 -1
- package/dist/mcp-result.js +0 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/operator-ui/generated.d.ts +2 -3
- package/dist/operator-ui/generated.js +3 -4
- package/dist/operator-ui/model.d.ts +10 -1
- package/dist/operator-ui/model.js +0 -1
- package/dist/operator-ui/view.d.ts +167 -0
- package/dist/operator-ui/view.js +270 -0
- package/dist/providers/cloudflare.d.ts +0 -1
- package/dist/providers/cloudflare.js +261 -299
- package/dist/providers/linear.d.ts +26 -5
- package/dist/providers/linear.js +32 -61
- package/dist/providers/mixpanel.d.ts +35 -4
- package/dist/providers/mixpanel.js +53 -79
- package/dist/providers/notion.d.ts +0 -1
- package/dist/providers/notion.js +87 -42
- package/dist/providers/stripe.d.ts +13 -1
- package/dist/providers/stripe.js +31 -60
- package/dist/registry.d.ts +34 -2
- package/dist/registry.js +85 -5
- package/dist/retired-toolkits.d.ts +0 -1
- package/dist/retired-toolkits.js +0 -1
- package/dist/routes/access-tokens.d.ts +0 -1
- package/dist/routes/access-tokens.js +0 -1
- package/dist/routes/activity.d.ts +0 -1
- package/dist/routes/activity.js +0 -1
- package/dist/routes/credentials.d.ts +0 -1
- package/dist/routes/credentials.js +0 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +0 -1
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/ui.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +11 -1
- package/dist/skills.d.ts +0 -1
- package/dist/skills.js +0 -1
- package/dist/storage/file.d.ts +0 -1
- package/dist/storage/file.js +0 -1
- package/dist/storage/memory.d.ts +0 -1
- package/dist/storage/memory.js +0 -1
- package/dist/timeout.d.ts +0 -1
- package/dist/timeout.js +0 -1
- package/dist/tool-safety.d.ts +0 -1
- package/dist/tool-safety.js +0 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +13 -137
- package/dist/validate.d.ts +13 -15
- package/dist/validate.js +24 -11
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/documentation/architecture.md +186 -5
- package/documentation/auth.md +11 -0
- package/documentation/call-admission.md +142 -6
- package/documentation/cloudflare.md +92 -10
- package/documentation/code-first-exploration.md +2 -2
- package/documentation/code-mode.md +14 -14
- package/documentation/connector-guides.md +172 -5
- package/documentation/connectors.md +89 -0
- package/documentation/linear.md +20 -2
- package/documentation/mcp-ui-design.md +2 -2
- package/documentation/meta-tools.md +7 -1
- package/documentation/mixpanel.md +42 -9
- package/documentation/notion.md +23 -2
- package/documentation/operations.md +325 -5
- package/documentation/operator-ui.md +120 -5
- package/documentation/provider-audit.md +160 -0
- package/documentation/provider-conventions.md +667 -0
- package/documentation/request-admission.md +167 -5
- package/documentation/rich-output-design.md +1 -2
- package/documentation/stripe.md +15 -0
- package/documentation/upgrading.md +454 -0
- package/ethos.md +14 -9
- package/examples/worker/README.md +102 -12
- package/examples/worker/src/index.ts +46 -1
- package/examples/worker/wrangler.jsonc +14 -1
- package/package.json +10 -6
- package/templates/node/.dockerignore +10 -0
- package/templates/node/.env.example +40 -3
- package/templates/node/AGENTS.md +16 -1
- package/templates/node/Dockerfile +49 -0
- package/templates/node/README.md +117 -2
- package/templates/node/docker-compose.yml +39 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +182 -0
- package/templates/node/src/index.ts +80 -4
- package/assets/connecta-clay-hero.png +0 -0
- package/dist/access-tokens.d.ts.map +0 -1
- package/dist/access-tokens.js.map +0 -1
- package/dist/activity.d.ts.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/apps-shell.d.ts.map +0 -1
- package/dist/apps-shell.js.map +0 -1
- package/dist/auth/bearer.d.ts.map +0 -1
- package/dist/auth/bearer.js.map +0 -1
- package/dist/auth/clerk.d.ts.map +0 -1
- package/dist/auth/clerk.js.map +0 -1
- package/dist/auth/downstream-oauth.d.ts.map +0 -1
- package/dist/auth/downstream-oauth.js.map +0 -1
- package/dist/call-admission.d.ts.map +0 -1
- package/dist/call-admission.js.map +0 -1
- package/dist/catalog-fingerprint.d.ts.map +0 -1
- package/dist/catalog-fingerprint.js.map +0 -1
- package/dist/catalog-limits.d.ts.map +0 -1
- package/dist/catalog-limits.js.map +0 -1
- package/dist/catalog-service.d.ts.map +0 -1
- package/dist/catalog-service.js.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/catalog.js.map +0 -1
- package/dist/concurrency.d.ts.map +0 -1
- package/dist/concurrency.js.map +0 -1
- package/dist/connector-scope.d.ts.map +0 -1
- package/dist/connector-scope.js.map +0 -1
- package/dist/connectors/api.d.ts.map +0 -1
- package/dist/connectors/api.js.map +0 -1
- package/dist/connectors/remote-mcp.d.ts.map +0 -1
- package/dist/connectors/remote-mcp.js.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js.map +0 -1
- package/dist/executor-admission.d.ts.map +0 -1
- package/dist/executor-admission.js.map +0 -1
- package/dist/executor-result.d.ts.map +0 -1
- package/dist/executor-result.js.map +0 -1
- package/dist/executors/quickjs-child.d.ts.map +0 -1
- package/dist/executors/quickjs-child.js.map +0 -1
- package/dist/executors/quickjs-protocol.d.ts.map +0 -1
- package/dist/executors/quickjs-protocol.js.map +0 -1
- package/dist/executors/quickjs-runtime.d.ts.map +0 -1
- package/dist/executors/quickjs-runtime.js.map +0 -1
- package/dist/executors/quickjs.d.ts.map +0 -1
- package/dist/executors/quickjs.js.map +0 -1
- package/dist/favicon.d.ts.map +0 -1
- package/dist/favicon.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-schema.d.ts.map +0 -1
- package/dist/json-schema.js.map +0 -1
- package/dist/mcp-result.d.ts.map +0 -1
- package/dist/mcp-result.js.map +0 -1
- package/dist/meta-tools.d.ts.map +0 -1
- package/dist/meta-tools.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/operator-ui/generated.d.ts.map +0 -1
- package/dist/operator-ui/generated.js.map +0 -1
- package/dist/operator-ui/model.d.ts.map +0 -1
- package/dist/operator-ui/model.js.map +0 -1
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/linear.d.ts.map +0 -1
- package/dist/providers/linear.js.map +0 -1
- package/dist/providers/mixpanel.d.ts.map +0 -1
- package/dist/providers/mixpanel.js.map +0 -1
- package/dist/providers/notion.d.ts.map +0 -1
- package/dist/providers/notion.js.map +0 -1
- package/dist/providers/stripe.d.ts.map +0 -1
- package/dist/providers/stripe.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/retired-toolkits.d.ts.map +0 -1
- package/dist/retired-toolkits.js.map +0 -1
- package/dist/routes/access-tokens.d.ts.map +0 -1
- package/dist/routes/access-tokens.js.map +0 -1
- package/dist/routes/activity.d.ts.map +0 -1
- package/dist/routes/activity.js.map +0 -1
- package/dist/routes/credentials.d.ts.map +0 -1
- package/dist/routes/credentials.js.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/mcp.js.map +0 -1
- package/dist/routes/oauth.d.ts.map +0 -1
- package/dist/routes/oauth.js.map +0 -1
- package/dist/routes/shared.d.ts.map +0 -1
- package/dist/routes/shared.js.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/storage/file.d.ts.map +0 -1
- package/dist/storage/file.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js.map +0 -1
- package/dist/tool-safety.d.ts.map +0 -1
- package/dist/tool-safety.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/examples/node/README.md +0 -52
- package/examples/node/src/index.ts +0 -71
- package/src/access-tokens.ts +0 -289
- package/src/activity.ts +0 -234
- package/src/apps-shell.ts +0 -370
- package/src/auth/bearer.ts +0 -61
- package/src/auth/clerk.ts +0 -553
- package/src/auth/downstream-oauth.ts +0 -575
- package/src/call-admission.ts +0 -521
- package/src/catalog-fingerprint.ts +0 -52
- package/src/catalog-limits.ts +0 -11
- package/src/catalog-service.ts +0 -1153
- package/src/catalog.ts +0 -790
- package/src/concurrency.ts +0 -39
- package/src/connector-scope.ts +0 -66
- package/src/connectors/api.ts +0 -159
- package/src/connectors/remote-mcp.ts +0 -1090
- package/src/credentials.ts +0 -428
- package/src/errors.ts +0 -364
- package/src/execute.ts +0 -1406
- package/src/executor-admission.ts +0 -328
- package/src/executor-result.ts +0 -107
- package/src/executors/quickjs-child.ts +0 -172
- package/src/executors/quickjs-protocol.ts +0 -99
- package/src/executors/quickjs-runtime.ts +0 -470
- package/src/executors/quickjs.ts +0 -776
- package/src/favicon.ts +0 -53
- package/src/index.ts +0 -666
- package/src/invocation.ts +0 -655
- package/src/json-schema.ts +0 -11
- package/src/mcp-result.ts +0 -43
- package/src/meta-tools.ts +0 -1607
- package/src/node.ts +0 -206
- package/src/operator-ui/browser.css +0 -423
- package/src/operator-ui/browser.ts +0 -1281
- package/src/operator-ui/generated.ts +0 -4
- package/src/operator-ui/model.ts +0 -111
- package/src/providers/cloudflare.ts +0 -3999
- package/src/providers/linear.ts +0 -302
- package/src/providers/mixpanel.ts +0 -229
- package/src/providers/notion.ts +0 -1879
- package/src/providers/stripe.ts +0 -307
- package/src/registry.ts +0 -1085
- package/src/retired-toolkits.ts +0 -14
- package/src/routes/access-tokens.ts +0 -115
- package/src/routes/activity.ts +0 -215
- package/src/routes/credentials.ts +0 -254
- package/src/routes/mcp.ts +0 -429
- package/src/routes/oauth.ts +0 -322
- package/src/routes/shared.ts +0 -247
- package/src/routes/ui.ts +0 -152
- package/src/server.ts +0 -195
- package/src/skills.ts +0 -271
- package/src/storage/file.ts +0 -123
- package/src/storage/memory.ts +0 -44
- package/src/timeout.ts +0 -84
- package/src/tool-safety.ts +0 -15
- package/src/types.ts +0 -523
- package/src/ui.ts +0 -744
- package/src/validate.ts +0 -387
- package/src/version.ts +0 -7
package/src/routes/mcp.ts
DELETED
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createMcpHandler,
|
|
3
|
-
isLegacyRequest,
|
|
4
|
-
McpServer,
|
|
5
|
-
WebStandardStreamableHTTPServerTransport,
|
|
6
|
-
} from "@modelcontextprotocol/server";
|
|
7
|
-
import type { ActivityActor, ActivityRequestContext } from "../activity.js";
|
|
8
|
-
import {
|
|
9
|
-
MCP_APPS_EXTENSION,
|
|
10
|
-
PROGRAM_UI_MIME_TYPE,
|
|
11
|
-
PROGRAM_UI_RESOURCE_URI,
|
|
12
|
-
PROGRAM_UI_SHELL_HTML,
|
|
13
|
-
} from "../apps-shell.js";
|
|
14
|
-
import { registerExecuteTool } from "../execute.js";
|
|
15
|
-
import {
|
|
16
|
-
ExecutorAdmissionError,
|
|
17
|
-
type AdmissionLease,
|
|
18
|
-
} from "../executor-admission.js";
|
|
19
|
-
import { registerMetaTools } from "../meta-tools.js";
|
|
20
|
-
import type { RegistryView } from "../registry.js";
|
|
21
|
-
import { instructionsFor } from "../skills.js";
|
|
22
|
-
import type { Logger } from "../types.js";
|
|
23
|
-
import {
|
|
24
|
-
authorize,
|
|
25
|
-
type RouteContext,
|
|
26
|
-
type RuntimeExecutionContext,
|
|
27
|
-
type ServerOptions,
|
|
28
|
-
} from "./shared.js";
|
|
29
|
-
|
|
30
|
-
export const MCP_CORS_HEADERS = {
|
|
31
|
-
"Access-Control-Allow-Origin": "*",
|
|
32
|
-
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
33
|
-
"Access-Control-Allow-Headers":
|
|
34
|
-
"Content-Type, Authorization, mcp-protocol-version, mcp-session-id, mcp-method, mcp-name",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Browser-based MCP clients call /mcp cross-origin. Without CORS on every
|
|
38
|
-
// response — errors included — the browser hides the 401, the client cannot
|
|
39
|
-
// read WWW-Authenticate, and OAuth discovery silently never starts.
|
|
40
|
-
function withMcpCors(response: Response): Response {
|
|
41
|
-
const headers = new Headers(response.headers);
|
|
42
|
-
for (const [name, value] of Object.entries(MCP_CORS_HEADERS)) {
|
|
43
|
-
headers.set(name, value);
|
|
44
|
-
}
|
|
45
|
-
headers.set(
|
|
46
|
-
"Access-Control-Expose-Headers",
|
|
47
|
-
"WWW-Authenticate, Retry-After, mcp-session-id, mcp-protocol-version",
|
|
48
|
-
);
|
|
49
|
-
return new Response(response.body, {
|
|
50
|
-
status: response.status,
|
|
51
|
-
statusText: response.statusText,
|
|
52
|
-
headers,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function requestAdmissionFailure(error: ExecutorAdmissionError): Response {
|
|
57
|
-
const overloaded = error.code === "executor_overloaded";
|
|
58
|
-
const data = {
|
|
59
|
-
code: overloaded ? "server_overloaded" : "server_shutting_down",
|
|
60
|
-
retryable: overloaded,
|
|
61
|
-
...(overloaded && error.retryAfterMs !== undefined
|
|
62
|
-
? { retryAfterMs: error.retryAfterMs }
|
|
63
|
-
: {}),
|
|
64
|
-
};
|
|
65
|
-
const headers = new Headers({
|
|
66
|
-
"Content-Type": "application/json",
|
|
67
|
-
"Cache-Control": "no-store",
|
|
68
|
-
});
|
|
69
|
-
if (overloaded && error.retryAfterMs !== undefined) {
|
|
70
|
-
headers.set(
|
|
71
|
-
"Retry-After",
|
|
72
|
-
String(Math.max(1, Math.ceil(error.retryAfterMs / 1_000))),
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
return new Response(
|
|
76
|
-
JSON.stringify({
|
|
77
|
-
jsonrpc: "2.0",
|
|
78
|
-
id: null,
|
|
79
|
-
error: {
|
|
80
|
-
code: overloaded ? -32001 : -32002,
|
|
81
|
-
message: overloaded
|
|
82
|
-
? "Server capacity is exhausted. Retry later."
|
|
83
|
-
: "Server is shutting down.",
|
|
84
|
-
data,
|
|
85
|
-
},
|
|
86
|
-
}),
|
|
87
|
-
{ status: 503, headers },
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* A request owns its permit through the response body, not merely until the
|
|
93
|
-
* handler returns. This is what makes slow clients and response-stream failure
|
|
94
|
-
* part of the same bounded lifecycle as success, error, and cancellation.
|
|
95
|
-
*/
|
|
96
|
-
function releaseAdmissionWithResponse(
|
|
97
|
-
response: Response,
|
|
98
|
-
lease: AdmissionLease,
|
|
99
|
-
signal: AbortSignal,
|
|
100
|
-
): Response {
|
|
101
|
-
let released = false;
|
|
102
|
-
let onAbort = () => {};
|
|
103
|
-
const release = () => {
|
|
104
|
-
if (released) return;
|
|
105
|
-
released = true;
|
|
106
|
-
signal.removeEventListener("abort", onAbort);
|
|
107
|
-
lease.release();
|
|
108
|
-
};
|
|
109
|
-
if (!response.body) {
|
|
110
|
-
release();
|
|
111
|
-
return response;
|
|
112
|
-
}
|
|
113
|
-
const reader = response.body.getReader();
|
|
114
|
-
onAbort = () => {
|
|
115
|
-
// `cancel()` belongs to an operator/auth/SDK-provided stream and may
|
|
116
|
-
// reject. Consume both outcomes: `.finally(release)` would release the
|
|
117
|
-
// permit but preserve the rejection as an unhandled promise.
|
|
118
|
-
void reader.cancel(signal.reason).then(release, release);
|
|
119
|
-
};
|
|
120
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
121
|
-
if (signal.aborted) onAbort();
|
|
122
|
-
const body = new ReadableStream<Uint8Array>({
|
|
123
|
-
async pull(controller) {
|
|
124
|
-
try {
|
|
125
|
-
const next = await reader.read();
|
|
126
|
-
if (next.done) {
|
|
127
|
-
release();
|
|
128
|
-
controller.close();
|
|
129
|
-
} else {
|
|
130
|
-
controller.enqueue(next.value);
|
|
131
|
-
}
|
|
132
|
-
} catch (error) {
|
|
133
|
-
release();
|
|
134
|
-
controller.error(error);
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
async cancel(reason) {
|
|
138
|
-
try {
|
|
139
|
-
await reader.cancel(reason);
|
|
140
|
-
} finally {
|
|
141
|
-
release();
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
return new Response(body, {
|
|
146
|
-
status: response.status,
|
|
147
|
-
statusText: response.statusText,
|
|
148
|
-
headers: response.headers,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* 404 for any `?toolkit=` value, kept after the feature's retirement (#178).
|
|
154
|
-
*
|
|
155
|
-
* Toolkits are gone, but the URLs that named them are not: clients were handed
|
|
156
|
-
* MCP endpoint URLs with `?toolkit=` baked in, and nothing about upgrading the
|
|
157
|
-
* server rotates them. Silently serving those clients the full registry would
|
|
158
|
-
* turn a former scoping boundary into fail-open — so a request still sending
|
|
159
|
-
* the param gets the same explicit 404 an unknown toolkit got before, plus an
|
|
160
|
-
* operator-side log line, which (as ever — issue #47) is the channel that
|
|
161
|
-
* actually reaches a human.
|
|
162
|
-
*/
|
|
163
|
-
function toolkitRetired(logger: Logger): Response {
|
|
164
|
-
logger.warn(
|
|
165
|
-
"[connecta] rejected an /mcp connection carrying ?toolkit= with 404: " +
|
|
166
|
-
"toolkits were retired in issue #178 (see ethos.md) — one deployment " +
|
|
167
|
-
"serves one audience. The client sees a transport-level failure and " +
|
|
168
|
-
"never the reason, so remove the ?toolkit= value from its MCP endpoint " +
|
|
169
|
-
"URL, or point it at the deployment for its audience.",
|
|
170
|
-
);
|
|
171
|
-
return new Response(
|
|
172
|
-
JSON.stringify({
|
|
173
|
-
jsonrpc: "2.0",
|
|
174
|
-
id: null,
|
|
175
|
-
error: {
|
|
176
|
-
code: -32600,
|
|
177
|
-
message:
|
|
178
|
-
"This deployment does not accept ?toolkit=. Toolkits were retired " +
|
|
179
|
-
"in issue #178 — remove the ?toolkit= value from the MCP endpoint " +
|
|
180
|
-
"URL, or ask the operator for the deployment serving this audience.",
|
|
181
|
-
},
|
|
182
|
-
}),
|
|
183
|
-
{
|
|
184
|
-
status: 404,
|
|
185
|
-
headers: {
|
|
186
|
-
"Content-Type": "application/json",
|
|
187
|
-
"Cache-Control": "no-store",
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* U5: one static template, served by a handler that answers exactly one URI
|
|
195
|
-
* and fails on every other. Registering it is also what declares the
|
|
196
|
-
* `resources` capability — which is why `resources/list` has to answer, and
|
|
197
|
-
* why it answers with nothing. That is the Apps spec's permitted omission of
|
|
198
|
-
* UI-only resources from listing, taken exactly: the capability stays honest
|
|
199
|
-
* because the method answers, and nothing downstream is ever listed or
|
|
200
|
-
* aggregated. Widening this handler to proxy downstream templates is a
|
|
201
|
-
* decision (see the design record), not a diff.
|
|
202
|
-
*/
|
|
203
|
-
function registerProgramUiResource(server: McpServer): void {
|
|
204
|
-
server.registerResource(
|
|
205
|
-
"connecta-program-ui",
|
|
206
|
-
PROGRAM_UI_RESOURCE_URI,
|
|
207
|
-
{
|
|
208
|
-
title: "connecta program view",
|
|
209
|
-
description:
|
|
210
|
-
"The MCP Apps shell that renders HTML an execute_code program handed connecta.ui.",
|
|
211
|
-
mimeType: PROGRAM_UI_MIME_TYPE,
|
|
212
|
-
},
|
|
213
|
-
(uri) => ({
|
|
214
|
-
contents: [
|
|
215
|
-
{
|
|
216
|
-
uri: uri.href,
|
|
217
|
-
mimeType: PROGRAM_UI_MIME_TYPE,
|
|
218
|
-
text: PROGRAM_UI_SHELL_HTML,
|
|
219
|
-
},
|
|
220
|
-
],
|
|
221
|
-
}),
|
|
222
|
-
);
|
|
223
|
-
// The SDK's generated listing would advertise the template it just
|
|
224
|
-
// registered. Replace it rather than accept that: the URI reaches the host
|
|
225
|
-
// through tool metadata, so the listing has nothing to carry.
|
|
226
|
-
server.server.setRequestHandler("resources/list", () => ({ resources: [] }));
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async function serveMcp(
|
|
230
|
-
request: Request,
|
|
231
|
-
opts: ServerOptions,
|
|
232
|
-
baseUrl: string,
|
|
233
|
-
actor: ActivityActor,
|
|
234
|
-
registry: RegistryView,
|
|
235
|
-
runtimeContext?: RuntimeExecutionContext,
|
|
236
|
-
): Promise<Response> {
|
|
237
|
-
const createServer = (): McpServer => {
|
|
238
|
-
const server = new McpServer(opts.serverInfo, {
|
|
239
|
-
instructions: instructionsFor(),
|
|
240
|
-
// U11: the Apps extension must be explicitly negotiated, and a
|
|
241
|
-
// conforming client acts on an extension only when both sides declare
|
|
242
|
-
// it — without this line no host reads execute_code's _meta.ui, no host
|
|
243
|
-
// fetches the shell, and the whole design is inert. This is the one
|
|
244
|
-
// extension connecta advertises; the versioned extensions framework
|
|
245
|
-
// stays declined as a general surface (documentation/mcp-2026-07-28.md).
|
|
246
|
-
capabilities: {
|
|
247
|
-
extensions: {
|
|
248
|
-
[MCP_APPS_EXTENSION]: { mimeTypes: [PROGRAM_UI_MIME_TYPE] },
|
|
249
|
-
},
|
|
250
|
-
// Registering the shell below declares `resources` on its own, but it
|
|
251
|
-
// would default `listChanged` to true. Connecta serves one build-time
|
|
252
|
-
// template and never sends a list_changed notification, so say so:
|
|
253
|
-
// a client that subscribes on the strength of that flag would wait
|
|
254
|
-
// forever for an event this server has no way to produce.
|
|
255
|
-
resources: { listChanged: false },
|
|
256
|
-
},
|
|
257
|
-
cacheHints: {
|
|
258
|
-
"tools/list": {
|
|
259
|
-
ttlMs: 3_600_000,
|
|
260
|
-
cacheScope: "private",
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
});
|
|
264
|
-
registerProgramUiResource(server);
|
|
265
|
-
const activity: ActivityRequestContext | undefined = opts.activity
|
|
266
|
-
? {
|
|
267
|
-
sink: opts.activity,
|
|
268
|
-
actor,
|
|
269
|
-
requestId: crypto.randomUUID(),
|
|
270
|
-
serverInfo: opts.serverInfo,
|
|
271
|
-
...(opts.activityDeploymentId
|
|
272
|
-
? { deploymentId: opts.activityDeploymentId }
|
|
273
|
-
: {}),
|
|
274
|
-
...(runtimeContext?.waitUntil
|
|
275
|
-
? { defer: runtimeContext.waitUntil.bind(runtimeContext) }
|
|
276
|
-
: {}),
|
|
277
|
-
logger: opts.logger,
|
|
278
|
-
}
|
|
279
|
-
: undefined;
|
|
280
|
-
registerMetaTools(server, registry, {
|
|
281
|
-
baseUrl,
|
|
282
|
-
...(activity ? { activity } : {}),
|
|
283
|
-
...(opts.defaultToolTimeoutMs !== undefined
|
|
284
|
-
? { defaultToolTimeoutMs: opts.defaultToolTimeoutMs }
|
|
285
|
-
: {}),
|
|
286
|
-
...(opts.probeTimeoutMs !== undefined
|
|
287
|
-
? { probeTimeoutMs: opts.probeTimeoutMs }
|
|
288
|
-
: {}),
|
|
289
|
-
...(opts.discoveryConcurrency !== undefined
|
|
290
|
-
? { discoveryConcurrency: opts.discoveryConcurrency }
|
|
291
|
-
: {}),
|
|
292
|
-
requestSignal: request.signal,
|
|
293
|
-
});
|
|
294
|
-
registerExecuteTool(server, registry, {
|
|
295
|
-
baseUrl,
|
|
296
|
-
executor: opts.executor,
|
|
297
|
-
logger: opts.logger,
|
|
298
|
-
...(activity ? { activity } : {}),
|
|
299
|
-
requestSignal: request.signal,
|
|
300
|
-
...(opts.discoveryConcurrency !== undefined
|
|
301
|
-
? { discoveryConcurrency: opts.discoveryConcurrency }
|
|
302
|
-
: {}),
|
|
303
|
-
...(opts.probeTimeoutMs !== undefined
|
|
304
|
-
? { probeTimeoutMs: opts.probeTimeoutMs }
|
|
305
|
-
: {}),
|
|
306
|
-
...(opts.maxEmittedBytes !== undefined
|
|
307
|
-
? { maxEmittedBytes: opts.maxEmittedBytes }
|
|
308
|
-
: {}),
|
|
309
|
-
...(opts.maxEmittedBlocks !== undefined
|
|
310
|
-
? { maxEmittedBlocks: opts.maxEmittedBlocks }
|
|
311
|
-
: {}),
|
|
312
|
-
});
|
|
313
|
-
return server;
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
// The v2 entry's built-in legacy fallback streams 2025 results as SSE.
|
|
317
|
-
// Connecta's established wire contract is JSON, so retain the documented
|
|
318
|
-
// user-land legacy branch with the same transport setting while the modern
|
|
319
|
-
// branch uses the fetch-native handler.
|
|
320
|
-
if (!(await isLegacyRequest(request))) {
|
|
321
|
-
return createMcpHandler(createServer, {
|
|
322
|
-
legacy: "reject",
|
|
323
|
-
onerror: (error) => opts.logger.error("[connecta] MCP handler error", error),
|
|
324
|
-
}).fetch(request);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Fresh server + transport per legacy request, stateless and JSON-shaped.
|
|
328
|
-
const server = createServer();
|
|
329
|
-
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
330
|
-
enableJsonResponse: true,
|
|
331
|
-
});
|
|
332
|
-
await server.connect(transport);
|
|
333
|
-
return transport.handleRequest(request);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export function createMcpRoute(
|
|
337
|
-
opts: ServerOptions,
|
|
338
|
-
): (context: RouteContext) => Promise<Response | null> {
|
|
339
|
-
let lastAdmissionWarningAt = 0;
|
|
340
|
-
let suppressedAdmissionWarnings = 0;
|
|
341
|
-
const warnAdmissionRejected = (error: ExecutorAdmissionError): void => {
|
|
342
|
-
const now = Date.now();
|
|
343
|
-
if (now - lastAdmissionWarningAt < 1_000) {
|
|
344
|
-
suppressedAdmissionWarnings++;
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
opts.logger.warn("[connecta] MCP request admission rejected", {
|
|
348
|
-
retryAfterMs: error.retryAfterMs,
|
|
349
|
-
active: opts.requestAdmission.activeCount,
|
|
350
|
-
queued: opts.requestAdmission.queuedCount,
|
|
351
|
-
suppressedSinceLastWarning: suppressedAdmissionWarnings,
|
|
352
|
-
});
|
|
353
|
-
lastAdmissionWarningAt = now;
|
|
354
|
-
suppressedAdmissionWarnings = 0;
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
return async function routeMcp(
|
|
358
|
-
context: RouteContext,
|
|
359
|
-
): Promise<Response | null> {
|
|
360
|
-
const {
|
|
361
|
-
path,
|
|
362
|
-
request,
|
|
363
|
-
baseUrl,
|
|
364
|
-
runtimeContext,
|
|
365
|
-
} = context;
|
|
366
|
-
if (path !== "/mcp") return null;
|
|
367
|
-
let admission: AdmissionLease;
|
|
368
|
-
try {
|
|
369
|
-
admission = await opts.requestAdmission.acquire({
|
|
370
|
-
signal: request.signal,
|
|
371
|
-
});
|
|
372
|
-
if (admission.waitMs > 0) {
|
|
373
|
-
opts.logger.debug("[connecta] MCP request admitted after queue wait", {
|
|
374
|
-
waitMs: admission.waitMs,
|
|
375
|
-
active: opts.requestAdmission.activeCount,
|
|
376
|
-
queued: opts.requestAdmission.queuedCount,
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
} catch (error) {
|
|
380
|
-
if (
|
|
381
|
-
error instanceof ExecutorAdmissionError &&
|
|
382
|
-
error.code === "executor_cancelled"
|
|
383
|
-
) {
|
|
384
|
-
throw request.signal.reason ?? error;
|
|
385
|
-
}
|
|
386
|
-
if (error instanceof ExecutorAdmissionError) {
|
|
387
|
-
if (error.code === "executor_overloaded") {
|
|
388
|
-
warnAdmissionRejected(error);
|
|
389
|
-
}
|
|
390
|
-
return withMcpCors(requestAdmissionFailure(error));
|
|
391
|
-
}
|
|
392
|
-
throw error;
|
|
393
|
-
}
|
|
394
|
-
try {
|
|
395
|
-
const authz = await authorize(request, baseUrl, opts.auth);
|
|
396
|
-
if (!authz.ok) {
|
|
397
|
-
return releaseAdmissionWithResponse(
|
|
398
|
-
withMcpCors(authz.response),
|
|
399
|
-
admission,
|
|
400
|
-
request.signal,
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
if (new URL(request.url).searchParams.has("toolkit")) {
|
|
404
|
-
return releaseAdmissionWithResponse(
|
|
405
|
-
withMcpCors(toolkitRetired(opts.logger)),
|
|
406
|
-
admission,
|
|
407
|
-
request.signal,
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
return releaseAdmissionWithResponse(
|
|
411
|
-
withMcpCors(
|
|
412
|
-
await serveMcp(
|
|
413
|
-
request,
|
|
414
|
-
opts,
|
|
415
|
-
baseUrl,
|
|
416
|
-
authz.actor,
|
|
417
|
-
opts.registry,
|
|
418
|
-
runtimeContext,
|
|
419
|
-
),
|
|
420
|
-
),
|
|
421
|
-
admission,
|
|
422
|
-
request.signal,
|
|
423
|
-
);
|
|
424
|
-
} catch (error) {
|
|
425
|
-
admission.release();
|
|
426
|
-
throw error;
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
}
|