@zackbart/connecta 0.24.4 → 0.25.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/AGENTS.md +40 -10
- package/CHANGELOG.md +253 -0
- package/dist/advertised-schema.js +53 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +141 -105
- package/dist/catalog.js +258 -33
- package/dist/concurrency.js +24 -23
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +2 -2
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +317 -241
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/execute.js +363 -257
- package/dist/executor-admission.js +67 -50
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +251 -210
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +51 -20
- package/dist/invocation.js +324 -285
- package/dist/meta-tools.js +206 -92
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +46 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +38 -2
- package/dist/program-source.js +205 -0
- package/dist/providers/notion.js +2 -2
- package/dist/providers/revenuecat.js +32 -5
- package/dist/providers/stripe.d.ts +3 -2
- package/dist/providers/vercel.js +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +305 -260
- package/dist/routes/activity.js +120 -137
- package/dist/routes/credentials.js +174 -163
- package/dist/routes/mcp.js +129 -70
- package/dist/routes/oauth-management.js +60 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +82 -0
- package/dist/routes/shared.d.ts +2 -0
- package/dist/routes/ui-data.js +210 -0
- package/dist/routes/ui.js +64 -32
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +111 -89
- package/dist/skills.js +1 -1
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +17 -47
- package/dist/types.d.ts +27 -7
- package/dist/ui.d.ts +16 -2
- package/dist/ui.js +50 -166
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +184 -10
- package/documentation/auth.md +80 -0
- package/documentation/code-mode.md +69 -24
- package/documentation/meta-tools.md +164 -27
- package/ethos.md +44 -47
- package/examples/worker/README.md +51 -5
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +6 -6
- package/examples/worker/wrangler.jsonc +16 -4
- package/package.json +11 -3
- package/templates/node/package.json +1 -1
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -96
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -188
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
- package/dist/tool-safety.d.ts +0 -9
package/dist/catalog.js
CHANGED
|
@@ -35,11 +35,29 @@ class SchemaWork {
|
|
|
35
35
|
const visit = this.visit.bind(this);
|
|
36
36
|
const text = this.text.bind(this);
|
|
37
37
|
const ancestors = [];
|
|
38
|
+
let serializedBytes = 0;
|
|
39
|
+
const addSerialized = (value) => {
|
|
40
|
+
serializedBytes += schemaEncoder.encode(value).length;
|
|
41
|
+
if (serializedBytes > this.byteLimit)
|
|
42
|
+
throw schemaSizeExceeded;
|
|
43
|
+
};
|
|
38
44
|
return this.text(JSON.stringify(value, function (key, item) {
|
|
39
45
|
visit();
|
|
40
46
|
text(key);
|
|
41
47
|
if (typeof item === "string")
|
|
42
48
|
text(item);
|
|
49
|
+
const omitted = item === undefined || typeof item === "function" || typeof item === "symbol";
|
|
50
|
+
const root = key === "" && ancestors.length === 0;
|
|
51
|
+
if (!omitted) {
|
|
52
|
+
// Array indexes are implicit in JSON. Object keys and primitive values
|
|
53
|
+
// are the useful lower bound; the final text check remains authoritative
|
|
54
|
+
// for braces, commas, and values whose encoding is larger than this bound.
|
|
55
|
+
if (!root && !Array.isArray(this))
|
|
56
|
+
addSerialized(JSON.stringify(key));
|
|
57
|
+
if (typeof item !== "object" || item === null) {
|
|
58
|
+
addSerialized(JSON.stringify(item));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
43
61
|
if (item !== null && typeof item === "object") {
|
|
44
62
|
while (ancestors.length && ancestors[ancestors.length - 1] !== this) {
|
|
45
63
|
ancestors.pop();
|
|
@@ -481,17 +499,114 @@ function renderConstraints(base, schema, byteLimit, onTruncated) {
|
|
|
481
499
|
? base
|
|
482
500
|
: `${grouped(base)} /* ${kept.join("; ")} */`;
|
|
483
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* `grouped` for the TypeScript dialect, whose JSDoc prose and string literals
|
|
504
|
+
* may carry unbalanced brackets from a downstream description: skipping both
|
|
505
|
+
* keeps a stray `)` in prose from hiding a real top-level union.
|
|
506
|
+
*/
|
|
507
|
+
function groupedType(part) {
|
|
508
|
+
let nesting = 0;
|
|
509
|
+
for (let i = 0; i < part.length; i += 1) {
|
|
510
|
+
const char = part[i];
|
|
511
|
+
if (part.startsWith("/*", i)) {
|
|
512
|
+
const end = part.indexOf("*/", i + 2);
|
|
513
|
+
if (end < 0)
|
|
514
|
+
break;
|
|
515
|
+
i = end + 1;
|
|
516
|
+
}
|
|
517
|
+
else if (char === '"') {
|
|
518
|
+
for (i += 1; i < part.length && part[i] !== '"'; i += 1) {
|
|
519
|
+
if (part[i] === "\\")
|
|
520
|
+
i += 1;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else if (char === "{" || char === "(" || char === "[" || char === "<") {
|
|
524
|
+
nesting += 1;
|
|
525
|
+
}
|
|
526
|
+
else if (char === "}" || char === ")" || char === "]" || char === ">") {
|
|
527
|
+
nesting -= 1;
|
|
528
|
+
}
|
|
529
|
+
else if (nesting === 0 &&
|
|
530
|
+
(part.startsWith(" | ", i) || part.startsWith(" & ", i))) {
|
|
531
|
+
return `(${part})`;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return part;
|
|
535
|
+
}
|
|
536
|
+
const TYPESCRIPT_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
537
|
+
function typescriptKey(key) {
|
|
538
|
+
return TYPESCRIPT_IDENTIFIER.test(key) ? key : JSON.stringify(key);
|
|
539
|
+
}
|
|
540
|
+
/** One-line JSDoc body; downstream prose can never close the comment early. */
|
|
541
|
+
function typescriptDoc(text) {
|
|
542
|
+
return text.replace(/\s+/g, " ").trim().replaceAll("*/", "*\\/");
|
|
543
|
+
}
|
|
544
|
+
function typescriptPrimitive(type) {
|
|
545
|
+
if (type === "integer")
|
|
546
|
+
return "number";
|
|
547
|
+
if (type === "array")
|
|
548
|
+
return "unknown[]";
|
|
549
|
+
if (type === "object")
|
|
550
|
+
return "Record<string, unknown>";
|
|
551
|
+
return ["string", "number", "boolean", "null"].includes(type)
|
|
552
|
+
? type
|
|
553
|
+
: "unknown";
|
|
554
|
+
}
|
|
555
|
+
function boundedParts(work, separator, prefix, suffix) {
|
|
556
|
+
// Check each addition before joining so a rejected schema never creates a
|
|
557
|
+
// large intermediate string just to discover that the final result is over.
|
|
558
|
+
const parts = [];
|
|
559
|
+
let bytes = schemaEncoder.encode(prefix).length + schemaEncoder.encode(suffix).length;
|
|
560
|
+
const separatorBytes = schemaEncoder.encode(separator).length;
|
|
561
|
+
return {
|
|
562
|
+
get length() {
|
|
563
|
+
return parts.length;
|
|
564
|
+
},
|
|
565
|
+
add(part) {
|
|
566
|
+
const partBytes = schemaEncoder.encode(part).length;
|
|
567
|
+
const added = partBytes + (parts.length > 0 ? separatorBytes : 0);
|
|
568
|
+
if (bytes + added > work.byteLimit)
|
|
569
|
+
throw schemaSizeExceeded;
|
|
570
|
+
parts.push(part);
|
|
571
|
+
bytes += added;
|
|
572
|
+
},
|
|
573
|
+
finish() {
|
|
574
|
+
return `${prefix}${parts.join(separator)}${suffix}`;
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
}
|
|
484
578
|
function renderSchema(schema, defs, seen, depth, options) {
|
|
485
579
|
options.work.visit();
|
|
486
580
|
return options.work.text(renderSchemaNode(schema, defs, seen, depth, options));
|
|
487
581
|
}
|
|
488
582
|
function renderSchemaNode(schema, defs, seen, depth, options) {
|
|
489
|
-
|
|
490
|
-
|
|
583
|
+
const ts = options.typescript === true;
|
|
584
|
+
const group = ts ? groupedType : grouped;
|
|
585
|
+
if (depth > 4) {
|
|
586
|
+
if (!ts)
|
|
587
|
+
return "…";
|
|
588
|
+
options.work.truncated = true;
|
|
589
|
+
return `unknown${COMPACT_DISCOVERY_TRUNCATION}`;
|
|
590
|
+
}
|
|
491
591
|
if (schema === null || typeof schema !== "object") {
|
|
592
|
+
if (ts)
|
|
593
|
+
return schema === false ? "never" : "unknown";
|
|
492
594
|
return options.work.json(schema);
|
|
493
595
|
}
|
|
494
596
|
const s = schema;
|
|
597
|
+
// OpenAPI 3.0's `nullable` has no JSON Schema meaning, so compact ignores
|
|
598
|
+
// it; a TypeScript reader writing `null` checks needs it in the type.
|
|
599
|
+
if (ts && s.nullable === true) {
|
|
600
|
+
const base = Object.create(null);
|
|
601
|
+
for (const key of propertyNames(s, options.work)) {
|
|
602
|
+
if (key !== "nullable")
|
|
603
|
+
base[key] = s[key];
|
|
604
|
+
}
|
|
605
|
+
const rendered = declaresShape(base)
|
|
606
|
+
? renderSchema(base, defs, seen, depth, options)
|
|
607
|
+
: "unknown";
|
|
608
|
+
return rendered === "unknown" ? rendered : `${rendered} | null`;
|
|
609
|
+
}
|
|
495
610
|
const constrain = (rendered) => options.renderConstraints
|
|
496
611
|
? renderConstraints(rendered, s, options.constraintByteLimit, options.onConstraintTruncated)
|
|
497
612
|
: rendered;
|
|
@@ -524,29 +639,41 @@ function renderSchemaNode(schema, defs, seen, depth, options) {
|
|
|
524
639
|
if (key !== "allOf")
|
|
525
640
|
own[key] = s[key];
|
|
526
641
|
}
|
|
527
|
-
const parts =
|
|
528
|
-
|
|
529
|
-
|
|
642
|
+
const parts = boundedParts(options.work, " & ", "", "");
|
|
643
|
+
const groupParts = declaresShape(own)
|
|
644
|
+
? s.allOf.length > 0
|
|
645
|
+
: s.allOf.length > 1;
|
|
646
|
+
if (declaresShape(own)) {
|
|
647
|
+
const rendered = renderSchema(own, defs, seen, depth, options);
|
|
648
|
+
parts.add(groupParts ? group(rendered) : rendered);
|
|
649
|
+
}
|
|
530
650
|
for (const member of s.allOf) {
|
|
531
|
-
|
|
651
|
+
const rendered = renderSchema(member, defs, seen, depth + 1, options);
|
|
652
|
+
parts.add(groupParts ? group(rendered) : rendered);
|
|
532
653
|
}
|
|
533
654
|
if (parts.length === 0)
|
|
534
655
|
return "unknown";
|
|
535
|
-
|
|
536
|
-
return parts[0];
|
|
537
|
-
return parts.map(grouped).join(" & ");
|
|
656
|
+
return parts.finish();
|
|
538
657
|
}
|
|
539
658
|
const reference = s.$ref ?? s.$dynamicRef;
|
|
540
659
|
if (typeof reference === "string") {
|
|
541
660
|
const dynamic = s.$ref === undefined;
|
|
542
661
|
const rawName = refName(options.work.text(reference));
|
|
543
662
|
const name = dynamic ? rawName.replace(/^#/, "") : rawName;
|
|
544
|
-
if (seen.has(name))
|
|
545
|
-
|
|
663
|
+
if (seen.has(name)) {
|
|
664
|
+
if (!ts)
|
|
665
|
+
return name;
|
|
666
|
+
// A cycle has no finite TypeScript expansion, and a bare definition
|
|
667
|
+
// name would be an undeclared type: degrade and send callers to JSON.
|
|
668
|
+
options.work.truncated = true;
|
|
669
|
+
return "unknown /* recursive */";
|
|
670
|
+
}
|
|
546
671
|
const target = resolveDefinition(defs, name);
|
|
547
672
|
if (target === undefined) {
|
|
548
|
-
if (dynamic)
|
|
673
|
+
if (dynamic || ts)
|
|
549
674
|
options.work.truncated = true;
|
|
675
|
+
if (ts)
|
|
676
|
+
return "unknown /* unresolved */";
|
|
550
677
|
return dynamic ? "unknown" : name;
|
|
551
678
|
}
|
|
552
679
|
const cacheKey = JSON.stringify([name, depth, [...seen]]);
|
|
@@ -561,10 +688,11 @@ function renderSchemaNode(schema, defs, seen, depth, options) {
|
|
|
561
688
|
}
|
|
562
689
|
const union = (s.oneOf ?? s.anyOf);
|
|
563
690
|
if (Array.isArray(union)) {
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
.
|
|
567
|
-
|
|
691
|
+
const parts = boundedParts(options.work, " | ", "", "");
|
|
692
|
+
for (const member of union) {
|
|
693
|
+
parts.add(renderSchema(member, defs, seen, depth + 1, options));
|
|
694
|
+
}
|
|
695
|
+
const rendered = parts.finish() || "unknown";
|
|
568
696
|
return constrain(rendered);
|
|
569
697
|
}
|
|
570
698
|
if (Array.isArray(s.enum)) {
|
|
@@ -580,21 +708,50 @@ function renderSchemaNode(schema, defs, seen, depth, options) {
|
|
|
580
708
|
return constrain(rendered);
|
|
581
709
|
}
|
|
582
710
|
const type = s.type;
|
|
711
|
+
// A type list renders member by member, so `["object", "null"]` beside
|
|
712
|
+
// `properties` keeps both its shape and its null rather than the object alone.
|
|
713
|
+
if (ts && Array.isArray(type) && type.length > 0) {
|
|
714
|
+
const members = new Set();
|
|
715
|
+
const parts = boundedParts(options.work, " | ", "", "");
|
|
716
|
+
for (const item of type) {
|
|
717
|
+
options.work.visit();
|
|
718
|
+
if (typeof item !== "string")
|
|
719
|
+
continue;
|
|
720
|
+
let rendered = "null";
|
|
721
|
+
if (item !== "null") {
|
|
722
|
+
const member = Object.create(null);
|
|
723
|
+
for (const key of propertyNames(s, options.work)) {
|
|
724
|
+
if (key !== "type")
|
|
725
|
+
member[key] = s[key];
|
|
726
|
+
}
|
|
727
|
+
member.type = item;
|
|
728
|
+
rendered = renderSchema(member, defs, seen, depth, options);
|
|
729
|
+
}
|
|
730
|
+
if (members.has(rendered))
|
|
731
|
+
continue;
|
|
732
|
+
members.add(rendered);
|
|
733
|
+
parts.add(rendered);
|
|
734
|
+
}
|
|
735
|
+
return parts.length === 0 ? "unknown" : parts.finish();
|
|
736
|
+
}
|
|
583
737
|
if (Array.isArray(s.prefixItems)) {
|
|
584
|
-
const parts =
|
|
738
|
+
const parts = boundedParts(options.work, ", ", "[", "]");
|
|
739
|
+
for (const item of s.prefixItems) {
|
|
740
|
+
parts.add(renderSchema(item, defs, seen, depth + 1, options));
|
|
741
|
+
}
|
|
585
742
|
if (s.items !== false) {
|
|
586
743
|
const rest = s.items === undefined || s.items === true
|
|
587
744
|
? "unknown"
|
|
588
745
|
: renderSchema(s.items, defs, seen, depth + 1, options);
|
|
589
|
-
parts.
|
|
746
|
+
parts.add(`...${group(rest)}[]`);
|
|
590
747
|
}
|
|
591
|
-
return
|
|
748
|
+
return parts.finish();
|
|
592
749
|
}
|
|
593
750
|
if (type === "array" || s.items) {
|
|
594
751
|
const items = s.items
|
|
595
752
|
? renderSchema(s.items, defs, seen, depth + 1, options)
|
|
596
753
|
: "unknown";
|
|
597
|
-
return `${items}[]`;
|
|
754
|
+
return ts ? `${groupedType(items)}[]` : `${items}[]`;
|
|
598
755
|
}
|
|
599
756
|
if (type === "object" || s.properties) {
|
|
600
757
|
const props = (s.properties ?? {});
|
|
@@ -606,10 +763,17 @@ function renderSchemaNode(schema, defs, seen, depth, options) {
|
|
|
606
763
|
...declaredKeys.filter((key) => !required.has(key)),
|
|
607
764
|
]
|
|
608
765
|
: declaredKeys;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
766
|
+
const additional = s.additionalProperties;
|
|
767
|
+
const index = ts && additional !== null && typeof additional === "object"
|
|
768
|
+
? renderSchema(additional, defs, seen, depth + 1, options)
|
|
769
|
+
: undefined;
|
|
770
|
+
if (keys.length === 0 && index === undefined) {
|
|
771
|
+
// In TypeScript `{}` reads as "empty"; an object with no declared
|
|
772
|
+
// properties is an open map unless it closes itself explicitly.
|
|
773
|
+
return ts && additional !== false ? "Record<string, unknown>" : "{}";
|
|
774
|
+
}
|
|
775
|
+
const parts = boundedParts(options.work, ts ? "; " : ", ", "{ ", " }");
|
|
776
|
+
for (const key of keys) {
|
|
613
777
|
const optional = required.has(key) ? "" : "?";
|
|
614
778
|
const rendered = renderSchema(props[key], defs, seen, depth + 1, options);
|
|
615
779
|
const description = props[key]?.description;
|
|
@@ -617,27 +781,40 @@ function renderSchemaNode(schema, defs, seen, depth, options) {
|
|
|
617
781
|
options.work.text(description);
|
|
618
782
|
}
|
|
619
783
|
options.work.text(key);
|
|
784
|
+
if (ts) {
|
|
785
|
+
const doc = options.propertyDescriptions && typeof description === "string" &&
|
|
786
|
+
description.trim()
|
|
787
|
+
? `/** ${typescriptDoc(description)} */ `
|
|
788
|
+
: "";
|
|
789
|
+
parts.add(`${doc}${typescriptKey(key)}${optional}: ${rendered}`);
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
620
792
|
const comment = options.propertyDescriptions && typeof description === "string"
|
|
621
793
|
? ` // ${description}`
|
|
622
794
|
: "";
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
|
|
795
|
+
parts.add(`${key}${optional}: ${rendered}${comment}`);
|
|
796
|
+
}
|
|
797
|
+
if (index !== undefined)
|
|
798
|
+
parts.add(`[key: string]: ${index}`);
|
|
799
|
+
return parts.finish();
|
|
626
800
|
}
|
|
627
801
|
if (typeof type === "string") {
|
|
628
|
-
|
|
802
|
+
const text = options.work.text(type);
|
|
803
|
+
return constrain(ts ? typescriptPrimitive(text) : text);
|
|
629
804
|
}
|
|
630
805
|
if (Array.isArray(type)) {
|
|
631
|
-
const
|
|
806
|
+
const parts = boundedParts(options.work, " | ", "", "");
|
|
807
|
+
for (const item of type) {
|
|
632
808
|
options.work.visit();
|
|
633
|
-
|
|
634
|
-
}
|
|
809
|
+
parts.add(options.work.text(String(item)));
|
|
810
|
+
}
|
|
811
|
+
const rendered = parts.finish();
|
|
635
812
|
return constrain(rendered);
|
|
636
813
|
}
|
|
637
814
|
if (options.renderConstraints && constraintEntries(s).length > 0) {
|
|
638
815
|
return renderConstraints("unknown", s, options.constraintByteLimit, options.onConstraintTruncated);
|
|
639
816
|
}
|
|
640
|
-
return options.work.json(schema);
|
|
817
|
+
return ts ? "unknown" : options.work.json(schema);
|
|
641
818
|
}
|
|
642
819
|
const compactSchemas = new WeakMap();
|
|
643
820
|
function resolveDefinition(schema, name) {
|
|
@@ -713,10 +890,58 @@ export function compactDiscoverySchema(schema) {
|
|
|
713
890
|
compactDiscoverySchemas.set(schema, result);
|
|
714
891
|
return result;
|
|
715
892
|
}
|
|
716
|
-
|
|
893
|
+
const typescriptDiscoveryTypes = new WeakMap();
|
|
894
|
+
const typescriptDescriptionTypes = new WeakMap();
|
|
895
|
+
/**
|
|
896
|
+
* One schema as a TypeScript type, under exactly the compact budgets: search
|
|
897
|
+
* drops prose and orders required keys first within 1,024 bytes, describe
|
|
898
|
+
* keeps prose as JSDoc within 8,192, and both share the 2,000-visit walk.
|
|
899
|
+
*/
|
|
900
|
+
function typescriptType(schema, description) {
|
|
901
|
+
const cache = description ? typescriptDescriptionTypes : typescriptDiscoveryTypes;
|
|
902
|
+
const cached = cache.get(schema);
|
|
903
|
+
if (cached)
|
|
904
|
+
return cached;
|
|
905
|
+
const result = boundedCompactSchema(schema, description, true);
|
|
906
|
+
cache.set(schema, result);
|
|
907
|
+
return result;
|
|
908
|
+
}
|
|
909
|
+
/** Marks runtime evidence inside the signature itself, not only beside it. */
|
|
910
|
+
const OBSERVED_OUTPUT_MARKER = "/* observed, not declared */ ";
|
|
911
|
+
/**
|
|
912
|
+
* Render a tool as the function `connecta.call(address, args)` resolves to,
|
|
913
|
+
* for an agent to read — never to execute: code mode still runs JavaScript.
|
|
914
|
+
*
|
|
915
|
+
* The input half comes from the declared input schema. The output half is the
|
|
916
|
+
* declared output schema, else a passively observed shape carrying a leading
|
|
917
|
+
* marker (runtime evidence is never presented as a contract), else `unknown`.
|
|
918
|
+
* A zero-field input renders as the optional `args?: {}` that call accepts:
|
|
919
|
+
* an MCP tool with no parameters rarely closes its schema, and reading its
|
|
920
|
+
* open `Record<string, unknown>` as "pass anything" would invite guessing.
|
|
921
|
+
*/
|
|
922
|
+
export function typescriptSignature(input, output, options) {
|
|
923
|
+
const renderedInput = typescriptType(input, options.description);
|
|
924
|
+
const renderedOutput = output
|
|
925
|
+
? typescriptType(output, options.description)
|
|
926
|
+
: undefined;
|
|
927
|
+
const args = renderedInput.text === "{}" ||
|
|
928
|
+
renderedInput.text === "Record<string, unknown>"
|
|
929
|
+
? "args?: {}"
|
|
930
|
+
: `args: ${renderedInput.text}`;
|
|
931
|
+
const result = renderedOutput
|
|
932
|
+
? `${options.observed ? OBSERVED_OUTPUT_MARKER : ""}${renderedOutput.text}`
|
|
933
|
+
: "unknown";
|
|
934
|
+
return {
|
|
935
|
+
text: `(${args}) => Promise<${result}>`,
|
|
936
|
+
inputTruncated: renderedInput.truncated,
|
|
937
|
+
outputTruncated: renderedOutput?.truncated === true,
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
function boundedCompactSchema(schema, description, typescript = false) {
|
|
717
941
|
const work = new SchemaWork(description ? MAX_COMPACT_DESCRIPTION_SCHEMA_BYTES : MAX_COMPACT_DISCOVERY_SCHEMA_BYTES);
|
|
718
942
|
const options = {
|
|
719
943
|
work,
|
|
944
|
+
typescript,
|
|
720
945
|
propertyDescriptions: description,
|
|
721
946
|
requiredFirst: !description,
|
|
722
947
|
enumByteLimit: description ? work.byteLimit : MAX_COMPACT_DISCOVERY_ENUM_BYTES,
|
package/dist/concurrency.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { runEdge } from "./runtime/run.js";
|
|
1
3
|
const DEFAULT_DISCOVERY_CONCURRENCY = 4;
|
|
2
4
|
export function resolveDiscoveryConcurrency(value) {
|
|
3
5
|
return value !== undefined && Number.isInteger(value) && value > 0
|
|
4
6
|
? value
|
|
5
7
|
: DEFAULT_DISCOVERY_CONCURRENCY;
|
|
6
8
|
}
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return settled;
|
|
9
|
+
/**
|
|
10
|
+
* Run `fn` over `items` with at most `limit` operations in flight, settling
|
|
11
|
+
* every one: a rejection lands in its slot instead of cancelling the rest.
|
|
12
|
+
* Results keep input order whatever order the work finishes in. A limit below
|
|
13
|
+
* one runs the items one at a time.
|
|
14
|
+
*/
|
|
15
|
+
export function mapSettledWithConcurrency(items, limit, fn) {
|
|
16
|
+
return runEdge(Effect.forEach(items, (item, index) => Effect.tryPromise({
|
|
17
|
+
// Promise.resolve keeps a non-promise return from `fn` working, as
|
|
18
|
+
// `await` did; a synchronous throw is caught either way.
|
|
19
|
+
try: () => Promise.resolve(fn(item, index)),
|
|
20
|
+
catch: (reason) => reason,
|
|
21
|
+
}).pipe(Effect.match({
|
|
22
|
+
onSuccess: (value) => ({
|
|
23
|
+
status: "fulfilled",
|
|
24
|
+
value,
|
|
25
|
+
}),
|
|
26
|
+
onFailure: (reason) => ({
|
|
27
|
+
status: "rejected",
|
|
28
|
+
reason,
|
|
29
|
+
}),
|
|
30
|
+
})), { concurrency: limit >= 1 ? Math.floor(limit) : 1 }));
|
|
30
31
|
}
|
package/dist/connector-scope.js
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const CONNECTOR_SCOPE_CLOSE_BUDGET_MS = 100;
|
|
4
|
-
/**
|
|
5
|
-
* Bound on cleanup continued after the caller-facing window expires.
|
|
6
|
-
*
|
|
7
|
-
* `remoteMcp` spends at most one second asking the downstream to terminate its
|
|
8
|
-
* session, leaving another second for the local close. Custom hooks still get a
|
|
9
|
-
* finite background window: handing a never-settling promise to a Worker's
|
|
10
|
-
* `waitUntil` would otherwise keep the invocation alive until the platform cap.
|
|
11
|
-
*/
|
|
12
|
-
const CONNECTOR_SCOPE_DEFER_BUDGET_MS = 2_000;
|
|
13
|
-
/** Resolve when `work` settles or `budgetMs` expires; never reject. */
|
|
14
|
-
function waitAtMost(work, budgetMs) {
|
|
15
|
-
return Promise.race([work, sleep(budgetMs)]).then(() => { });
|
|
16
|
-
}
|
|
1
|
+
import { closeScope } from "./runtime/connector-scope.js";
|
|
2
|
+
import { runEdge } from "./runtime/run.js";
|
|
17
3
|
/**
|
|
18
4
|
* Tell a connector that a scope owned by the core has ended.
|
|
19
5
|
*
|
|
@@ -25,25 +11,8 @@ function waitAtMost(work, budgetMs) {
|
|
|
25
11
|
* window. Callers own the at-most-once guarantee and must not use the scope
|
|
26
12
|
* again after this returns.
|
|
27
13
|
*/
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return;
|
|
33
|
-
// Attach both handlers before either timer can win, so a late rejection is
|
|
34
|
-
// consumed rather than becoming an unhandled rejection.
|
|
35
|
-
const settled = closing.then(() => { }, () => { });
|
|
36
|
-
if (defer) {
|
|
37
|
-
try {
|
|
38
|
-
defer(waitAtMost(settled, CONNECTOR_SCOPE_DEFER_BUDGET_MS));
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// A runtime hook is best-effort too; the caller cap still applies.
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
await waitAtMost(settled, CONNECTOR_SCOPE_CLOSE_BUDGET_MS);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// The scope is over whether or not the connector managed to clean it up.
|
|
48
|
-
}
|
|
14
|
+
export function closeConnectorScope(connector, ctx, defer) {
|
|
15
|
+
// The Promise face of closeScope (src/runtime/connector-scope.ts), which
|
|
16
|
+
// owns the budgets; an Effect caller registers it as a Scope finalizer.
|
|
17
|
+
return runEdge(closeScope(connector, ctx, defer));
|
|
49
18
|
}
|
package/dist/connectors/api.d.ts
CHANGED
|
@@ -52,9 +52,9 @@ export interface ApiOptions {
|
|
|
52
52
|
* discovery metadata. See `Connector.usageGuide`.
|
|
53
53
|
*/
|
|
54
54
|
usageGuide?: string | ConnectorUsageGuide;
|
|
55
|
-
/** Optional operator-managed credential exposed through ctx.credential and
|
|
55
|
+
/** Optional operator-managed credential exposed through ctx.credential and the connection in the operator UI. */
|
|
56
56
|
credential?: ConnectorCredentialConfig;
|
|
57
|
-
/** Optional validation behind
|
|
57
|
+
/** Optional validation behind the connection's Test action in the operator UI. */
|
|
58
58
|
testCredential?: (value: string, ctx: ConnectorContext) => Promise<CredentialTestResult>;
|
|
59
59
|
/** Optional validation for named multi-field credentials. */
|
|
60
60
|
testCredentials?: (values: ConnectorCredentialValues, ctx: ConnectorContext) => Promise<CredentialTestResult>;
|
|
@@ -2,8 +2,8 @@ import type { FetchLike, Transport } from "@modelcontextprotocol/client";
|
|
|
2
2
|
import { ConnectorCallError } from "../errors.js";
|
|
3
3
|
import type { Connector, ConnectorCallAdmissionPolicy, ConnectorContext, ConnectorCredentialConfig, ConnectorUsageGuide, Logger } from "../types.js";
|
|
4
4
|
/**
|
|
5
|
-
* A static downstream credential the
|
|
6
|
-
* rather than
|
|
5
|
+
* A static downstream credential supplied through the connection in the
|
|
6
|
+
* operator UI rather than baked into deployment source.
|
|
7
7
|
*
|
|
8
8
|
* The connector, its endpoint, and the credential *slot* stay declared in
|
|
9
9
|
* code; only the secret arrives through the operator route, exactly as for
|
|
@@ -15,8 +15,8 @@ import type { Connector, ConnectorCallAdmissionPolicy, ConnectorContext, Connect
|
|
|
15
15
|
interface RemoteMcpCredentialAuth {
|
|
16
16
|
type: "credential";
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* `{ label: "API key" }`; a maintained provider passes the name the provider
|
|
18
|
+
* Slot description rendered in the connection in the operator UI. Defaults
|
|
19
|
+
* to `{ label: "API key" }`; a maintained provider passes the name the provider
|
|
20
20
|
* itself uses. Named `fields` are refused — this shape reads the reserved
|
|
21
21
|
* `value` field only.
|
|
22
22
|
*/
|
|
@@ -122,7 +122,8 @@ export declare class RemoteMcpRedirectError extends ConnectorCallError {
|
|
|
122
122
|
* Wrap fetch with explicit, bounded redirect handling.
|
|
123
123
|
*
|
|
124
124
|
* The starting URL of each fetch call is trusted by its caller (the configured
|
|
125
|
-
* MCP endpoint, or an OAuth URL
|
|
125
|
+
* MCP endpoint, or an OAuth URL the pinned SDK discovered and
|
|
126
|
+
* `learnedUrlSafeFetch` already admitted). Only Location
|
|
126
127
|
* values are policy-controlled here. No rejected target is ever fetched, so
|
|
127
128
|
* arbitrary static header names receive the same protection as Authorization.
|
|
128
129
|
*/
|