@zackbart/connecta 0.3.0 → 0.4.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/CHANGELOG.md +143 -0
- package/README.md +34 -14
- package/SECURITY.md +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +62 -0
- package/dist/catalog.js.map +1 -1
- package/dist/connectors/api.d.ts +10 -0
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/api.js +16 -39
- package/dist/connectors/api.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +14 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +29 -0
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/credentials.d.ts +2 -1
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +4 -2
- package/dist/credentials.js.map +1 -1
- package/dist/errors.d.ts +20 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +39 -1
- package/dist/errors.js.map +1 -1
- package/dist/executors/quickjs.d.ts.map +1 -1
- package/dist/executors/quickjs.js +32 -4
- package/dist/executors/quickjs.js.map +1 -1
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -1
- package/dist/index.js.map +1 -1
- package/dist/json-schema.d.ts +3 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +6 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/meta-tools.d.ts +34 -2
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +104 -15
- package/dist/meta-tools.js.map +1 -1
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +34 -6
- package/dist/server.js.map +1 -1
- package/dist/storage/file.d.ts.map +1 -1
- package/dist/storage/file.js +19 -3
- package/dist/storage/file.js.map +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +13 -9
- package/dist/ui.js.map +1 -1
- package/dist/validate.d.ts +71 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +96 -0
- package/dist/validate.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
- package/src/catalog.ts +61 -0
- package/src/connectors/api.ts +25 -50
- package/src/connectors/remote-mcp.ts +52 -0
- package/src/credentials.ts +6 -3
- package/src/errors.ts +45 -2
- package/src/executors/quickjs.ts +32 -4
- package/src/index.ts +94 -1
- package/src/json-schema.ts +11 -0
- package/src/meta-tools.ts +156 -28
- package/src/server.ts +39 -6
- package/src/storage/file.ts +18 -2
- package/src/ui.ts +13 -8
- package/src/validate.ts +154 -0
- package/src/version.ts +1 -1
package/dist/ui.js
CHANGED
|
@@ -9,15 +9,16 @@ export const CONNECTA_FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" vie
|
|
|
9
9
|
export function resolveBranding(branding) {
|
|
10
10
|
const productName = branding?.productName?.trim() || "Connecta";
|
|
11
11
|
const ownerName = branding?.ownerName?.trim();
|
|
12
|
+
// Operator branding URLs become masthead/callback hrefs, so a non-http(s)
|
|
13
|
+
// scheme (javascript:, data:) is dropped the same as an unset URL — the
|
|
14
|
+
// callers already render a <span> instead of an <a> when it is absent.
|
|
15
|
+
const productUrl = branding?.productUrl?.trim();
|
|
16
|
+
const ownerUrl = branding?.ownerUrl?.trim();
|
|
12
17
|
return {
|
|
13
18
|
productName,
|
|
14
|
-
...(
|
|
15
|
-
? { productUrl: branding.productUrl.trim() }
|
|
16
|
-
: {}),
|
|
19
|
+
...(productUrl && isSafeHttpUrl(productUrl) ? { productUrl } : {}),
|
|
17
20
|
...(ownerName ? { ownerName } : {}),
|
|
18
|
-
...(
|
|
19
|
-
? { ownerUrl: branding.ownerUrl.trim() }
|
|
20
|
-
: {}),
|
|
21
|
+
...(ownerUrl && isSafeHttpUrl(ownerUrl) ? { ownerUrl } : {}),
|
|
21
22
|
description: branding?.description?.trim() ||
|
|
22
23
|
`Manage the services this ${productName} instance makes available to agents.`,
|
|
23
24
|
pageTitle: branding?.pageTitle?.trim() ||
|
|
@@ -205,10 +206,13 @@ function jsonForInlineScript(value) {
|
|
|
205
206
|
* normal sign-in flow and a short-lived session token; bearer-only deployments
|
|
206
207
|
* retain the manual-token fallback.
|
|
207
208
|
*/
|
|
208
|
-
export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding) {
|
|
209
|
+
export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding, nonce) {
|
|
209
210
|
const auth = uiAuth ?? { kind: "bearer" };
|
|
210
211
|
const brand = resolveBranding(branding);
|
|
211
212
|
const title = brand.pageTitle;
|
|
213
|
+
// When the /ui response ships a nonce-based CSP, every <script> it emits must
|
|
214
|
+
// carry that nonce to run; without a nonce the markup is unchanged.
|
|
215
|
+
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
212
216
|
// Top-left corner. With an owner set it reads "<owner> <product>"; without
|
|
213
217
|
// one the product label stands alone. Either half links out when the
|
|
214
218
|
// matching URL is configured.
|
|
@@ -225,7 +229,7 @@ export function renderUiHtml(uiAuth, mcpUrl = "/mcp", branding) {
|
|
|
225
229
|
: `<span class="product">${escapeHtmlAttr(brand.productName)}</span>`
|
|
226
230
|
: "";
|
|
227
231
|
const clerkScript = uiAuth?.kind === "clerk"
|
|
228
|
-
? `<script defer crossorigin="anonymous" data-clerk-publishable-key="${escapeHtmlAttr(uiAuth.publishableKey)}" src="${escapeHtmlAttr(uiAuth.frontendApiUrl)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
|
|
232
|
+
? `<script${nonceAttr} defer crossorigin="anonymous" data-clerk-publishable-key="${escapeHtmlAttr(uiAuth.publishableKey)}" src="${escapeHtmlAttr(uiAuth.frontendApiUrl)}/npm/@clerk/clerk-js@6/dist/clerk.browser.js"></script>`
|
|
229
233
|
: "";
|
|
230
234
|
return `<!doctype html>
|
|
231
235
|
<html lang="en">
|
|
@@ -584,7 +588,7 @@ ${clerkScript}
|
|
|
584
588
|
</section>
|
|
585
589
|
</main>
|
|
586
590
|
|
|
587
|
-
<script>
|
|
591
|
+
<script${nonceAttr}>
|
|
588
592
|
const AUTH = ${jsonForInlineScript(auth)};
|
|
589
593
|
const MCP_URL = ${jsonForInlineScript(mcpUrl)};
|
|
590
594
|
const filterUiConnectors = ${filterUiConnectors.toString()};
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAIA,8CAA8C;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;OAM7B,CAAC;AAeR,MAAM,UAAU,eAAe,CAC7B,QAA2B;IAE3B,MAAM,WAAW,GAAG,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC;IAChE,MAAM,SAAS,GAAG,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC9C,
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ConnectorCallError } from "./errors.js";
|
|
2
|
+
import type { JsonSchema, Logger } from "./types.js";
|
|
3
|
+
export interface ValidateToolInputOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Tool address used in the error and warning text, conventionally
|
|
6
|
+
* `"connectorId.toolName"`.
|
|
7
|
+
*/
|
|
8
|
+
address: string;
|
|
9
|
+
/**
|
|
10
|
+
* Destination for the one-time warning emitted when a schema turns out to be
|
|
11
|
+
* unusable. Default console.
|
|
12
|
+
*/
|
|
13
|
+
logger?: Logger;
|
|
14
|
+
/**
|
|
15
|
+
* Fail-closed on a schema the validator cannot evaluate (default false =
|
|
16
|
+
* today's fail-open behavior). When true, a schema that cannot be compiled —
|
|
17
|
+
* or that only fails on first use, e.g. an unresolvable `$ref` — yields a
|
|
18
|
+
* non-retryable `invalid_args` error instead of passing the raw arguments
|
|
19
|
+
* through, so unvalidated input is never silently admitted. The happy path
|
|
20
|
+
* (a schema that compiles and validates) is unaffected.
|
|
21
|
+
*/
|
|
22
|
+
failClosed?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface PrecompileValidatorOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Tool address used in the warning text, conventionally
|
|
27
|
+
* `"connectorId.toolName"`.
|
|
28
|
+
*/
|
|
29
|
+
address: string;
|
|
30
|
+
/**
|
|
31
|
+
* Destination for the warning emitted when the schema cannot be compiled.
|
|
32
|
+
* Default console.
|
|
33
|
+
*/
|
|
34
|
+
logger?: Logger;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate call arguments against a tool's JSON Schema.
|
|
38
|
+
*
|
|
39
|
+
* Returns a non-retryable `invalid_args` ConnectorCallError describing the
|
|
40
|
+
* mismatch, or null when the arguments are acceptable. It deliberately returns
|
|
41
|
+
* rather than throws: the caller decides what to do with the failure, which is
|
|
42
|
+
* what lets a connector own its error prose, or strip connector-wide
|
|
43
|
+
* convention arguments (a `confirm` flag on writes, say) that individual tool
|
|
44
|
+
* schemas do not declare before deciding the call is really invalid.
|
|
45
|
+
*
|
|
46
|
+
* A schema the validator cannot compile (or that only fails on first use, e.g.
|
|
47
|
+
* an unresolvable `$ref`) is warned about once and then passed through — a
|
|
48
|
+
* broken schema should not break an otherwise working tool. Pass
|
|
49
|
+
* `failClosed: true` to instead reject such calls with `invalid_args`, for
|
|
50
|
+
* callers that would rather refuse a call than forward unvalidated arguments.
|
|
51
|
+
*
|
|
52
|
+
* The compiled validator is cached by **schema object identity**, so pass a
|
|
53
|
+
* stable object: hold the parsed manifest and hand the same schema back on
|
|
54
|
+
* every call. A schema rebuilt per call is a cache miss every time — it still
|
|
55
|
+
* validates correctly, but recompiles the validator on each call, silently and
|
|
56
|
+
* with nothing to show for it but latency.
|
|
57
|
+
*
|
|
58
|
+
* `api()` uses this internally; it is exported for connectors that implement
|
|
59
|
+
* the `Connector` interface directly.
|
|
60
|
+
*/
|
|
61
|
+
export declare function validateToolInput(schema: JsonSchema, args: unknown, opts: ValidateToolInputOptions): ConnectorCallError | null;
|
|
62
|
+
/**
|
|
63
|
+
* Eagerly compile and cache a tool's inputSchema so a schema the validator
|
|
64
|
+
* cannot use surfaces once at connector construction rather than silently on
|
|
65
|
+
* the first call. Reuses the same module-level cache `validateToolInput` reads,
|
|
66
|
+
* so the runtime path hits the cache. Warning-only: it never throws and never
|
|
67
|
+
* changes call behavior. A schema that only fails on first `validate()` (e.g.
|
|
68
|
+
* an unresolvable `$ref`) still slips through here and is caught at call time.
|
|
69
|
+
*/
|
|
70
|
+
export declare function precompileValidator(schema: JsonSchema, opts: PrecompileValidatorOptions): void;
|
|
71
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6BD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,wBAAwB,GAC7B,kBAAkB,GAAG,IAAI,CAqC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,0BAA0B,GAC/B,IAAI,CAQN"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Validator } from "@cfworker/json-schema";
|
|
2
|
+
import { ConnectorCallError } from "./errors.js";
|
|
3
|
+
// Lazy validator cache keyed by the schema object itself; null marks a schema
|
|
4
|
+
// the validator rejected (warned once, then passed through rather than
|
|
5
|
+
// breaking a working tool). A WeakMap so schemas belonging to a discarded
|
|
6
|
+
// connector are collectable, the same pattern compactSchema uses.
|
|
7
|
+
const validators = new WeakMap();
|
|
8
|
+
function unevaluableSchema(address) {
|
|
9
|
+
return new ConnectorCallError("invalid_args", `Cannot validate arguments for "${address}": its inputSchema could not be evaluated`);
|
|
10
|
+
}
|
|
11
|
+
function disableValidation(schema, address, logger, err) {
|
|
12
|
+
validators.set(schema, null);
|
|
13
|
+
logger.warn(`[connecta] tool "${address}" has an inputSchema the validator cannot use (${err instanceof Error ? err.message : String(err)}) — arguments are not validated`);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate call arguments against a tool's JSON Schema.
|
|
17
|
+
*
|
|
18
|
+
* Returns a non-retryable `invalid_args` ConnectorCallError describing the
|
|
19
|
+
* mismatch, or null when the arguments are acceptable. It deliberately returns
|
|
20
|
+
* rather than throws: the caller decides what to do with the failure, which is
|
|
21
|
+
* what lets a connector own its error prose, or strip connector-wide
|
|
22
|
+
* convention arguments (a `confirm` flag on writes, say) that individual tool
|
|
23
|
+
* schemas do not declare before deciding the call is really invalid.
|
|
24
|
+
*
|
|
25
|
+
* A schema the validator cannot compile (or that only fails on first use, e.g.
|
|
26
|
+
* an unresolvable `$ref`) is warned about once and then passed through — a
|
|
27
|
+
* broken schema should not break an otherwise working tool. Pass
|
|
28
|
+
* `failClosed: true` to instead reject such calls with `invalid_args`, for
|
|
29
|
+
* callers that would rather refuse a call than forward unvalidated arguments.
|
|
30
|
+
*
|
|
31
|
+
* The compiled validator is cached by **schema object identity**, so pass a
|
|
32
|
+
* stable object: hold the parsed manifest and hand the same schema back on
|
|
33
|
+
* every call. A schema rebuilt per call is a cache miss every time — it still
|
|
34
|
+
* validates correctly, but recompiles the validator on each call, silently and
|
|
35
|
+
* with nothing to show for it but latency.
|
|
36
|
+
*
|
|
37
|
+
* `api()` uses this internally; it is exported for connectors that implement
|
|
38
|
+
* the `Connector` interface directly.
|
|
39
|
+
*/
|
|
40
|
+
export function validateToolInput(schema, args, opts) {
|
|
41
|
+
const logger = opts.logger ?? console;
|
|
42
|
+
let validator = validators.get(schema);
|
|
43
|
+
if (validator === undefined) {
|
|
44
|
+
try {
|
|
45
|
+
validator = new Validator(schema, "2020-12", false);
|
|
46
|
+
validators.set(schema, validator);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
disableValidation(schema, opts.address, logger, err);
|
|
50
|
+
validator = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// A schema the validator could not compile (or that a prior call disabled):
|
|
54
|
+
// pass through by default, refuse when the caller opted into fail-closed.
|
|
55
|
+
if (validator === null) {
|
|
56
|
+
return opts.failClosed ? unevaluableSchema(opts.address) : null;
|
|
57
|
+
}
|
|
58
|
+
let result;
|
|
59
|
+
try {
|
|
60
|
+
result = validator.validate(args);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
// e.g. an unresolvable $ref — surfaces on first validate, not compile.
|
|
64
|
+
disableValidation(schema, opts.address, logger, err);
|
|
65
|
+
return opts.failClosed ? unevaluableSchema(opts.address) : null;
|
|
66
|
+
}
|
|
67
|
+
if (result && !result.valid) {
|
|
68
|
+
const units = result.errors.filter((u) => u.instanceLocation !== "#");
|
|
69
|
+
const detail = (units.length > 0 ? units : result.errors)
|
|
70
|
+
.slice(0, 3)
|
|
71
|
+
.map((u) => `${u.instanceLocation}: ${u.error}`)
|
|
72
|
+
.join("; ");
|
|
73
|
+
return new ConnectorCallError("invalid_args", `Invalid arguments for "${opts.address}": ${detail || "input does not match the tool's inputSchema"}`);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Eagerly compile and cache a tool's inputSchema so a schema the validator
|
|
79
|
+
* cannot use surfaces once at connector construction rather than silently on
|
|
80
|
+
* the first call. Reuses the same module-level cache `validateToolInput` reads,
|
|
81
|
+
* so the runtime path hits the cache. Warning-only: it never throws and never
|
|
82
|
+
* changes call behavior. A schema that only fails on first `validate()` (e.g.
|
|
83
|
+
* an unresolvable `$ref`) still slips through here and is caught at call time.
|
|
84
|
+
*/
|
|
85
|
+
export function precompileValidator(schema, opts) {
|
|
86
|
+
if (validators.has(schema))
|
|
87
|
+
return;
|
|
88
|
+
const logger = opts.logger ?? console;
|
|
89
|
+
try {
|
|
90
|
+
validators.set(schema, new Validator(schema, "2020-12", false));
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
disableValidation(schema, opts.address, logger, err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* a bump that forgets this file fails the build rather than shipping a stale
|
|
5
5
|
* version to `/health` and to downstream MCP handshakes.
|
|
6
6
|
*/
|
|
7
|
-
export declare const CONNECTA_VERSION = "0.
|
|
7
|
+
export declare const CONNECTA_VERSION = "0.4.1";
|
|
8
8
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind nine meta-tools.",
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
"types": "./dist/node.d.ts",
|
|
44
44
|
"import": "./dist/node.js"
|
|
45
45
|
},
|
|
46
|
+
"./json-schema": {
|
|
47
|
+
"types": "./dist/json-schema.d.ts",
|
|
48
|
+
"import": "./dist/json-schema.js"
|
|
49
|
+
},
|
|
46
50
|
"./quickjs": {
|
|
47
51
|
"types": "./dist/executors/quickjs.d.ts",
|
|
48
52
|
"import": "./dist/executors/quickjs.js"
|
package/src/catalog.ts
CHANGED
|
@@ -79,6 +79,41 @@ function refName(ref: string): string {
|
|
|
79
79
|
return ref.split("/").pop() ?? ref;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Whether a schema declares anything renderSchema knows how to render on its
|
|
84
|
+
* own. Used to decide if the non-allOf half of a schema is worth rendering:
|
|
85
|
+
* without this, a plain `{ allOf: [...] }` would render its (empty) local half
|
|
86
|
+
* through the raw-JSON fallback and emit `{} & …`.
|
|
87
|
+
*/
|
|
88
|
+
function declaresShape(s: Record<string, unknown>): boolean {
|
|
89
|
+
return (
|
|
90
|
+
typeof s.$ref === "string" ||
|
|
91
|
+
Array.isArray(s.oneOf) ||
|
|
92
|
+
Array.isArray(s.anyOf) ||
|
|
93
|
+
Array.isArray(s.enum) ||
|
|
94
|
+
s.const !== undefined ||
|
|
95
|
+
s.items !== undefined ||
|
|
96
|
+
s.properties !== undefined ||
|
|
97
|
+
s.type !== undefined
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Parenthesize a top-level union so it doesn't read as part of a surrounding
|
|
103
|
+
* `&`. Only separators outside braces count, so a nested union or a property
|
|
104
|
+
* description containing a pipe doesn't trigger stray parentheses.
|
|
105
|
+
*/
|
|
106
|
+
function grouped(part: string): string {
|
|
107
|
+
let nesting = 0;
|
|
108
|
+
for (let i = 0; i < part.length; i += 1) {
|
|
109
|
+
const char = part[i];
|
|
110
|
+
if (char === "{" || char === "(" || char === "[") nesting += 1;
|
|
111
|
+
else if (char === "}" || char === ")" || char === "]") nesting -= 1;
|
|
112
|
+
else if (nesting === 0 && part.startsWith(" | ", i)) return `(${part})`;
|
|
113
|
+
}
|
|
114
|
+
return part;
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
function renderSchema(
|
|
83
118
|
schema: unknown,
|
|
84
119
|
defs: Record<string, unknown>,
|
|
@@ -91,6 +126,27 @@ function renderSchema(
|
|
|
91
126
|
}
|
|
92
127
|
const s = schema as Record<string, unknown>;
|
|
93
128
|
|
|
129
|
+
// allOf composes rather than replaces: it is checked before every other
|
|
130
|
+
// keyword, and renders the schema's own shape alongside its members instead
|
|
131
|
+
// of returning early. A schema carrying both allOf and properties (the usual
|
|
132
|
+
// OpenAPI-derived "extend this base" shape, and equally legal with $ref,
|
|
133
|
+
// enum, const, or items) would otherwise silently drop whichever half lost
|
|
134
|
+
// the branch race. The schema's own shape comes first, being the more
|
|
135
|
+
// specific half, and is rendered at the current depth because its members
|
|
136
|
+
// sit at this nesting level, not one below.
|
|
137
|
+
if (Array.isArray(s.allOf)) {
|
|
138
|
+
const { allOf: _members, ...own } = s;
|
|
139
|
+
const parts = declaresShape(own)
|
|
140
|
+
? [renderSchema(own, defs, seen, depth)]
|
|
141
|
+
: [];
|
|
142
|
+
for (const member of s.allOf) {
|
|
143
|
+
parts.push(renderSchema(member, defs, seen, depth + 1));
|
|
144
|
+
}
|
|
145
|
+
if (parts.length === 0) return "unknown";
|
|
146
|
+
if (parts.length === 1) return parts[0] as string;
|
|
147
|
+
return parts.map(grouped).join(" & ");
|
|
148
|
+
}
|
|
149
|
+
|
|
94
150
|
if (typeof s.$ref === "string") {
|
|
95
151
|
const name = refName(s.$ref);
|
|
96
152
|
if (seen.has(name)) return name;
|
|
@@ -112,6 +168,11 @@ function renderSchema(
|
|
|
112
168
|
if (Array.isArray(s.enum)) {
|
|
113
169
|
return s.enum.map((value) => JSON.stringify(value)).join(" | ");
|
|
114
170
|
}
|
|
171
|
+
// Checked before type/properties so a discriminator like
|
|
172
|
+
// { type: "string", const: "emoji" } renders as "emoji" rather than string.
|
|
173
|
+
// JSON.stringify(undefined) returns undefined (not a string), so an explicit
|
|
174
|
+
// `const: undefined` must fall through to the regular type rendering.
|
|
175
|
+
if (s.const !== undefined) return JSON.stringify(s.const);
|
|
115
176
|
|
|
116
177
|
const type = s.type;
|
|
117
178
|
if (type === "array" || s.items) {
|
package/src/connectors/api.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ConnectorCallError } from "../errors.js";
|
|
1
|
+
import { precompileValidator, validateToolInput } from "../validate.js";
|
|
3
2
|
import type {
|
|
4
3
|
Connector,
|
|
5
4
|
ConnectorCredentialConfig,
|
|
@@ -50,6 +49,16 @@ export interface ApiOptions {
|
|
|
50
49
|
* on loose coercion.
|
|
51
50
|
*/
|
|
52
51
|
validateArgs?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Fail-closed on a tool whose `inputSchema` the validator cannot evaluate
|
|
54
|
+
* (default false). The default surfaces such a schema as a one-time warning
|
|
55
|
+
* and then passes the raw arguments through, so a broken schema never breaks
|
|
56
|
+
* an otherwise working tool. Set true to instead reject those calls with a
|
|
57
|
+
* non-retryable `invalid_args` ConnectorCallError, so a schema that cannot be
|
|
58
|
+
* enforced never silently admits unvalidated input. Only consulted when
|
|
59
|
+
* `validateArgs` is not false.
|
|
60
|
+
*/
|
|
61
|
+
strictValidation?: boolean;
|
|
53
62
|
tools: ApiTool[];
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -74,34 +83,15 @@ export function api(id: string, opts: ApiOptions): Connector {
|
|
|
74
83
|
}));
|
|
75
84
|
const byName = new Map(opts.tools.map((t) => [t.name, t]));
|
|
76
85
|
const validateArgs = opts.validateArgs ?? true;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
) => {
|
|
85
|
-
validators.set(tool.name, null);
|
|
86
|
-
ctx.logger.warn(
|
|
87
|
-
`[connecta] tool "${id}.${tool.name}" has an inputSchema the validator cannot use (${
|
|
88
|
-
err instanceof Error ? err.message : String(err)
|
|
89
|
-
}) — arguments are not validated`,
|
|
90
|
-
);
|
|
91
|
-
};
|
|
92
|
-
const validatorFor = (tool: ApiTool, ctx: ConnectorContext) => {
|
|
93
|
-
let validator = validators.get(tool.name);
|
|
94
|
-
if (validator === undefined) {
|
|
95
|
-
try {
|
|
96
|
-
validator = new Validator(tool.inputSchema as never, "2020-12", false);
|
|
97
|
-
validators.set(tool.name, validator);
|
|
98
|
-
} catch (err) {
|
|
99
|
-
disableValidation(tool, ctx, err);
|
|
100
|
-
validator = null;
|
|
101
|
-
}
|
|
86
|
+
const strictValidation = opts.strictValidation ?? false;
|
|
87
|
+
if (validateArgs) {
|
|
88
|
+
// Compile each schema now so a validator-hostile inputSchema surfaces once
|
|
89
|
+
// here rather than silently on its first call. Warning-only; never throws.
|
|
90
|
+
for (const t of opts.tools) {
|
|
91
|
+
if (t.inputSchema)
|
|
92
|
+
precompileValidator(t.inputSchema, { address: `${id}.${t.name}` });
|
|
102
93
|
}
|
|
103
|
-
|
|
104
|
-
};
|
|
94
|
+
}
|
|
105
95
|
return {
|
|
106
96
|
id,
|
|
107
97
|
title: opts.title,
|
|
@@ -121,27 +111,12 @@ export function api(id: string, opts: ApiOptions): Connector {
|
|
|
121
111
|
}
|
|
122
112
|
const input = args ?? {};
|
|
123
113
|
if (validateArgs && tool.inputSchema) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
disableValidation(tool, ctx, err);
|
|
131
|
-
}
|
|
132
|
-
if (result && !result.valid) {
|
|
133
|
-
const units = result.errors.filter(
|
|
134
|
-
(u) => u.instanceLocation !== "#",
|
|
135
|
-
);
|
|
136
|
-
const detail = (units.length > 0 ? units : result.errors)
|
|
137
|
-
.slice(0, 3)
|
|
138
|
-
.map((u) => `${u.instanceLocation}: ${u.error}`)
|
|
139
|
-
.join("; ");
|
|
140
|
-
throw new ConnectorCallError(
|
|
141
|
-
"invalid_args",
|
|
142
|
-
`Invalid arguments for "${id}.${name}": ${detail || "input does not match the tool's inputSchema"}`,
|
|
143
|
-
);
|
|
144
|
-
}
|
|
114
|
+
const invalid = validateToolInput(tool.inputSchema, input, {
|
|
115
|
+
address: `${id}.${name}`,
|
|
116
|
+
logger: ctx.logger,
|
|
117
|
+
failClosed: strictValidation,
|
|
118
|
+
});
|
|
119
|
+
if (invalid) throw invalid;
|
|
145
120
|
}
|
|
146
121
|
return tool.handler(input, ctx);
|
|
147
122
|
},
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
Connector,
|
|
11
11
|
ConnectorContext,
|
|
12
12
|
ConnectorStatus,
|
|
13
|
+
Logger,
|
|
13
14
|
ToolDef,
|
|
14
15
|
} from "../types.js";
|
|
15
16
|
|
|
@@ -23,6 +24,19 @@ export interface RemoteMcpOptions {
|
|
|
23
24
|
title?: string;
|
|
24
25
|
description?: string;
|
|
25
26
|
auth?: RemoteMcpAuth;
|
|
27
|
+
/**
|
|
28
|
+
* Refuse to connect to a non-`https://` `url` at construction (default
|
|
29
|
+
* false). Loopback hosts (`localhost`, `127.0.0.1`, `[::1]`) are always
|
|
30
|
+
* allowed for local development. Off by default, static `headers` credentials
|
|
31
|
+
* over a cleartext connection are warned about but permitted; set this true
|
|
32
|
+
* to make that misconfiguration a hard error instead.
|
|
33
|
+
*/
|
|
34
|
+
requireHttps?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Destination for the cleartext-credential warning emitted at construction.
|
|
37
|
+
* Default console.
|
|
38
|
+
*/
|
|
39
|
+
logger?: Logger;
|
|
26
40
|
/**
|
|
27
41
|
* @internal Testing seam. When set, this transport is used instead of the
|
|
28
42
|
* HTTP transport, letting tests point the connector at an in-process MCP
|
|
@@ -35,6 +49,15 @@ function msg(err: unknown): string {
|
|
|
35
49
|
return err instanceof Error ? err.message : String(err);
|
|
36
50
|
}
|
|
37
51
|
|
|
52
|
+
function isLoopbackHost(hostname: string): boolean {
|
|
53
|
+
return (
|
|
54
|
+
hostname === "localhost" ||
|
|
55
|
+
hostname === "127.0.0.1" ||
|
|
56
|
+
hostname === "[::1]" ||
|
|
57
|
+
hostname === "::1"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
38
61
|
interface ConnectionState {
|
|
39
62
|
client: Client | null;
|
|
40
63
|
transport: Transport | null;
|
|
@@ -60,6 +83,27 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
60
83
|
// from the isolate singleton. Those are request-bound in Cloudflare Workers.
|
|
61
84
|
const states = new WeakMap<object, ConnectionState>();
|
|
62
85
|
const isOauth = opts.auth?.type === "oauth";
|
|
86
|
+
const logger = opts.logger ?? console;
|
|
87
|
+
|
|
88
|
+
// Check the destination scheme once at construction: buildTransport (and the
|
|
89
|
+
// SDK's fetch) attach any static credentials to every request, so an http://
|
|
90
|
+
// endpoint sends bearer tokens / API keys in cleartext. Loopback is exempt
|
|
91
|
+
// for local development.
|
|
92
|
+
const destination = new URL(opts.url);
|
|
93
|
+
const insecureDestination =
|
|
94
|
+
destination.protocol !== "https:" && !isLoopbackHost(destination.hostname);
|
|
95
|
+
if (insecureDestination) {
|
|
96
|
+
if (opts.requireHttps) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`[connecta] connector "${id}" url ${opts.url} is not https:// (and not loopback) — refusing to connect (requireHttps).`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (opts.auth?.type === "headers") {
|
|
102
|
+
logger.warn(
|
|
103
|
+
`[connecta] connector "${id}" sends static credentials to ${opts.url} over a non-https:// connection — those tokens will be transmitted in cleartext.`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
63
107
|
|
|
64
108
|
/** Typed per-call auth signal; the SDK's UnauthorizedError stays as cause. */
|
|
65
109
|
const authRequiredError = (cause: unknown) =>
|
|
@@ -98,6 +142,14 @@ export function remoteMcp(id: string, opts: RemoteMcpOptions): Connector {
|
|
|
98
142
|
return state.provider;
|
|
99
143
|
};
|
|
100
144
|
|
|
145
|
+
// NOTE: StreamableHTTPClientTransport speaks over fetch, which transparently
|
|
146
|
+
// follows 3xx redirects. A malicious or compromised downstream MCP could
|
|
147
|
+
// redirect to an internal address (e.g. http://169.254.169.254/…) and fetch
|
|
148
|
+
// would re-issue the request — potentially carrying static auth headers. The
|
|
149
|
+
// scheme check above only guards the first hop; a fully robust guard (manual
|
|
150
|
+
// redirect handling + per-hop re-validation + stripping auth headers cross-
|
|
151
|
+
// origin) lives in the SDK transport and is deferred to a future non-patch
|
|
152
|
+
// release rather than reimplemented here.
|
|
101
153
|
const buildTransport = (
|
|
102
154
|
ctx: ConnectorContext,
|
|
103
155
|
state: ConnectionState,
|
package/src/credentials.ts
CHANGED
|
@@ -21,7 +21,8 @@ interface CredentialPlaintext {
|
|
|
21
21
|
|
|
22
22
|
export interface CredentialFieldMetadata {
|
|
23
23
|
configured: true;
|
|
24
|
-
|
|
24
|
+
/** Only emitted when the value is long enough that four chars don't leak much. */
|
|
25
|
+
lastFour?: string;
|
|
25
26
|
updatedAt: string;
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -200,7 +201,9 @@ export class CredentialVault {
|
|
|
200
201
|
field,
|
|
201
202
|
{
|
|
202
203
|
configured: true as const,
|
|
203
|
-
|
|
204
|
+
// Only reveal the tail on values comfortably longer than four chars;
|
|
205
|
+
// for a short secret those four would be half of it.
|
|
206
|
+
...(value.length >= 12 ? { lastFour: value.slice(-4) } : {}),
|
|
204
207
|
updatedAt: credential.updatedAt,
|
|
205
208
|
},
|
|
206
209
|
]),
|
|
@@ -208,7 +211,7 @@ export class CredentialVault {
|
|
|
208
211
|
const single = fields.value;
|
|
209
212
|
return {
|
|
210
213
|
configured: true,
|
|
211
|
-
...(single ? { lastFour: single.lastFour } : {}),
|
|
214
|
+
...(single?.lastFour ? { lastFour: single.lastFour } : {}),
|
|
212
215
|
updatedAt: credential.updatedAt,
|
|
213
216
|
fields,
|
|
214
217
|
};
|