@zackbart/connecta 0.18.2 → 0.19.0
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 +100 -4
- package/README.md +4 -0
- package/dist/catalog-service.d.ts +20 -13
- package/dist/catalog-service.js +123 -116
- package/dist/catalog.js +29 -46
- package/dist/connector-scope.js +2 -7
- package/dist/connectors/api.d.ts +4 -16
- package/dist/connectors/api.js +19 -46
- package/dist/connectors/guarded-fetch.d.ts +9 -23
- package/dist/connectors/guarded-fetch.js +38 -76
- package/dist/connectors/remote-mcp.js +36 -79
- package/dist/errors.d.ts +6 -27
- package/dist/errors.js +8 -5
- package/dist/execute.d.ts +24 -22
- package/dist/execute.js +98 -145
- package/dist/executor-result.d.ts +1 -0
- package/dist/executor-result.js +4 -11
- package/dist/executors/quickjs-child.js +1 -3
- package/dist/executors/quickjs-runtime.js +1 -3
- package/dist/executors/quickjs.js +1 -3
- package/dist/index.js +27 -57
- package/dist/invocation.js +114 -178
- package/dist/meta-tools.d.ts +15 -28
- package/dist/meta-tools.js +33 -89
- package/dist/providers/cloudflare.d.ts +2 -18
- package/dist/providers/cloudflare.js +1460 -2451
- package/dist/providers/linear.d.ts +4 -41
- package/dist/providers/linear.js +8 -39
- package/dist/providers/mixpanel.d.ts +3 -25
- package/dist/providers/mixpanel.js +7 -22
- package/dist/providers/notion.d.ts +1 -15
- package/dist/providers/notion.js +44 -173
- package/dist/providers/revenuecat.d.ts +4 -57
- package/dist/providers/revenuecat.js +10 -93
- package/dist/providers/stripe.d.ts +1 -12
- package/dist/providers/stripe.js +7 -45
- package/dist/registry.d.ts +16 -34
- package/dist/registry.js +18 -103
- package/dist/result-shapes.d.ts +13 -0
- package/dist/result-shapes.js +331 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/oauth.js +3 -3
- package/dist/routes/shared.d.ts +15 -15
- package/dist/routes/shared.js +1 -3
- package/dist/skills.js +3 -3
- package/dist/timeout.d.ts +8 -7
- package/dist/timeout.js +47 -38
- package/dist/types.d.ts +3 -3
- package/dist/ui.d.ts +1 -25
- package/dist/ui.js +18 -45
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +5 -2
- package/documentation/call-admission.md +1 -1
- package/documentation/cloudflare.md +1 -1
- package/documentation/code-mode.md +13 -13
- package/documentation/connectors.md +34 -1
- package/documentation/linear.md +1 -1
- package/documentation/meta-tools.md +17 -3
- package/documentation/mixpanel.md +1 -1
- package/documentation/notion.md +1 -1
- package/documentation/operations.md +20 -15
- package/documentation/provider-conventions.md +1 -1
- package/documentation/revenuecat.md +1 -1
- package/documentation/stripe.md +1 -1
- package/documentation/upgrading.md +24 -4
- package/ethos.md +74 -120
- package/package.json +3 -4
- package/templates/node/package.json +1 -1
- package/documentation/code-first-exploration.md +0 -292
- package/documentation/mcp-2026-07-28.md +0 -46
- package/documentation/mcp-ui-design.md +0 -382
- package/documentation/program-ui-read-calls.md +0 -213
- package/documentation/provider-audit.md +0 -198
- package/documentation/rich-output-design.md +0 -211
package/dist/meta-tools.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { boundedDiscoveryText, CatalogService, DEFAULT_SEARCH_LIMIT, DiscoveryPolicyError, groupedSearchResult, MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES, MAX_SEARCH_LIMIT, } from "./catalog-service.js";
|
|
3
3
|
import { resolveDiscoveryConcurrency } from "./concurrency.js";
|
|
4
|
+
import { msg } from "./errors.js";
|
|
5
|
+
import { serializeResultText } from "./executor-result.js";
|
|
4
6
|
import { InvocationService, MAX_RETRY_BACKOFF_MS, retryBackoffMs, } from "./invocation.js";
|
|
5
7
|
import { isValidMaxResultBytes, MIN_MAX_RESULT_BYTES, resolveMaxResultBytes, } from "./registry.js";
|
|
6
8
|
import { hasConnectorGuides, listSkills, resolveSkill, } from "./skills.js";
|
|
@@ -9,9 +11,9 @@ export { MAX_DESCRIBE_ADDRESSES, MAX_DISCOVERY_RESULT_BYTES, MAX_RETRY_BACKOFF_M
|
|
|
9
11
|
const RESULT_TTL_SECONDS = 900;
|
|
10
12
|
const enc = new TextEncoder();
|
|
11
13
|
const dec = new TextDecoder();
|
|
12
|
-
export function jsonResult(obj) {
|
|
14
|
+
export function jsonResult(obj, text = JSON.stringify(obj)) {
|
|
13
15
|
return {
|
|
14
|
-
content: [{ type: "text", text
|
|
16
|
+
content: [{ type: "text", text }],
|
|
15
17
|
...(obj !== null && typeof obj === "object" && !Array.isArray(obj)
|
|
16
18
|
? { structuredContent: obj }
|
|
17
19
|
: {}),
|
|
@@ -20,9 +22,6 @@ export function jsonResult(obj) {
|
|
|
20
22
|
export function errorResult(message) {
|
|
21
23
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
22
24
|
}
|
|
23
|
-
function msg(err) {
|
|
24
|
-
return err instanceof Error ? err.message : String(err);
|
|
25
|
-
}
|
|
26
25
|
function discoveryErrorResult(error) {
|
|
27
26
|
const result = jsonResult({
|
|
28
27
|
error: {
|
|
@@ -38,12 +37,7 @@ async function discoveryResult(operation, hint) {
|
|
|
38
37
|
try {
|
|
39
38
|
const value = await operation();
|
|
40
39
|
const text = boundedDiscoveryText(value, hint);
|
|
41
|
-
return
|
|
42
|
-
content: [{ type: "text", text }],
|
|
43
|
-
...(value !== null && typeof value === "object" && !Array.isArray(value)
|
|
44
|
-
? { structuredContent: value }
|
|
45
|
-
: {}),
|
|
46
|
-
};
|
|
40
|
+
return jsonResult(value, text);
|
|
47
41
|
}
|
|
48
42
|
catch (err) {
|
|
49
43
|
if (err instanceof DiscoveryPolicyError) {
|
|
@@ -58,20 +52,8 @@ function isContinuationByte(b) {
|
|
|
58
52
|
}
|
|
59
53
|
/** Smallest accepted `get_result` byte offset. */
|
|
60
54
|
const MIN_RESULT_OFFSET = 0;
|
|
61
|
-
/**
|
|
62
|
-
*
|
|
63
|
-
* at or past {@link MIN_RESULT_OFFSET}. Shared by the registered zod schema and
|
|
64
|
-
* the handler's own check, the way `isValidMaxResultBytes` is shared across the
|
|
65
|
-
* cap's intake points (issue #32) — so a value valid at the wire is valid in
|
|
66
|
-
* process, and the two cannot drift.
|
|
67
|
-
*
|
|
68
|
-
* Everything else is rejected rather than coerced, because coercion is how an
|
|
69
|
-
* out-of-domain offset used to void a result silently: `Math.max(0, NaN)` is
|
|
70
|
-
* `NaN`, which slices to nothing, serializes as `"offset": null`, and reports
|
|
71
|
-
* no `nextOffset` — a caller sees a successful, empty result instead of an
|
|
72
|
-
* error. An offset past the end of the payload stays legal: it is a whole
|
|
73
|
-
* number of bytes, and it answers with an empty final page.
|
|
74
|
-
*/
|
|
55
|
+
/** Whole-byte offset accepted by the result representation documented in
|
|
56
|
+
* documentation/meta-tools.md#result-representation. */
|
|
75
57
|
function isValidResultOffset(value) {
|
|
76
58
|
return Number.isInteger(value) && value >= MIN_RESULT_OFFSET;
|
|
77
59
|
}
|
|
@@ -93,21 +75,8 @@ export function alignStartToCharBoundary(bytes, offset) {
|
|
|
93
75
|
o--;
|
|
94
76
|
return o;
|
|
95
77
|
}
|
|
96
|
-
/**
|
|
97
|
-
*
|
|
98
|
-
* `(offset, total]`, so decoding `bytes[offset, end)` never splits a codepoint
|
|
99
|
-
* (which would emit U+FFFD and break byte-exact reassembly). If backing up
|
|
100
|
-
* would make no progress — a single codepoint wider than the window — extend
|
|
101
|
-
* forward to the end of that codepoint instead so paging always advances.
|
|
102
|
-
* Assumes `offset` is itself a codepoint boundary (offsets are the prior
|
|
103
|
-
* `nextOffset`, which this function guarantees, and 0 is always a boundary).
|
|
104
|
-
*
|
|
105
|
-
* The return is always `> offset` while `offset < total`, whatever `end` is
|
|
106
|
-
* asked for. That is the belt-and-braces half of issue #32: cap validation
|
|
107
|
-
* keeps an empty window from arising in the first place, and this keeps an
|
|
108
|
-
* empty window from turning into a `nextOffset === offset` paging loop if one
|
|
109
|
-
* ever does. Exported for direct testing of that invariant.
|
|
110
|
-
*/
|
|
78
|
+
/** End boundary for UTF-8-safe, forward-progressing result pages. See
|
|
79
|
+
* documentation/meta-tools.md#result-representation. */
|
|
111
80
|
export function alignEndToCharBoundary(bytes, offset, end, total) {
|
|
112
81
|
if (end >= total)
|
|
113
82
|
return total;
|
|
@@ -615,10 +584,6 @@ function applyFieldsToContent(content, fields, outputSchema) {
|
|
|
615
584
|
* the three give one answer to the same question. A value JSON cannot serialize
|
|
616
585
|
* at all (a BigInt) still throws, as before, and is reported as a failure.
|
|
617
586
|
*/
|
|
618
|
-
function serializeResultText(value) {
|
|
619
|
-
const serialized = JSON.stringify(value);
|
|
620
|
-
return serialized === undefined ? String(value) : serialized;
|
|
621
|
-
}
|
|
622
587
|
/**
|
|
623
588
|
* Stash `text` under `result:<uuid>` (ttl 900s) and describe it as the
|
|
624
589
|
* truncation notice every over-cap path hands back.
|
|
@@ -761,7 +726,7 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
761
726
|
requestScope,
|
|
762
727
|
probeTimeoutMs,
|
|
763
728
|
concurrency: discoveryConcurrency,
|
|
764
|
-
|
|
729
|
+
defer: opts.defer,
|
|
765
730
|
// searchRoute keeps its top-level default. In-program callers use a
|
|
766
731
|
// separate CatalogService configured for connecta.search.
|
|
767
732
|
});
|
|
@@ -792,19 +757,18 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
792
757
|
// override the registry already warned about at startup is dropped
|
|
793
758
|
// here, so the connector simply inherits `globalCap`.
|
|
794
759
|
const cap = resolveMaxResultBytes(resolved.connector.maxResultBytes, globalCap);
|
|
760
|
+
const processed = (toolResult, truncated, value) => ({
|
|
761
|
+
toolResult,
|
|
762
|
+
...value,
|
|
763
|
+
...(truncated ? { friction: "result_too_large" } : {}),
|
|
764
|
+
});
|
|
795
765
|
if (call.resultMode === "value") {
|
|
796
766
|
let value = fields
|
|
797
767
|
? projectionValue(result, fields, resolved.definition.outputSchema)
|
|
798
768
|
: result;
|
|
799
769
|
const guarded = await guardValue(value, results, cap);
|
|
800
770
|
value = guarded.result;
|
|
801
|
-
return {
|
|
802
|
-
toolResult: jsonResult({ ok: true, data: value }),
|
|
803
|
-
value,
|
|
804
|
-
...(guarded.truncated
|
|
805
|
-
? { friction: "result_too_large" }
|
|
806
|
-
: {}),
|
|
807
|
-
};
|
|
771
|
+
return processed(jsonResult({ ok: true, data: value }), guarded.truncated, { value });
|
|
808
772
|
}
|
|
809
773
|
if (resolved.connector.kind === "mcp") {
|
|
810
774
|
const mcpResult = result;
|
|
@@ -813,33 +777,25 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
813
777
|
content = applyFieldsToContent(content, fields, resolved.definition.outputSchema);
|
|
814
778
|
}
|
|
815
779
|
const guarded = await guardContent(content, results, cap);
|
|
816
|
-
return
|
|
817
|
-
toolResult: guarded.result,
|
|
818
|
-
...(guarded.truncated
|
|
819
|
-
? { friction: "result_too_large" }
|
|
820
|
-
: {}),
|
|
821
|
-
};
|
|
780
|
+
return processed(guarded.result, guarded.truncated);
|
|
822
781
|
}
|
|
823
782
|
const value = fields
|
|
824
783
|
? projectionValue(result, fields, resolved.definition.outputSchema)
|
|
825
784
|
: result;
|
|
826
785
|
const guarded = await guardText(serializeResultText(value), results, cap);
|
|
827
|
-
return {
|
|
828
|
-
toolResult: guarded.result,
|
|
829
|
-
value,
|
|
830
|
-
...(guarded.truncated
|
|
831
|
-
? { friction: "result_too_large" }
|
|
832
|
-
: {}),
|
|
833
|
-
};
|
|
786
|
+
return processed(guarded.result, guarded.truncated, { value });
|
|
834
787
|
},
|
|
835
788
|
activityFriction: (processed) => processed.friction,
|
|
836
789
|
});
|
|
837
790
|
if (!outcome.ok) {
|
|
838
791
|
const structuredRecovery = outcome.error.nextAction !== undefined;
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
792
|
+
const recoveryRequired = structuredRecovery ||
|
|
793
|
+
[
|
|
794
|
+
"auth_required",
|
|
795
|
+
"invalid_args",
|
|
796
|
+
"input_required_unsupported",
|
|
797
|
+
].includes(outcome.error.code);
|
|
798
|
+
const failedResult = recoveryRequired ||
|
|
843
799
|
call.resultMode === "value"
|
|
844
800
|
? jsonResult({
|
|
845
801
|
ok: false,
|
|
@@ -849,10 +805,7 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
|
|
|
849
805
|
...(call.diagnostics ? { timing: outcome.timing } : {}),
|
|
850
806
|
})
|
|
851
807
|
: errorResult(outcome.error.message);
|
|
852
|
-
if (
|
|
853
|
-
outcome.error.code === "auth_required" ||
|
|
854
|
-
outcome.error.code === "invalid_args" ||
|
|
855
|
-
outcome.error.code === "input_required_unsupported") {
|
|
808
|
+
if (recoveryRequired) {
|
|
856
809
|
failedResult.isError = true;
|
|
857
810
|
}
|
|
858
811
|
return {
|
|
@@ -1050,7 +1003,6 @@ const CALL_DESTRUCTIVE_DESC = "Call any tool not explicitly annotated readOnlyHi
|
|
|
1050
1003
|
const GET_RESULT_DESC = "Page a truncated direct-call result by id and byte offset. A program result is never paged; reduce it inside execute_code. Returns text, offset, nextOffset when more remains, and totalBytes.";
|
|
1051
1004
|
const AUTHORIZE_DESC = "Use after auth_required. Returns an OAuth or operator-credential handoff, or reports required deployment configuration. force=true restarts OAuth only; this tool never accepts credentials.";
|
|
1052
1005
|
const SKILLS_DESC = 'List or fetch on-demand guidance. Fetch usage once per task for program syntax, selection, repair, examples, and runtime details.';
|
|
1053
|
-
const SEARCH_WITH_DESCRIBE_DESC = SEARCH_DESC;
|
|
1054
1006
|
/**
|
|
1055
1007
|
* Sentences appended to a meta-tool description only when this connection
|
|
1056
1008
|
* actually has connector guides. Tool descriptions are always-loaded context,
|
|
@@ -1107,20 +1059,12 @@ const CALL_INPUT_SCHEMA = {
|
|
|
1107
1059
|
*/
|
|
1108
1060
|
export function registerMetaTools(server, registry, ctx) {
|
|
1109
1061
|
const mt = createMetaTools(registry, ctx.baseUrl, {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
...(ctx.discoveryConcurrency !== undefined
|
|
1117
|
-
? { discoveryConcurrency: ctx.discoveryConcurrency }
|
|
1118
|
-
: {}),
|
|
1119
|
-
...(ctx.activity !== undefined ? { activity: ctx.activity } : {}),
|
|
1120
|
-
...(ctx.requestSignal !== undefined
|
|
1121
|
-
? { requestSignal: ctx.requestSignal }
|
|
1122
|
-
: {}),
|
|
1123
|
-
...(ctx.defer !== undefined ? { defer: ctx.defer } : {}),
|
|
1062
|
+
defaultToolTimeoutMs: ctx.defaultToolTimeoutMs,
|
|
1063
|
+
probeTimeoutMs: ctx.probeTimeoutMs,
|
|
1064
|
+
discoveryConcurrency: ctx.discoveryConcurrency,
|
|
1065
|
+
activity: ctx.activity,
|
|
1066
|
+
requestSignal: ctx.requestSignal,
|
|
1067
|
+
defer: ctx.defer,
|
|
1124
1068
|
});
|
|
1125
1069
|
server.registerTool("skills", {
|
|
1126
1070
|
description: describedFor(registry, SKILLS_DESC, "skills"),
|
|
@@ -1129,7 +1073,7 @@ export function registerMetaTools(server, registry, ctx) {
|
|
|
1129
1073
|
_meta: { ui: { visibility: ["model"] } },
|
|
1130
1074
|
}, async (args) => mt.skills(args));
|
|
1131
1075
|
server.registerTool("search_tools", {
|
|
1132
|
-
description: describedFor(registry,
|
|
1076
|
+
description: describedFor(registry, SEARCH_DESC, "search"),
|
|
1133
1077
|
inputSchema: z.object({
|
|
1134
1078
|
query: z.string().optional(),
|
|
1135
1079
|
connector: z.string().optional(),
|
|
@@ -3,25 +3,9 @@ import type { Connector, ConnectorCredentialConfig } from "../types.js";
|
|
|
3
3
|
export declare const CLOUDFLARE_API_BASE = "https://api.cloudflare.com/client/v4";
|
|
4
4
|
/** Authentication schemes accepted by Cloudflare's v4 API. */
|
|
5
5
|
export type CloudflareAuthentication = "apiToken" | "globalApiKey";
|
|
6
|
-
/**
|
|
7
|
-
* Every DNS record type the records API accepts, for filtering a list.
|
|
8
|
-
* Enumerated in the schema so an agent picks a legal type without reading
|
|
9
|
-
* Cloudflare's documentation.
|
|
10
|
-
*/
|
|
6
|
+
/** See documentation/cloudflare.md#dns-record-types. */
|
|
11
7
|
export declare const CLOUDFLARE_DNS_RECORD_TYPES: readonly ["A", "AAAA", "CAA", "CERT", "CNAME", "DNSKEY", "DS", "HTTPS", "LOC", "MX", "NAPTR", "NS", "OPENPGPKEY", "PTR", "SMIMEA", "SRV", "SSHFP", "SVCB", "TLSA", "TXT", "URI"];
|
|
12
|
-
/**
|
|
13
|
-
* The record types whose value is a single `content` string — the eight this
|
|
14
|
-
* connection can create and update.
|
|
15
|
-
*
|
|
16
|
-
* The other thirteen (CAA, CERT, DNSKEY, DS, HTTPS, LOC, NAPTR, SMIMEA, SRV,
|
|
17
|
-
* SSHFP, SVCB, TLSA, URI) carry a per-type structured `data` object instead,
|
|
18
|
-
* each with its own field set. Accepting them here would mean either a
|
|
19
|
-
* free-form `data` passthrough — exactly the untyped `{}` this connection
|
|
20
|
-
* exists to avoid — or thirteen more hand-written schemas for record types
|
|
21
|
-
* that are rare in the day-to-day work this surface is for. They remain fully
|
|
22
|
-
* readable and filterable; only the named create/update tools omit them. The
|
|
23
|
-
* guarded raw mutation tool remains available for their documented bodies.
|
|
24
|
-
*/
|
|
8
|
+
/** Content-valued types only; see documentation/cloudflare.md#dns-record-types. */
|
|
25
9
|
export declare const CLOUDFLARE_CONTENT_DNS_RECORD_TYPES: readonly ["A", "AAAA", "CNAME", "MX", "NS", "OPENPGPKEY", "PTR", "TXT"];
|
|
26
10
|
export interface CloudflareOptions {
|
|
27
11
|
/** Human-readable display name; defaults to "Cloudflare". */
|