@zackbart/connecta 0.8.0 → 0.9.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 +128 -0
- package/README.md +31 -55
- package/SECURITY.md +5 -11
- package/dist/activity.d.ts +0 -8
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +0 -1
- package/dist/activity.js.map +1 -1
- package/dist/auth/bearer.d.ts +1 -7
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +2 -23
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/clerk.d.ts +2 -11
- package/dist/auth/clerk.d.ts.map +1 -1
- package/dist/auth/clerk.js +31 -46
- package/dist/auth/clerk.js.map +1 -1
- package/dist/auth/downstream-oauth.d.ts +15 -6
- package/dist/auth/downstream-oauth.d.ts.map +1 -1
- package/dist/auth/downstream-oauth.js +60 -11
- package/dist/auth/downstream-oauth.js.map +1 -1
- package/dist/call-admission.d.ts.map +1 -1
- package/dist/call-admission.js +0 -1
- package/dist/call-admission.js.map +1 -1
- package/dist/catalog-service.d.ts +1 -2
- package/dist/catalog-service.d.ts.map +1 -1
- package/dist/catalog-service.js +12 -18
- package/dist/catalog-service.js.map +1 -1
- package/dist/catalog.d.ts +6 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +51 -0
- package/dist/catalog.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +1 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +73 -71
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/errors.d.ts +19 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/execute.d.ts +4 -9
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +41 -20
- package/dist/execute.js.map +1 -1
- package/dist/executor-result.js +1 -1
- package/dist/executor-result.js.map +1 -1
- package/dist/index.d.ts +3 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -90
- package/dist/index.js.map +1 -1
- package/dist/invocation.d.ts +5 -0
- package/dist/invocation.d.ts.map +1 -1
- package/dist/invocation.js +46 -10
- package/dist/invocation.js.map +1 -1
- package/dist/meta-tools.d.ts +4 -12
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +119 -169
- package/dist/meta-tools.js.map +1 -1
- package/dist/operator-ui/generated.d.ts +2 -2
- package/dist/operator-ui/generated.d.ts.map +1 -1
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/generated.js.map +1 -1
- package/dist/operator-ui/model.d.ts +1 -22
- package/dist/operator-ui/model.d.ts.map +1 -1
- package/dist/operator-ui/model.js.map +1 -1
- package/dist/registry.d.ts +11 -178
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +52 -330
- package/dist/registry.js.map +1 -1
- package/dist/retired-toolkits.d.ts +3 -0
- package/dist/retired-toolkits.d.ts.map +1 -0
- package/dist/retired-toolkits.js +9 -0
- package/dist/retired-toolkits.js.map +1 -0
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/activity.js +2 -5
- package/dist/routes/activity.js.map +1 -1
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/credentials.js +1 -12
- package/dist/routes/credentials.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +94 -188
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/oauth.d.ts.map +1 -1
- package/dist/routes/oauth.js +3 -8
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/shared.d.ts +9 -30
- package/dist/routes/shared.d.ts.map +1 -1
- package/dist/routes/shared.js +10 -69
- package/dist/routes/shared.js.map +1 -1
- package/dist/routes/ui.d.ts.map +1 -1
- package/dist/routes/ui.js +4 -10
- package/dist/routes/ui.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -31
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +3 -6
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +9 -38
- package/dist/skills.js.map +1 -1
- package/dist/timeout.d.ts.map +1 -1
- package/dist/timeout.js +2 -4
- package/dist/timeout.js.map +1 -1
- package/dist/types.d.ts +8 -61
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +1 -2
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +5 -35
- package/dist/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/src/activity.ts +0 -9
- package/src/auth/bearer.ts +3 -32
- package/src/auth/clerk.ts +35 -48
- package/src/auth/downstream-oauth.ts +106 -24
- package/src/call-admission.ts +0 -1
- package/src/catalog-service.ts +23 -23
- package/src/catalog.ts +55 -0
- package/src/connectors/remote-mcp.ts +106 -86
- package/src/errors.ts +22 -0
- package/src/execute.ts +43 -24
- package/src/executor-result.ts +1 -1
- package/src/index.ts +24 -208
- package/src/invocation.ts +63 -10
- package/src/meta-tools.ts +139 -187
- package/src/operator-ui/browser.css +1 -54
- package/src/operator-ui/browser.ts +16 -150
- package/src/operator-ui/generated.ts +2 -2
- package/src/operator-ui/model.ts +1 -23
- package/src/registry.ts +59 -429
- package/src/retired-toolkits.ts +14 -0
- package/src/routes/activity.ts +1 -6
- package/src/routes/credentials.ts +0 -12
- package/src/routes/mcp.ts +110 -230
- package/src/routes/oauth.ts +2 -8
- package/src/routes/shared.ts +10 -100
- package/src/routes/ui.ts +2 -12
- package/src/server.ts +1 -31
- package/src/skills.ts +9 -38
- package/src/timeout.ts +2 -4
- package/src/types.ts +11 -62
- package/src/ui.ts +9 -46
- package/src/version.ts +1 -1
- package/dist/credential-health.d.ts +0 -205
- package/dist/credential-health.d.ts.map +0 -1
- package/dist/credential-health.js +0 -610
- package/dist/credential-health.js.map +0 -1
- package/dist/toolkits.d.ts +0 -124
- package/dist/toolkits.d.ts.map +0 -1
- package/dist/toolkits.js +0 -322
- package/dist/toolkits.js.map +0 -1
- package/src/credential-health.ts +0 -835
- package/src/toolkits.ts +0 -461
package/src/meta-tools.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import type {
|
|
4
4
|
ActivityCallSource,
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import {
|
|
8
8
|
boundedDiscoveryText,
|
|
9
9
|
CatalogService,
|
|
10
|
+
DEFAULT_SEARCH_LIMIT,
|
|
10
11
|
DiscoveryPolicyError,
|
|
11
12
|
groupedSearchResult,
|
|
12
13
|
MAX_DESCRIBE_ADDRESSES,
|
|
@@ -48,7 +49,6 @@ import {
|
|
|
48
49
|
normalizeTimeoutMs,
|
|
49
50
|
withAbortableTimeout,
|
|
50
51
|
} from "./timeout.js";
|
|
51
|
-
import { credentialVerdictApplies } from "./credential-health.js";
|
|
52
52
|
import type { ConnectorStatus, KVStorage } from "./types.js";
|
|
53
53
|
|
|
54
54
|
export {
|
|
@@ -56,7 +56,6 @@ export {
|
|
|
56
56
|
MAX_DISCOVERY_RESULT_BYTES,
|
|
57
57
|
MAX_RETRY_BACKOFF_MS,
|
|
58
58
|
MAX_SEARCH_LIMIT,
|
|
59
|
-
DiscoveryPolicyError,
|
|
60
59
|
retryBackoffMs,
|
|
61
60
|
};
|
|
62
61
|
|
|
@@ -77,7 +76,7 @@ const dec = new TextDecoder();
|
|
|
77
76
|
|
|
78
77
|
export function jsonResult(obj: unknown): ToolResult {
|
|
79
78
|
return {
|
|
80
|
-
content: [{ type: "text", text: JSON.stringify(obj
|
|
79
|
+
content: [{ type: "text", text: JSON.stringify(obj) }],
|
|
81
80
|
...(obj !== null && typeof obj === "object" && !Array.isArray(obj)
|
|
82
81
|
? { structuredContent: obj as Record<string, unknown> }
|
|
83
82
|
: {}),
|
|
@@ -108,8 +107,12 @@ function discoveryErrorResult(error: DiscoveryPolicyError): ToolResult {
|
|
|
108
107
|
return result;
|
|
109
108
|
}
|
|
110
109
|
|
|
111
|
-
function discoveryResult(
|
|
110
|
+
async function discoveryResult(
|
|
111
|
+
operation: () => unknown | Promise<unknown>,
|
|
112
|
+
hint: string,
|
|
113
|
+
): Promise<ToolResult> {
|
|
112
114
|
try {
|
|
115
|
+
const value = await operation();
|
|
113
116
|
const text = boundedDiscoveryText(value, hint);
|
|
114
117
|
return {
|
|
115
118
|
content: [{ type: "text", text }],
|
|
@@ -255,7 +258,7 @@ function applyFieldsToContent(
|
|
|
255
258
|
} catch {
|
|
256
259
|
return b;
|
|
257
260
|
}
|
|
258
|
-
return { ...b, text: JSON.stringify(applyFields(parsed, fields)
|
|
261
|
+
return { ...b, text: JSON.stringify(applyFields(parsed, fields)) };
|
|
259
262
|
});
|
|
260
263
|
}
|
|
261
264
|
|
|
@@ -277,7 +280,7 @@ function applyFieldsToContent(
|
|
|
277
280
|
* at all (a BigInt) still throws, as before, and is reported as a failure.
|
|
278
281
|
*/
|
|
279
282
|
function serializeResultText(value: unknown): string {
|
|
280
|
-
const serialized = JSON.stringify(value
|
|
283
|
+
const serialized = JSON.stringify(value);
|
|
281
284
|
return serialized === undefined ? String(value) : serialized;
|
|
282
285
|
}
|
|
283
286
|
|
|
@@ -391,7 +394,7 @@ async function guardContent(
|
|
|
391
394
|
): Promise<ToolResult> {
|
|
392
395
|
let text: string;
|
|
393
396
|
try {
|
|
394
|
-
text = JSON.stringify(content
|
|
397
|
+
text = JSON.stringify(content);
|
|
395
398
|
} catch {
|
|
396
399
|
// A block carrying a BigInt or a cycle cannot be serialized, so it cannot
|
|
397
400
|
// be measured, stashed, or paged either — there is nothing this guard could
|
|
@@ -453,15 +456,7 @@ export interface GetResultArgs {
|
|
|
453
456
|
/** Page size in bytes; a whole number >= 1. Defaults to the deployment cap. */
|
|
454
457
|
maxBytes?: number;
|
|
455
458
|
}
|
|
456
|
-
export
|
|
457
|
-
address: string;
|
|
458
|
-
args?: Record<string, unknown>;
|
|
459
|
-
fields?: string[];
|
|
460
|
-
resultMode?: ResultMode;
|
|
461
|
-
timeoutMs?: number;
|
|
462
|
-
maxRetries?: number;
|
|
463
|
-
diagnostics?: boolean;
|
|
464
|
-
}
|
|
459
|
+
export type BatchCall = CallArgs;
|
|
465
460
|
export interface BatchArgs {
|
|
466
461
|
calls: BatchCall[];
|
|
467
462
|
resultMode?: ResultMode;
|
|
@@ -614,17 +609,26 @@ export function createMetaTools(
|
|
|
614
609
|
},
|
|
615
610
|
);
|
|
616
611
|
if (!outcome.ok) {
|
|
612
|
+
const failedResult =
|
|
613
|
+
outcome.error.code === "auth_required" ||
|
|
614
|
+
outcome.error.code === "input_required_unsupported" ||
|
|
615
|
+
call.resultMode === "value"
|
|
616
|
+
? jsonResult({
|
|
617
|
+
ok: false,
|
|
618
|
+
error: outcome.error,
|
|
619
|
+
durationMs: outcome.durationMs,
|
|
620
|
+
attempts: outcome.attempts,
|
|
621
|
+
...(call.diagnostics ? { timing: outcome.timing } : {}),
|
|
622
|
+
})
|
|
623
|
+
: errorResult(outcome.error.message);
|
|
624
|
+
if (
|
|
625
|
+
outcome.error.code === "auth_required" ||
|
|
626
|
+
outcome.error.code === "input_required_unsupported"
|
|
627
|
+
) {
|
|
628
|
+
failedResult.isError = true;
|
|
629
|
+
}
|
|
617
630
|
return {
|
|
618
|
-
toolResult:
|
|
619
|
-
call.resultMode === "value"
|
|
620
|
-
? jsonResult({
|
|
621
|
-
ok: false,
|
|
622
|
-
error: outcome.error,
|
|
623
|
-
durationMs: outcome.durationMs,
|
|
624
|
-
attempts: outcome.attempts,
|
|
625
|
-
...(call.diagnostics ? { timing: outcome.timing } : {}),
|
|
626
|
-
})
|
|
627
|
-
: errorResult(outcome.error.message),
|
|
631
|
+
toolResult: failedResult,
|
|
628
632
|
durationMs: outcome.durationMs,
|
|
629
633
|
attempts: outcome.attempts,
|
|
630
634
|
timing: outcome.timing,
|
|
@@ -683,11 +687,13 @@ export function createMetaTools(
|
|
|
683
687
|
const inspect = async (c: (typeof connectors)[number]) => {
|
|
684
688
|
const statusStarted = Date.now();
|
|
685
689
|
const observed = registry.healthFor(c.id);
|
|
686
|
-
const
|
|
690
|
+
const drift = await registry.credentialDriftFor(c.id);
|
|
687
691
|
let status:
|
|
688
692
|
| ConnectorStatus
|
|
689
693
|
| { state: "ok" | "error" | "unknown"; message?: string };
|
|
690
|
-
if (
|
|
694
|
+
if (drift) {
|
|
695
|
+
status = { state: "auth_required", message: drift };
|
|
696
|
+
} else if (probe) {
|
|
691
697
|
try {
|
|
692
698
|
status = await withProbeDeadline(
|
|
693
699
|
`list_connectors probe of "${c.id}"`,
|
|
@@ -700,32 +706,7 @@ export function createMetaTools(
|
|
|
700
706
|
// hanging the whole list_connectors call.
|
|
701
707
|
status = { state: "error", message: msg(err) };
|
|
702
708
|
}
|
|
703
|
-
} else if (
|
|
704
|
-
verdict &&
|
|
705
|
-
// Deployment-wide, deliberately, like `hasObservedSuccess` beside
|
|
706
|
-
// it: a sibling toolkit's successful call proves the shared
|
|
707
|
-
// credential works, and a verdict retired for one view but not
|
|
708
|
-
// another would make the same connector read differently per scope
|
|
709
|
-
// for a reason that has nothing to do with scope.
|
|
710
|
-
credentialVerdictApplies(verdict, registry.observedSuccessAt(c.id))
|
|
711
|
-
) {
|
|
712
|
-
// The proactive layer (issue #24): a liveness check already found
|
|
713
|
-
// the stored credential dead, so say so on the cheap path instead of
|
|
714
|
-
// waiting for an agent's real call to discover it. Only while it is
|
|
715
|
-
// the freshest evidence — a successful call since then retires it.
|
|
716
|
-
status = {
|
|
717
|
-
state: verdict.state,
|
|
718
|
-
...(verdict.message ? { message: verdict.message } : {}),
|
|
719
|
-
...(verdict.authorizationUrl
|
|
720
|
-
? { authorizationUrl: verdict.authorizationUrl }
|
|
721
|
-
: {}),
|
|
722
|
-
};
|
|
723
709
|
} else {
|
|
724
|
-
// "error" comes from THIS view's own observations — a sibling
|
|
725
|
-
// toolkit's failure is not this session's experience — while
|
|
726
|
-
// ok/unknown may lean on the deployment-wide success signal, since
|
|
727
|
-
// "the connector answers at all" is a fact about the connector.
|
|
728
|
-
// Unscoped, the two are the same log, so this is unchanged there.
|
|
729
710
|
const derived =
|
|
730
711
|
observed?.consecutiveFailures && observed.consecutiveFailures > 0
|
|
731
712
|
? ("error" as const)
|
|
@@ -733,43 +714,13 @@ export function createMetaTools(
|
|
|
733
714
|
? ("ok" as const)
|
|
734
715
|
: ("unknown" as const);
|
|
735
716
|
status = {
|
|
736
|
-
|
|
737
|
-
// been called yet, but the credential was verified, which is how
|
|
738
|
-
// re-authorization shows up here as ok rather than as an absence
|
|
739
|
-
// of evidence. It never DOWNgrades an observed failure: a real
|
|
740
|
-
// call that failed is stronger evidence than a background check.
|
|
741
|
-
state:
|
|
742
|
-
derived === "unknown" && verdict?.state === "ok"
|
|
743
|
-
? ("ok" as const)
|
|
744
|
-
: derived,
|
|
717
|
+
state: derived,
|
|
745
718
|
...(observed?.lastError ? { message: observed.lastError } : {}),
|
|
746
719
|
};
|
|
747
720
|
}
|
|
748
|
-
// Stamped
|
|
749
|
-
//
|
|
750
|
-
// report a verdict older than it is, and would lose the race against a
|
|
751
|
-
// real call that succeeded WHILE it ran (that success must retire the
|
|
752
|
-
// verdict, and only an honest timestamp says so).
|
|
721
|
+
// Stamped after any live probe so the response reports when its
|
|
722
|
+
// observation completed, not when a potentially slow request began.
|
|
753
723
|
const checkedAt = new Date().toISOString();
|
|
754
|
-
// A live status probe IS a liveness observation of the stored
|
|
755
|
-
// credential, so it updates the same verdict a background check
|
|
756
|
-
// writes: the cached read afterwards agrees with what the operator
|
|
757
|
-
// just saw, and they are not swept again moments later. Recorded from
|
|
758
|
-
// the STATUS phase only, and only when the connector actually answered
|
|
759
|
-
// — a catalog refresh below is not a credential check (the sweep never
|
|
760
|
-
// fetches one), it is already counted in the health log, and letting
|
|
761
|
-
// its failure land here would spend the freshness budget on it. The
|
|
762
|
-
// registry ignores this for connectors storing no credential of ours.
|
|
763
|
-
if (probe && (status.state === "ok" || status.state === "auth_required")) {
|
|
764
|
-
await registry.recordCredentialHealth(c.id, {
|
|
765
|
-
state: status.state,
|
|
766
|
-
checkedAt,
|
|
767
|
-
...(status.message ? { message: status.message } : {}),
|
|
768
|
-
...("authorizationUrl" in status && status.authorizationUrl
|
|
769
|
-
? { authorizationUrl: status.authorizationUrl }
|
|
770
|
-
: {}),
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
724
|
let tools = registry.peekTools(c.id);
|
|
774
725
|
// An auth_required status may have just started OAuth. A second
|
|
775
726
|
// listTools probe would overwrite its state/verifier while returning
|
|
@@ -803,15 +754,6 @@ export function createMetaTools(
|
|
|
803
754
|
state: "auth_required" as const,
|
|
804
755
|
message: details.message,
|
|
805
756
|
};
|
|
806
|
-
await registry.recordCredentialHealth(c.id, {
|
|
807
|
-
state: "auth_required",
|
|
808
|
-
checkedAt,
|
|
809
|
-
...(status.message && { message: status.message }),
|
|
810
|
-
...("authorizationUrl" in status &&
|
|
811
|
-
status.authorizationUrl
|
|
812
|
-
? { authorizationUrl: status.authorizationUrl }
|
|
813
|
-
: {}),
|
|
814
|
-
});
|
|
815
757
|
} else {
|
|
816
758
|
status = { state: "error" as const, message: msg(err) };
|
|
817
759
|
}
|
|
@@ -820,9 +762,6 @@ export function createMetaTools(
|
|
|
820
762
|
}
|
|
821
763
|
const latencyMs = Date.now() - statusStarted;
|
|
822
764
|
const latestObserved = registry.healthFor(c.id);
|
|
823
|
-
const credentialCheck = probe
|
|
824
|
-
? await registry.credentialHealthFor(c.id)
|
|
825
|
-
: verdict;
|
|
826
765
|
return {
|
|
827
766
|
id: c.id,
|
|
828
767
|
...(c.title ? { title: c.title } : {}),
|
|
@@ -833,7 +772,6 @@ export function createMetaTools(
|
|
|
833
772
|
latencyMs,
|
|
834
773
|
probe,
|
|
835
774
|
...(latestObserved ?? observed),
|
|
836
|
-
...(credentialCheck ? { credentialCheck } : {}),
|
|
837
775
|
...("authorizationUrl" in status &&
|
|
838
776
|
status.authorizationUrl && {
|
|
839
777
|
authorizationUrl: status.authorizationUrl,
|
|
@@ -866,31 +804,18 @@ export function createMetaTools(
|
|
|
866
804
|
},
|
|
867
805
|
|
|
868
806
|
async searchTools(args: SearchArgs): Promise<ToolResult> {
|
|
869
|
-
|
|
870
|
-
|
|
807
|
+
return discoveryResult(
|
|
808
|
+
async () =>
|
|
871
809
|
groupedSearchResult(await catalog.search(args)),
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
} catch (err) {
|
|
875
|
-
if (err instanceof DiscoveryPolicyError) {
|
|
876
|
-
return discoveryErrorResult(err);
|
|
877
|
-
}
|
|
878
|
-
throw err;
|
|
879
|
-
}
|
|
810
|
+
"Request a smaller limit, omit fullDescriptions, or use compact schemas.",
|
|
811
|
+
);
|
|
880
812
|
},
|
|
881
813
|
|
|
882
814
|
async describeTools(args: DescribeArgs): Promise<ToolResult> {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
);
|
|
888
|
-
} catch (err) {
|
|
889
|
-
if (err instanceof DiscoveryPolicyError) {
|
|
890
|
-
return discoveryErrorResult(err);
|
|
891
|
-
}
|
|
892
|
-
throw err;
|
|
893
|
-
}
|
|
815
|
+
return discoveryResult(
|
|
816
|
+
async () => ({ tools: await catalog.describe(args) }),
|
|
817
|
+
'Split the address list or use format: "compact".',
|
|
818
|
+
);
|
|
894
819
|
},
|
|
895
820
|
|
|
896
821
|
async callTool(args: CallArgs): Promise<ToolResult> {
|
|
@@ -1076,6 +1001,21 @@ export function createMetaTools(
|
|
|
1076
1001
|
...(details.retryAfterMs !== undefined
|
|
1077
1002
|
? { retryAfterMs: details.retryAfterMs }
|
|
1078
1003
|
: {}),
|
|
1004
|
+
...(details.connector !== undefined
|
|
1005
|
+
? { connector: batchSummaryString(details.connector) }
|
|
1006
|
+
: {}),
|
|
1007
|
+
...(details.operation !== undefined
|
|
1008
|
+
? { operation: batchSummaryString(details.operation) }
|
|
1009
|
+
: {}),
|
|
1010
|
+
...(details.recovery !== undefined
|
|
1011
|
+
? { recovery: details.recovery }
|
|
1012
|
+
: {}),
|
|
1013
|
+
...(details.nextAction !== undefined
|
|
1014
|
+
? { nextAction: details.nextAction }
|
|
1015
|
+
: {}),
|
|
1016
|
+
...(details.retry !== undefined
|
|
1017
|
+
? { retry: batchSummaryString(details.retry) }
|
|
1018
|
+
: {}),
|
|
1079
1019
|
},
|
|
1080
1020
|
};
|
|
1081
1021
|
}),
|
|
@@ -1090,9 +1030,55 @@ export function createMetaTools(
|
|
|
1090
1030
|
return errorResult(`Unknown connector "${args.connector}"`);
|
|
1091
1031
|
}
|
|
1092
1032
|
if (!connector.startAuth) {
|
|
1093
|
-
|
|
1094
|
-
|
|
1033
|
+
if (!connector.credential) {
|
|
1034
|
+
return jsonResult({
|
|
1035
|
+
connector: connector.id,
|
|
1036
|
+
recovery: "unavailable",
|
|
1037
|
+
message:
|
|
1038
|
+
`Connector "${connector.id}" declares neither downstream OAuth ` +
|
|
1039
|
+
"nor an operator-managed credential slot. Update the connector " +
|
|
1040
|
+
"or deployment configuration before retrying.",
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
const ctx = registry.contextFor(
|
|
1044
|
+
connector.id,
|
|
1045
|
+
baseUrl,
|
|
1046
|
+
requestScope,
|
|
1095
1047
|
);
|
|
1048
|
+
if (!ctx.credential) {
|
|
1049
|
+
return jsonResult({
|
|
1050
|
+
connector: connector.id,
|
|
1051
|
+
recovery: "unavailable",
|
|
1052
|
+
message:
|
|
1053
|
+
"Credential storage is not configured. Configure " +
|
|
1054
|
+
"credentials.encryptionKey, redeploy, then call " +
|
|
1055
|
+
"authorize_connector again.",
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
const fields = connector.credential.fields?.map((field) => ({
|
|
1059
|
+
name: field.name,
|
|
1060
|
+
guidance: field.description ?? field.label,
|
|
1061
|
+
})) ?? [
|
|
1062
|
+
{
|
|
1063
|
+
name: "value",
|
|
1064
|
+
guidance:
|
|
1065
|
+
connector.credential.description ??
|
|
1066
|
+
connector.credential.label,
|
|
1067
|
+
},
|
|
1068
|
+
];
|
|
1069
|
+
return jsonResult({
|
|
1070
|
+
connector: connector.id,
|
|
1071
|
+
recovery: "operator_config",
|
|
1072
|
+
credential: {
|
|
1073
|
+
label: connector.credential.label,
|
|
1074
|
+
fields,
|
|
1075
|
+
},
|
|
1076
|
+
operatorUrl: new URL("/credentials", baseUrl).toString(),
|
|
1077
|
+
instructions:
|
|
1078
|
+
"Have the operator open operatorUrl, set and test the credential, " +
|
|
1079
|
+
"then retry the original call. No redeploy is needed. Credential " +
|
|
1080
|
+
"mutation requires a Clerk-authenticated operator.",
|
|
1081
|
+
});
|
|
1096
1082
|
}
|
|
1097
1083
|
const ctx = registry.contextFor(connector.id, baseUrl, requestScope);
|
|
1098
1084
|
try {
|
|
@@ -1100,17 +1086,6 @@ export function createMetaTools(
|
|
|
1100
1086
|
ctx,
|
|
1101
1087
|
args.force !== undefined ? { force: args.force } : {},
|
|
1102
1088
|
);
|
|
1103
|
-
// startAuth just spoke to the downstream about this exact credential, so
|
|
1104
|
-
// its answer replaces any older liveness verdict — including the stale
|
|
1105
|
-
// `auth_required` that sent the agent here, once it reports ok.
|
|
1106
|
-
await registry.recordCredentialHealth(connector.id, {
|
|
1107
|
-
state: status.state,
|
|
1108
|
-
checkedAt: new Date().toISOString(),
|
|
1109
|
-
...(status.message ? { message: status.message } : {}),
|
|
1110
|
-
...(status.authorizationUrl
|
|
1111
|
-
? { authorizationUrl: status.authorizationUrl }
|
|
1112
|
-
: {}),
|
|
1113
|
-
});
|
|
1114
1089
|
if (status.state === "auth_required" && !status.authorizationUrl) {
|
|
1115
1090
|
// auth_required with nothing to open is a dead end for the operator.
|
|
1116
1091
|
return errorResult(
|
|
@@ -1119,6 +1094,7 @@ export function createMetaTools(
|
|
|
1119
1094
|
}
|
|
1120
1095
|
return jsonResult({
|
|
1121
1096
|
connector: connector.id,
|
|
1097
|
+
recovery: "oauth",
|
|
1122
1098
|
status: status.state,
|
|
1123
1099
|
...(status.authorizationUrl
|
|
1124
1100
|
? {
|
|
@@ -1142,7 +1118,7 @@ export function createMetaTools(
|
|
|
1142
1118
|
|
|
1143
1119
|
const LIST_DESC =
|
|
1144
1120
|
"List connectors with status, cached tool count, and recent real-call health. Use probe=false for a fast inventory; use probe=true (default) only to diagnose live health or authorization.";
|
|
1145
|
-
const SEARCH_DESC = `Start here when a tool address is unknown. Exact/name matches rank above description matches; an empty query browses all.
|
|
1121
|
+
const SEARCH_DESC = `Start here when a tool address is unknown. Exact/name matches rank above description matches; an empty query browses all. The default page has ${DEFAULT_SEARCH_LIMIT} tools; explicit limit can request up to ${MAX_SEARCH_LIMIT}. includeSchemas="compact" usually removes the describe_tools round trip.`;
|
|
1146
1122
|
const DESCRIBE_DESC = `Inspect up to ${MAX_DESCRIBE_ADDRESSES} known tool addresses when search_tools did not include a sufficient schema. Returns descriptions, input/output schemas, and behavior annotations; format "compact" is the default.`;
|
|
1147
1123
|
const CALL_DESC =
|
|
1148
1124
|
'Use for one tool explicitly annotated readOnlyHint: true. For 2–10 independent read-only calls use batch_call; for dependent steps or data reduction use execute_code when available. Unannotated, write-capable, and destructive tools are refused and require call_destructive_tool. fields selects JSON dot-paths, resultMode "value" unwraps results, timeoutMs sets a deadline, safe maxRetries are annotation-gated, diagnostics adds timing, and large results page through get_result.';
|
|
@@ -1153,7 +1129,7 @@ const GET_RESULT_DESC =
|
|
|
1153
1129
|
const BATCH_DESC =
|
|
1154
1130
|
"Use for 2–10 independent tools explicitly annotated readOnlyHint: true. Calls run in parallel with shared request-scoped clients; use execute_code when available instead for dependencies or in-sandbox reduction. Unannotated, write-capable, and destructive tools are refused. Batch timeout, safe retry, result mode, and diagnostics defaults may be overridden per call. An oversized final envelope returns ordered outcome summaries plus a get_result page handle.";
|
|
1155
1131
|
const AUTHORIZE_DESC =
|
|
1156
|
-
"Use after
|
|
1132
|
+
"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.";
|
|
1157
1133
|
const SKILLS_DESC =
|
|
1158
1134
|
'List or fetch concise guidance for choosing among Connecta meta-tools. Call skills({ name: "usage" }) once when the routing workflow is unfamiliar; do not refetch it in the same task.';
|
|
1159
1135
|
|
|
@@ -1163,10 +1139,7 @@ const SKILLS_DESC =
|
|
|
1163
1139
|
* so a deployment with no guides gets every base description unchanged rather
|
|
1164
1140
|
* than paying for text about a feature it does not use.
|
|
1165
1141
|
*
|
|
1166
|
-
* Registration is per connection and reads the
|
|
1167
|
-
* so under a toolkit these sentences reflect the SCOPED connector set: a scoped
|
|
1168
|
-
* session whose connectors carry no guides sees the base descriptions, and
|
|
1169
|
-
* never learns from a tool description that guides exist out of scope.
|
|
1142
|
+
* Registration is per connection and reads the configured connector set.
|
|
1170
1143
|
*/
|
|
1171
1144
|
const GUIDE_NOTES = {
|
|
1172
1145
|
skills:
|
|
@@ -1208,6 +1181,16 @@ const READ_ONLY_LOCAL = {
|
|
|
1208
1181
|
openWorldHint: false,
|
|
1209
1182
|
} as const;
|
|
1210
1183
|
|
|
1184
|
+
const CALL_INPUT_SCHEMA = {
|
|
1185
|
+
address: z.string(),
|
|
1186
|
+
args: z.record(z.string(), z.unknown()).optional(),
|
|
1187
|
+
fields: z.array(z.string()).optional(),
|
|
1188
|
+
resultMode: z.enum(["mcp", "value"]).optional(),
|
|
1189
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
1190
|
+
maxRetries: z.number().int().min(0).max(2).optional(),
|
|
1191
|
+
diagnostics: z.boolean().optional(),
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1211
1194
|
/** Register the nine meta-tools onto an McpServer instance. */
|
|
1212
1195
|
export function registerMetaTools(
|
|
1213
1196
|
server: McpServer,
|
|
@@ -1243,13 +1226,8 @@ export function registerMetaTools(
|
|
|
1243
1226
|
"skills",
|
|
1244
1227
|
{
|
|
1245
1228
|
description: describedFor(registry, SKILLS_DESC, "skills"),
|
|
1246
|
-
inputSchema: { name: z.string().optional() },
|
|
1247
|
-
annotations:
|
|
1248
|
-
readOnlyHint: true,
|
|
1249
|
-
destructiveHint: false,
|
|
1250
|
-
idempotentHint: true,
|
|
1251
|
-
openWorldHint: false,
|
|
1252
|
-
},
|
|
1229
|
+
inputSchema: z.object({ name: z.string().optional() }),
|
|
1230
|
+
annotations: READ_ONLY_LOCAL,
|
|
1253
1231
|
},
|
|
1254
1232
|
async (args) => mt.skills(args as SkillArgs),
|
|
1255
1233
|
);
|
|
@@ -1258,7 +1236,7 @@ export function registerMetaTools(
|
|
|
1258
1236
|
"list_connectors",
|
|
1259
1237
|
{
|
|
1260
1238
|
description: LIST_DESC,
|
|
1261
|
-
inputSchema: { probe: z.boolean().optional() },
|
|
1239
|
+
inputSchema: z.object({ probe: z.boolean().optional() }),
|
|
1262
1240
|
annotations: READ_ONLY_REMOTE,
|
|
1263
1241
|
},
|
|
1264
1242
|
async (args) => mt.listConnectors(args as ListArgs),
|
|
@@ -1268,14 +1246,14 @@ export function registerMetaTools(
|
|
|
1268
1246
|
"search_tools",
|
|
1269
1247
|
{
|
|
1270
1248
|
description: describedFor(registry, SEARCH_DESC, "search"),
|
|
1271
|
-
inputSchema: {
|
|
1249
|
+
inputSchema: z.object({
|
|
1272
1250
|
query: z.string().optional(),
|
|
1273
1251
|
connector: z.string().optional(),
|
|
1274
1252
|
limit: z.number().int().positive().max(MAX_SEARCH_LIMIT).optional(),
|
|
1275
1253
|
offset: z.number().int().nonnegative().optional(),
|
|
1276
1254
|
fullDescriptions: z.boolean().optional(),
|
|
1277
1255
|
includeSchemas: z.enum(["compact", "json"]).optional(),
|
|
1278
|
-
},
|
|
1256
|
+
}),
|
|
1279
1257
|
annotations: READ_ONLY_REMOTE,
|
|
1280
1258
|
},
|
|
1281
1259
|
async (args) => mt.searchTools(args as SearchArgs),
|
|
@@ -1285,11 +1263,11 @@ export function registerMetaTools(
|
|
|
1285
1263
|
"describe_tools",
|
|
1286
1264
|
{
|
|
1287
1265
|
description: describedFor(registry, DESCRIBE_DESC, "describe"),
|
|
1288
|
-
inputSchema: {
|
|
1266
|
+
inputSchema: z.object({
|
|
1289
1267
|
addresses: z.array(z.string()).max(MAX_DESCRIBE_ADDRESSES),
|
|
1290
1268
|
format: z.enum(["compact", "json"]).optional(),
|
|
1291
1269
|
fullDescriptions: z.boolean().optional(),
|
|
1292
|
-
},
|
|
1270
|
+
}),
|
|
1293
1271
|
annotations: READ_ONLY_REMOTE,
|
|
1294
1272
|
},
|
|
1295
1273
|
async (args) => mt.describeTools(args as DescribeArgs),
|
|
@@ -1299,15 +1277,7 @@ export function registerMetaTools(
|
|
|
1299
1277
|
"call_tool",
|
|
1300
1278
|
{
|
|
1301
1279
|
description: CALL_DESC,
|
|
1302
|
-
inputSchema:
|
|
1303
|
-
address: z.string(),
|
|
1304
|
-
args: z.record(z.string(), z.unknown()).optional(),
|
|
1305
|
-
fields: z.array(z.string()).optional(),
|
|
1306
|
-
resultMode: z.enum(["mcp", "value"]).optional(),
|
|
1307
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
1308
|
-
maxRetries: z.number().int().min(0).max(2).optional(),
|
|
1309
|
-
diagnostics: z.boolean().optional(),
|
|
1310
|
-
},
|
|
1280
|
+
inputSchema: z.object(CALL_INPUT_SCHEMA),
|
|
1311
1281
|
// call_tool admits only tools that are themselves explicitly read-only;
|
|
1312
1282
|
// anything else is refused and routed to call_destructive_tool.
|
|
1313
1283
|
annotations: READ_ONLY_REMOTE,
|
|
@@ -1319,15 +1289,7 @@ export function registerMetaTools(
|
|
|
1319
1289
|
"call_destructive_tool",
|
|
1320
1290
|
{
|
|
1321
1291
|
description: CALL_DESTRUCTIVE_DESC,
|
|
1322
|
-
inputSchema:
|
|
1323
|
-
address: z.string(),
|
|
1324
|
-
args: z.record(z.string(), z.unknown()).optional(),
|
|
1325
|
-
fields: z.array(z.string()).optional(),
|
|
1326
|
-
resultMode: z.enum(["mcp", "value"]).optional(),
|
|
1327
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
1328
|
-
maxRetries: z.number().int().min(0).max(2).optional(),
|
|
1329
|
-
diagnostics: z.boolean().optional(),
|
|
1330
|
-
},
|
|
1292
|
+
inputSchema: z.object(CALL_INPUT_SCHEMA),
|
|
1331
1293
|
annotations: {
|
|
1332
1294
|
destructiveHint: true,
|
|
1333
1295
|
readOnlyHint: false,
|
|
@@ -1341,10 +1303,10 @@ export function registerMetaTools(
|
|
|
1341
1303
|
"authorize_connector",
|
|
1342
1304
|
{
|
|
1343
1305
|
description: AUTHORIZE_DESC,
|
|
1344
|
-
inputSchema: {
|
|
1306
|
+
inputSchema: z.object({
|
|
1345
1307
|
connector: z.string(),
|
|
1346
1308
|
force: z.boolean().optional(),
|
|
1347
|
-
},
|
|
1309
|
+
}),
|
|
1348
1310
|
// Starts (or with force, resets) a downstream OAuth flow — it changes
|
|
1349
1311
|
// stored connector auth state, so it is deliberately not read-only.
|
|
1350
1312
|
annotations: {
|
|
@@ -1360,7 +1322,7 @@ export function registerMetaTools(
|
|
|
1360
1322
|
"get_result",
|
|
1361
1323
|
{
|
|
1362
1324
|
description: GET_RESULT_DESC,
|
|
1363
|
-
inputSchema: {
|
|
1325
|
+
inputSchema: z.object({
|
|
1364
1326
|
id: z.string(),
|
|
1365
1327
|
// Both bounds are the shared rules (isValidResultOffset,
|
|
1366
1328
|
// isValidMaxResultBytes) expressed for the wire: spelling them against
|
|
@@ -1368,7 +1330,7 @@ export function registerMetaTools(
|
|
|
1368
1330
|
// in-handler checks if either floor ever moves.
|
|
1369
1331
|
offset: z.number().int().min(MIN_RESULT_OFFSET).optional(),
|
|
1370
1332
|
maxBytes: z.number().int().min(MIN_MAX_RESULT_BYTES).optional(),
|
|
1371
|
-
},
|
|
1333
|
+
}),
|
|
1372
1334
|
annotations: READ_ONLY_LOCAL,
|
|
1373
1335
|
},
|
|
1374
1336
|
async (args) => mt.getResult(args as GetResultArgs),
|
|
@@ -1378,26 +1340,16 @@ export function registerMetaTools(
|
|
|
1378
1340
|
"batch_call",
|
|
1379
1341
|
{
|
|
1380
1342
|
description: BATCH_DESC,
|
|
1381
|
-
inputSchema: {
|
|
1343
|
+
inputSchema: z.object({
|
|
1382
1344
|
calls: z
|
|
1383
|
-
.array(
|
|
1384
|
-
z.object({
|
|
1385
|
-
address: z.string(),
|
|
1386
|
-
args: z.record(z.string(), z.unknown()).optional(),
|
|
1387
|
-
fields: z.array(z.string()).optional(),
|
|
1388
|
-
resultMode: z.enum(["mcp", "value"]).optional(),
|
|
1389
|
-
timeoutMs: z.number().int().positive().optional(),
|
|
1390
|
-
maxRetries: z.number().int().min(0).max(2).optional(),
|
|
1391
|
-
diagnostics: z.boolean().optional(),
|
|
1392
|
-
}),
|
|
1393
|
-
)
|
|
1345
|
+
.array(z.object(CALL_INPUT_SCHEMA))
|
|
1394
1346
|
.min(1)
|
|
1395
1347
|
.max(10),
|
|
1396
1348
|
resultMode: z.enum(["mcp", "value"]).optional(),
|
|
1397
1349
|
timeoutMs: z.number().int().positive().optional(),
|
|
1398
1350
|
maxRetries: z.number().int().min(0).max(2).optional(),
|
|
1399
1351
|
diagnostics: z.boolean().optional(),
|
|
1400
|
-
},
|
|
1352
|
+
}),
|
|
1401
1353
|
// Same gate as call_tool: every call in the batch must be explicitly
|
|
1402
1354
|
// read-only or the batch is refused.
|
|
1403
1355
|
annotations: READ_ONLY_REMOTE,
|
|
@@ -236,57 +236,6 @@
|
|
|
236
236
|
.connector-description { margin-top: .25rem; max-width: 40rem; }
|
|
237
237
|
.connector-message,
|
|
238
238
|
.connector-auth { margin-top: .75rem; }
|
|
239
|
-
.connector-toolkits {
|
|
240
|
-
align-items: baseline;
|
|
241
|
-
display: flex;
|
|
242
|
-
flex-wrap: wrap;
|
|
243
|
-
gap: .25rem .5rem;
|
|
244
|
-
margin-top: .5rem;
|
|
245
|
-
}
|
|
246
|
-
.scope-label {
|
|
247
|
-
border: 1px solid var(--rule);
|
|
248
|
-
display: inline-block;
|
|
249
|
-
font-family: var(--mono);
|
|
250
|
-
font-size: .72rem;
|
|
251
|
-
padding: .05rem .35rem;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
.toolkit-copy { margin-bottom: 1rem; max-width: 42rem; }
|
|
255
|
-
.toolkit-ledger { border-bottom: 1px solid var(--rule); }
|
|
256
|
-
.toolkit-card {
|
|
257
|
-
border-top: 1px solid var(--rule);
|
|
258
|
-
padding: .9rem 0 1rem;
|
|
259
|
-
}
|
|
260
|
-
.toolkit-head {
|
|
261
|
-
display: grid;
|
|
262
|
-
gap: var(--gap);
|
|
263
|
-
grid-template-columns: minmax(0, 2fr) minmax(10rem, 1fr);
|
|
264
|
-
}
|
|
265
|
-
.toolkit-name {
|
|
266
|
-
align-items: baseline;
|
|
267
|
-
display: flex;
|
|
268
|
-
flex-wrap: wrap;
|
|
269
|
-
gap: .5rem;
|
|
270
|
-
}
|
|
271
|
-
.toolkit-name h3 { overflow-wrap: anywhere; }
|
|
272
|
-
.toolkit-state { text-align: right; }
|
|
273
|
-
.toolkit-connectors {
|
|
274
|
-
display: flex;
|
|
275
|
-
flex-wrap: wrap;
|
|
276
|
-
gap: .5rem;
|
|
277
|
-
margin-top: .75rem;
|
|
278
|
-
}
|
|
279
|
-
.toolkit-connector {
|
|
280
|
-
border-left: 1px solid var(--ink);
|
|
281
|
-
display: inline-flex;
|
|
282
|
-
flex-wrap: wrap;
|
|
283
|
-
gap: .4rem;
|
|
284
|
-
padding-left: .5rem;
|
|
285
|
-
}
|
|
286
|
-
.toolkit-rules { margin-top: .75rem; }
|
|
287
|
-
.toolkit-rules > * + * { margin-top: .25rem; }
|
|
288
|
-
.toolkit-endpoint { margin-top: .75rem; }
|
|
289
|
-
|
|
290
239
|
.credential-ledger { border-bottom: 1px solid var(--rule); }
|
|
291
240
|
.credential-card { border-top: 1px solid var(--rule); padding-bottom: .75rem; padding-top: .75rem; }
|
|
292
241
|
.credential-head {
|
|
@@ -403,11 +352,9 @@
|
|
|
403
352
|
.section,
|
|
404
353
|
.section + .section { margin-top: 2.5rem; }
|
|
405
354
|
.connector-head,
|
|
406
|
-
.toolkit-head,
|
|
407
355
|
.tool,
|
|
408
356
|
.activity-item { grid-template-columns: 1fr; }
|
|
409
|
-
.connector-state
|
|
410
|
-
.toolkit-state { text-align: left; }
|
|
357
|
+
.connector-state { text-align: left; }
|
|
411
358
|
.credential-field { align-items: start; grid-template-columns: 1fr; gap: .25rem; }
|
|
412
359
|
input { min-height: 2.75rem; }
|
|
413
360
|
}
|