@zackbart/connecta 0.22.2 → 0.23.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +1 -1
  3. package/dist/auth/downstream-oauth.d.ts +3 -1
  4. package/dist/auth/downstream-oauth.js +11 -0
  5. package/dist/catalog-service.d.ts +1 -6
  6. package/dist/catalog-service.js +3 -53
  7. package/dist/errors.d.ts +1 -1
  8. package/dist/execute.d.ts +5 -52
  9. package/dist/execute.js +40 -347
  10. package/dist/executors/quickjs-protocol.d.ts +0 -7
  11. package/dist/executors/quickjs-protocol.js +2 -10
  12. package/dist/executors/quickjs.js +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/invocation.d.ts +0 -33
  15. package/dist/invocation.js +54 -121
  16. package/dist/meta-tools.d.ts +3 -6
  17. package/dist/meta-tools.js +6 -17
  18. package/dist/registry.js +4 -0
  19. package/dist/routes/mcp.js +0 -48
  20. package/dist/server.d.ts +1 -2
  21. package/dist/server.js +1 -19
  22. package/dist/skills.d.ts +1 -1
  23. package/dist/skills.js +55 -19
  24. package/dist/types.d.ts +1 -15
  25. package/dist/version.d.ts +1 -1
  26. package/dist/version.js +1 -1
  27. package/documentation/architecture.md +7 -11
  28. package/documentation/auth.md +4 -5
  29. package/documentation/call-admission.md +10 -11
  30. package/documentation/code-mode.md +65 -260
  31. package/documentation/connectors.md +7 -5
  32. package/documentation/meta-tools.md +31 -14
  33. package/documentation/operations.md +7 -9
  34. package/documentation/provider-conventions.md +2 -2
  35. package/documentation/upgrading.md +59 -9
  36. package/ethos.md +14 -13
  37. package/package.json +1 -1
  38. package/templates/node/package.json +1 -1
  39. package/dist/apps-shell.d.ts +0 -37
  40. package/dist/apps-shell.js +0 -174
package/CHANGELOG.md CHANGED
@@ -2,6 +2,65 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## 0.23.0 — 2026-09-07
6
+
7
+ This release removes server-owned program views, connector HTTP routes, and
8
+ three convenience APIs. It also exposes configured account titles during
9
+ discovery and clarifies schema inspection and dependent calls.
10
+ Programs use canonical tool addresses and JavaScript promises; callers own
11
+ retry timing. Stored programs using shortcut globals, `connecta.batch`, or
12
+ `connecta.ui` need migration. Direct calls must omit `maxRetries`. Deployments
13
+ using none of these need no configuration or storage changes. The seven tools,
14
+ operator pages, credentials, and emitted media remain. Cloudflare Global API
15
+ Key authentication and multi-field credentials remain supported.
16
+
17
+ ### Added
18
+
19
+ - Optional `investigate` guidance for purchase verification, experiments, and
20
+ customer or deployment investigations. It explains account selection,
21
+ evidence requirements, and when to stop with an unresolved gap (#527).
22
+ - Bounded configured account titles in the initial connector inventory and
23
+ program search results, without provider calls or changes to ranking (#527).
24
+
25
+ ### Changed
26
+
27
+ - Remove connector `handleRequest` routes. Stale declarations fail at
28
+ construction; custom routes belong to the existing deployment fetch handler.
29
+ - Remove MCP Apps rendering, its HTML shell, resource handlers, extension
30
+ declaration, and tool UI metadata. Clients render returned data.
31
+ - Remove connector shortcut globals and their sanitization and collision rules.
32
+ Programs call the canonical address through `connecta.call`.
33
+ - Remove `connecta.batch`. Use `Promise.all` or `Promise.allSettled` under the
34
+ same host-call and connector admission limits.
35
+ - Remove automatic retries and backoff timing from direct calls. Failures keep
36
+ their classification and provider retry hint. Direct-call schemas reject
37
+ unknown arguments, including the removed `maxRetries` option.
38
+ - Clarify JSON schema inspection, provider result shapes, and promise failures.
39
+ Keep discovery and calls together when schemas suffice; allow a small sample
40
+ for unfamiliar results before continuing in another program (#527).
41
+
42
+ ### Fixed
43
+
44
+ - Remove the stale CI invocation of the retired evaluation `audit` script.
45
+ The benchmark self-tests remain in CI; whole-agent comparisons run locally.
46
+
47
+ ## 0.22.3 — 2026-09-03
48
+
49
+ This patch carries OAuth discovery across the browser redirect. It matters for
50
+ downstream MCP servers whose authentication challenge names a protected-resource
51
+ metadata URL outside the RFC 9728 default path, including Cloudflare Access
52
+ Managed OAuth. Existing grants and connectors using the default path require no
53
+ configuration or storage migration.
54
+
55
+ ### Fixed
56
+
57
+ - **Non-default OAuth discovery survives callbacks.** The KV OAuth provider now
58
+ persists the SDK's discovery state in the authorization generation, restores
59
+ it in the callback's fresh request scope, and deletes it through discovery,
60
+ all-credential, and generation cleanup. A callback therefore exchanges its
61
+ code against the same validated authorization-server issuer that registered
62
+ the client instead of invalidating that client after rediscovery (#523).
63
+
5
64
  ## 0.22.2 — 2026-09-01
6
65
 
7
66
  This patch makes the maintained-provider release check credential-free and
package/README.md CHANGED
@@ -45,7 +45,7 @@ This is the kind of thing the agent writes, not you:
45
45
 
46
46
  ```js
47
47
  async () => {
48
- const { nodes } = await tracker.list_issues({ state: "started" });
48
+ const { nodes } = await connecta.call("tracker.list_issues", { state: "started" });
49
49
  const byOwner = {};
50
50
  for (const issue of nodes) {
51
51
  (byOwner[issue.assignee?.name ?? "unassigned"] ??= []).push(issue.identifier);
@@ -1,4 +1,4 @@
1
- import type { FetchLike, OAuthClientInformationContext, OAuthClientInformationMixed, OAuthClientMetadata, OAuthClientProvider, OAuthTokens } from "@modelcontextprotocol/client";
1
+ import type { FetchLike, OAuthClientInformationContext, OAuthClientInformationMixed, OAuthClientMetadata, OAuthClientProvider, OAuthDiscoveryState, OAuthTokens } from "@modelcontextprotocol/client";
2
2
  import type { KVStorage } from "../types.js";
3
3
  type OAuthRefreshFlightOutcome = {
4
4
  status: "refreshed";
@@ -134,6 +134,8 @@ export declare class KvOAuthProvider implements OAuthClientProvider {
134
134
  get clientMetadata(): OAuthClientMetadata;
135
135
  clientInformation(ctx?: OAuthClientInformationContext): Promise<OAuthClientInformationMixed | undefined>;
136
136
  saveClientInformation(info: OAuthClientInformationMixed, ctx?: OAuthClientInformationContext): Promise<void>;
137
+ discoveryState(): Promise<OAuthDiscoveryState | undefined>;
138
+ saveDiscoveryState(state: OAuthDiscoveryState): Promise<void>;
137
139
  tokens(ctx?: OAuthClientInformationContext): Promise<OAuthTokens | undefined>;
138
140
  saveTokens(tokens: OAuthTokens, ctx?: OAuthClientInformationContext): Promise<void>;
139
141
  /**
@@ -24,6 +24,7 @@ const OAUTH_VALUE_KEYS = [
24
24
  "oauth:pending",
25
25
  "oauth:verifier",
26
26
  "oauth:state",
27
+ "oauth:discovery",
27
28
  ];
28
29
  const MAX_CLEANUP_BACKLOG = 1_000;
29
30
  function isRefreshTokenRequest(init) {
@@ -621,6 +622,12 @@ export class KvOAuthProvider {
621
622
  async saveClientInformation(info, ctx) {
622
623
  await this.writeValue("oauth:client", info, (value) => JSON.stringify(value), ctx?.issuer);
623
624
  }
625
+ async discoveryState() {
626
+ return (await this.readValue("oauth:discovery", (raw) => JSON.parse(raw)))?.value;
627
+ }
628
+ async saveDiscoveryState(state) {
629
+ await this.writeValue("oauth:discovery", state, (value) => JSON.stringify(value));
630
+ }
624
631
  async tokens(ctx) {
625
632
  const refreshGeneration = ctx ? await this.flowGeneration() : undefined;
626
633
  const successIdentity = refreshGeneration !== undefined
@@ -800,6 +807,7 @@ export class KvOAuthProvider {
800
807
  oauthValueStorageKey("oauth:client", generation),
801
808
  oauthValueStorageKey("oauth:tokens", generation),
802
809
  oauthValueStorageKey("oauth:verifier", generation),
810
+ oauthValueStorageKey("oauth:discovery", generation),
803
811
  ]);
804
812
  }
805
813
  else if (scope === "client") {
@@ -811,6 +819,9 @@ export class KvOAuthProvider {
811
819
  else if (scope === "verifier") {
812
820
  await this.storage.delete(oauthValueStorageKey("oauth:verifier", generation));
813
821
  }
822
+ else if (scope === "discovery") {
823
+ await this.storage.delete(oauthValueStorageKey("oauth:discovery", generation));
824
+ }
814
825
  if (endsRefresh) {
815
826
  this.failRefreshFlight(new Error("OAuth refresh invalidated credentials before tokens were saved."));
816
827
  }
@@ -181,12 +181,6 @@ export declare class CatalogService {
181
181
  private catalogLoadFailure;
182
182
  private unknownToolFailure;
183
183
  resolveTool(address: string, callOptions?: ConnectorOperationOptions): Promise<CatalogResolution>;
184
- /**
185
- * Resolve the JavaScript-safe property used by a lazy code-mode namespace
186
- * back to exactly one catalog tool. Ambiguous aliases fail with an explicit
187
- * escape hatch instead of silently choosing the first tool.
188
- */
189
- resolveToolAlias(connectorId: string, alias: string, aliasFor: (toolName: string) => string, callOptions?: ConnectorOperationOptions): Promise<CatalogResolution>;
190
184
  search(args: CatalogSearchArgs): Promise<CatalogSearchPage>;
191
185
  describe(args: CatalogDescribeArgs): Promise<CatalogDescription[]>;
192
186
  }
@@ -247,6 +241,7 @@ export declare function flatSearchResult(page: CatalogSearchPage): {
247
241
  tools: {
248
242
  guideSummary?: string;
249
243
  guide?: string;
244
+ connectorTitle?: string;
250
245
  name: string;
251
246
  address: string;
252
247
  description?: string;
@@ -336,59 +336,6 @@ export class CatalogService {
336
336
  catalogMs: Date.now() - started,
337
337
  };
338
338
  }
339
- /**
340
- * Resolve the JavaScript-safe property used by a lazy code-mode namespace
341
- * back to exactly one catalog tool. Ambiguous aliases fail with an explicit
342
- * escape hatch instead of silently choosing the first tool.
343
- */
344
- async resolveToolAlias(connectorId, alias, aliasFor, callOptions = {}) {
345
- const connector = this.registry.getConnector(connectorId);
346
- if (!connector) {
347
- return this.unknownAddressFailure(`${connectorId}.${alias}`, alias);
348
- }
349
- const started = Date.now();
350
- let tools;
351
- try {
352
- tools = await this.loadConnector(connector.id, callOptions);
353
- }
354
- catch (cause) {
355
- return this.catalogLoadFailure(cause, started, connector, alias);
356
- }
357
- const [definition, ...collisions] = tools.filter((tool) => aliasFor(tool.name) === alias);
358
- if (!definition) {
359
- return this.unknownToolFailure(alias, connector, started);
360
- }
361
- if (collisions.length > 0) {
362
- const names = [definition, ...collisions]
363
- .map((tool) => `"${tool.name}"`)
364
- .join(", ");
365
- return {
366
- ok: false,
367
- error: {
368
- code: "ambiguous_tool_alias",
369
- message: `Tool alias "${boundedEchoText(alias)}" is ambiguous on connector "${connector.id}" because ${names} sanitize to the same name. Use connecta.call with an exact address.`,
370
- retryable: false,
371
- nextAction: {
372
- function: "connecta.call",
373
- addresses: [definition, ...collisions].map((tool) => `${connector.id}.${tool.name}`),
374
- purpose: "Choose the intended canonical address and call it with the original arguments.",
375
- },
376
- },
377
- catalogMs: Date.now() - started,
378
- connector,
379
- toolName: alias,
380
- };
381
- }
382
- return {
383
- ok: true,
384
- resolved: {
385
- connector,
386
- toolName: definition.name,
387
- definition,
388
- },
389
- catalogMs: Date.now() - started,
390
- };
391
- }
392
339
  async search(args) {
393
340
  const query = args.query ?? "";
394
341
  const retrievalQuery = lexicalSearchQuery(query);
@@ -894,6 +841,9 @@ export function flatSearchResult(page) {
894
841
  return {
895
842
  tools: page.entries.map((entry) => ({
896
843
  ...entry.tool,
844
+ ...(entry.connector.title
845
+ ? { connectorTitle: boundedEchoText(entry.connector.title.replace(/\s+/g, " ").trim(), 117) }
846
+ : {}),
897
847
  ...(entry.guide ? { guide: entry.guide } : {}),
898
848
  ...(entry.guideSummary
899
849
  ? { guideSummary: entry.guideSummary }
package/dist/errors.d.ts CHANGED
@@ -74,7 +74,7 @@ export declare class ConnectorCallError extends Error {
74
74
  validation?: ArgumentValidationDetails;
75
75
  });
76
76
  }
77
- /** The `error` object surfaced in value-mode call results and connecta.batch entries. */
77
+ /** The `error` object surfaced in value-mode call results and rejected promises. */
78
78
  export interface CallErrorDetails {
79
79
  code: string;
80
80
  message: string;
package/dist/execute.d.ts CHANGED
@@ -5,7 +5,6 @@ import { type ToolResult } from "./meta-tools.js";
5
5
  import { InvocationFailure } from "./invocation.js";
6
6
  import type { RegistryView } from "./registry.js";
7
7
  import type { Executor, ExecutorProvider, Logger } from "./types.js";
8
- export declare const EXECUTE_MAX_BATCH_CALLS = 10;
9
8
  /** Complete entries plus an exact omission count, all inside this byte cap. */
10
9
  export declare const CONNECTOR_INVENTORY_MAX_BYTES = 256;
11
10
  /**
@@ -17,7 +16,7 @@ export declare const CONNECTOR_INVENTORY_MAX_BYTES = 256;
17
16
  */
18
17
  export declare const EXECUTE_MAX_EMITTED_BYTES = 4000000;
19
18
  export declare const EXECUTE_MAX_EMITTED_BLOCKS = 32;
20
- type ExecuteDiagnosticOperation = "search" | "describe" | "call" | "batch";
19
+ type ExecuteDiagnosticOperation = "search" | "describe" | "call";
21
20
  interface ExecuteOperationDiagnostics {
22
21
  operation: ExecuteDiagnosticOperation;
23
22
  count: number;
@@ -26,7 +25,6 @@ interface ExecuteOperationDiagnostics {
26
25
  resultBytes: number;
27
26
  catalogMs: number;
28
27
  connectorMs: number;
29
- calls?: number;
30
28
  }
31
29
  declare class ExecuteDiagnostics {
32
30
  private readonly started;
@@ -35,19 +33,12 @@ declare class ExecuteDiagnostics {
35
33
  setupMs: number;
36
34
  executorWallMs: number;
37
35
  private emitted?;
38
- private ui?;
39
36
  /** Numbers only, per R8 — and only once something was emitted, so a
40
37
  * non-emitting run's diagnostics stay byte-for-byte what they were. */
41
38
  recordEmitted(count: number, bytes: number): void;
42
- /**
43
- * U9: the UI payload gets its own aggregate — one number, the payload's
44
- * serialized size. Folding it into `emitted` would desync that aggregate's
45
- * pair, which reports the bytes a specific block count cost.
46
- */
47
- recordUi(bytes: number): void;
48
39
  private stats;
49
40
  recordCatalog(operation: "search" | "describe", durationMs: number, ok: boolean, result?: unknown): void;
50
- recordCall(operation: "call" | "batch", outcome: {
41
+ recordCall(outcome: {
51
42
  ok: boolean;
52
43
  durationMs: number;
53
44
  timing: {
@@ -56,7 +47,6 @@ declare class ExecuteDiagnostics {
56
47
  };
57
48
  value?: unknown;
58
49
  }): void;
59
- recordBatch(durationMs: number, ok: boolean, calls: number, result?: unknown): void;
60
50
  finish(): {
61
51
  timing: {
62
52
  totalMs: number;
@@ -71,7 +61,6 @@ declare class ExecuteDiagnostics {
71
61
  count: number;
72
62
  bytes: number;
73
63
  };
74
- ui?: number;
75
64
  };
76
65
  }
77
66
  /**
@@ -91,56 +80,20 @@ export type EmittedBlock = {
91
80
  data: string;
92
81
  mimeType: string;
93
82
  };
94
- interface UiPayload {
95
- html: string;
96
- }
97
- /**
98
- * Request-local collection for `connecta.emit` and `connecta.ui`. Budgets fail
99
- * loudly at the crossing call — nothing is partially accepted and prior blocks
100
- * are unaffected — so a program learns it is over budget while it can still
101
- * choose differently (M5, U4). Accepted output never rides `ExecuteResult`; the
102
- * handler that owns this collector delivers it on the final tool result.
103
- *
104
- * The two channels share the byte aggregate and nothing else: a UI payload is
105
- * not a block, so it spends no block count, and at most one is ever accepted.
106
- */
107
83
  export declare class EmitCollector {
108
84
  private readonly maxBytes;
109
85
  private readonly maxBlocks;
110
86
  private readonly diagnostics?;
111
87
  readonly blocks: EmittedBlock[];
112
- /** The shared transport aggregate: emitted blocks plus the UI payload. */
113
88
  bytes: number;
114
- /** The one accepted UI payload (U2), delivered in result `_meta` on success. */
115
- ui?: UiPayload;
116
- /** What the blocks alone cost, so the `emitted` aggregate stays a true pair. */
117
- private blockBytes;
118
89
  constructor(maxBytes: number, maxBlocks: number, diagnostics?: ExecuteDiagnostics | undefined);
119
90
  accept(raw: unknown): void;
120
- /**
121
- * U2 and U4: one payload per run, measured as the serialized bytes of
122
- * `{ html }` against the same aggregate emit spends. A second call throws
123
- * naming the constraint rather than replacing the first — one tool result
124
- * renders one view, and last-wins would silently discard a payload the
125
- * program deliberately supplied.
126
- *
127
- * Multiplicity is checked before shape, so the second call is told what it
128
- * actually broke. A program whose second payload is also malformed has one
129
- * problem worth naming — that there is a second payload at all — and a
130
- * complaint about its type would send the author to fix the wrong thing.
131
- */
132
- acceptUi(...values: unknown[]): void;
133
- private assertUiVacant;
134
- private acceptUiPayload;
135
91
  }
136
- /** Convert a connector/tool name into a valid JS identifier. */
137
- export declare function sanitizeIdentifier(name: string): string;
138
92
  /**
139
- * Expose one fixed host provider plus trusted sandbox setup that creates a
140
- * lazy proxy global per connector. No connector catalog is touched until code
141
- * calls that namespace or explicitly asks search/describe.
93
+ * Expose one host provider and typed guest errors. Catalogs load only when
94
+ * the program calls a tool or asks search/describe.
142
95
  */
143
- export declare function buildSandboxProviders(registry: RegistryView, baseUrl: string, logger: Logger, activity?: ActivityRequestContext, limits?: {
96
+ export declare function buildSandboxProviders(registry: RegistryView, baseUrl: string, _logger: Logger, activity?: ActivityRequestContext, limits?: {
144
97
  signal?: AbortSignal | undefined;
145
98
  maxHostCalls?: number | undefined;
146
99
  hostCallTimeoutMs?: number | undefined;