@zackbart/connecta 0.18.1 → 0.18.3

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 (38) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +4 -0
  3. package/dist/catalog-service.d.ts +4 -0
  4. package/dist/catalog-service.js +22 -6
  5. package/dist/connectors/remote-mcp.d.ts +49 -2
  6. package/dist/connectors/remote-mcp.js +302 -7
  7. package/dist/invocation.js +9 -3
  8. package/dist/providers/linear.d.ts +7 -1
  9. package/dist/providers/linear.js +12 -2
  10. package/dist/providers/mixpanel.d.ts +5 -1
  11. package/dist/providers/mixpanel.js +13 -2
  12. package/dist/providers/revenuecat.d.ts +3 -1
  13. package/dist/providers/revenuecat.js +12 -3
  14. package/dist/providers/stripe.d.ts +9 -3
  15. package/dist/providers/stripe.js +24 -5
  16. package/dist/registry.d.ts +7 -0
  17. package/dist/registry.js +9 -0
  18. package/dist/result-shapes.d.ts +13 -0
  19. package/dist/result-shapes.js +331 -0
  20. package/dist/skills.js +3 -3
  21. package/dist/version.d.ts +1 -1
  22. package/dist/version.js +1 -1
  23. package/documentation/architecture.md +5 -2
  24. package/documentation/code-mode.md +6 -6
  25. package/documentation/connectors.md +35 -0
  26. package/documentation/linear.md +25 -4
  27. package/documentation/meta-tools.md +22 -3
  28. package/documentation/mixpanel.md +19 -0
  29. package/documentation/operations.md +3 -1
  30. package/documentation/provider-conventions.md +37 -21
  31. package/documentation/revenuecat.md +23 -1
  32. package/documentation/storage-and-credentials.md +55 -0
  33. package/documentation/stripe.md +22 -2
  34. package/documentation/upgrading.md +32 -4
  35. package/ethos.md +3 -3
  36. package/examples/worker/src/index.ts +5 -0
  37. package/package.json +1 -1
  38. package/templates/node/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,86 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## 0.18.3 — 2026-08-25
6
+
7
+ This change gives code mode memory where downstream MCP catalogs are usually
8
+ silent: after a successful call with no declared output schema, Connecta keeps
9
+ only the result's field names and broad JSON types and shows that open shape on
10
+ later discovery. Nothing breaks, no deployment option changes, and providers
11
+ that already declare outputs are untouched. The bounded cache lives only in the
12
+ current process or Worker isolate and forgets each shape after 24 hours.
13
+ Discovery still never executes a tool, and
14
+ the new `outputSchemaSource: "observed"` marker is the warning label that keeps
15
+ one or several real results from masquerading as a provider contract.
16
+
17
+ ### Added
18
+
19
+ - **Passive observed output schemas.** Successful explicitly read-only calls
20
+ whose provider declared no `outputSchema` infer and merge an open
21
+ optional-field schema of names and broad JSON types. The inference retains no
22
+ arguments, scalar values, raw results, code, credentials, or errors. Property
23
+ names may be user-authored. It bounds depth, breadth, names, nodes, definition
24
+ size, and schema bytes; keeps at most 256 runtime entries; ties each entry to
25
+ the exact tool definition; and expires it after 24 hours.
26
+ Search and describe return it with `outputSchemaSource: "observed"`; a
27
+ declared schema always wins. Observation is synchronous, storage-free, and
28
+ unable to fail the call. The design follows new warm-cache evidence and two
29
+ live deployment audits: BePresent had 246/378 tools without output schemas,
30
+ while OneMany's maintained connectors declared all 90 (#442).
31
+
32
+ ## 0.18.2 — 2026-08-18
33
+
34
+ This patch closes the gap the RevenueCat rollout exposed on the same day 0.18.1
35
+ shipped: a hosted-MCP connection could authenticate with OAuth from the
36
+ Connections page or with a header literal baked into the deployment file, but
37
+ never with a key an operator pastes on `/credentials` the way every `api()`
38
+ connector already can. Now it can. Nothing breaks, no constructor changes, and a
39
+ deployment that keeps its static keys in runtime secrets can ignore this release.
40
+ Two things are worth reading before adopting the new shape: the operator vault
41
+ (`credentials.encryptionKey`) must be configured, and a key pasted with a
42
+ wrapped newline is refused before it is framed rather than echoed back by the
43
+ runtime that rejects it.
44
+
45
+ ### Added
46
+
47
+ - **`auth: { type: "credential" }` for `remoteMcp()` and every maintained
48
+ hosted connection.** The connector declares an operator credential slot on
49
+ `/credentials` (label and guidance from the provider, overridable), reads the
50
+ stored value on every request before trusting a cached client, and frames it
51
+ as `Authorization: Bearer <value>` by default — `header` and `scheme` are
52
+ configurable, `scheme: null` sends the value bare, and a scheme ending in
53
+ `Basic` base64-encodes it, which is how Mixpanel's documented
54
+ `Bearer Basic <base64(user:secret)>` is spelled. A missing or empty value is
55
+ `auth_required`, and `authorize_connector` returns the operator handoff to
56
+ `/credentials`; a deployment without a vault warns at construction and answers
57
+ `recovery: "unavailable"` at use. Rotation on `/credentials` takes effect on
58
+ the next call: a SHA-256 digest of the connected value is compared per
59
+ request (including against an in-flight connect) and a differing digest
60
+ closes the old client. The `/credentials` Test action connects with the
61
+ candidate and counts the catalog, and only when an operator presses it — the
62
+ #179 refusal of proactive probing stands, now written into P10. Provider
63
+ defaults: Stripe (requires `mode`, refuses `connectedAccount`), Linear
64
+ (Bearer, per Linear's MCP docs), Mixpanel (`Bearer Basic`, service account),
65
+ RevenueCat (single-project branch, "API v2 secret key") (#439).
66
+
67
+ ### Changed
68
+
69
+ - **A stored credential never reaches an agent or operator surface, even
70
+ malformed.** The value is refused before framing if it carries a control
71
+ character (the wrapped-newline paste), with a message that names the problem
72
+ and not the value; behind that, any error whose message or `cause` chain
73
+ quotes the raw or framed value is replaced whole rather than redacted. Both
74
+ layers are tested independently (#439).
75
+
76
+ - **Provider conventions P9 and P10 say what is now true.** P9 names both
77
+ headless shapes and requires the framing to match the provider's published
78
+ MCP contract; P10 is retitled to allow an operator-requested credential test
79
+ while forbidding any unasked probe (#439).
80
+
81
+ - **Linear's headers example uses `Bearer`.** Linear's MCP server documents
82
+ `Authorization: Bearer <token>` for API keys; the bare form is its GraphQL
83
+ convention. Existing `headers` connectors are untouched (#439).
84
+
5
85
  ## 0.18.1 — 2026-08-18
6
86
 
7
87
  This patch is the response to one long investigation run against a live
package/README.md CHANGED
@@ -67,6 +67,10 @@ Fifty issues in, one small object out. Your context window notices.
67
67
  classifications, imported one at a time.
68
68
  - **Let the agent work in code.** Search, chain, filter, join, and reduce
69
69
  inside the sandbox instead of round-tripping every call through the model.
70
+ - **Teach undeclared result shapes by using them.** Successful read-only calls
71
+ retain field names and broad types in bounded runtime memory, never scalar
72
+ values, so later programs can project a remote MCP result its provider never
73
+ documented.
70
74
  - **Keep writes deliberate.** Only tools marked read-only run in a program.
71
75
  Everything else is a separate, visible call your client can gate.
72
76
  - **Run it where you like.** Node, a Docker container, or a Cloudflare Worker,
@@ -49,6 +49,7 @@ interface CatalogSearchEntry {
49
49
  description?: string;
50
50
  inputSchema?: unknown;
51
51
  outputSchema?: unknown;
52
+ outputSchemaSource?: "observed";
52
53
  inputSchemaTruncated?: true;
53
54
  outputSchemaTruncated?: true;
54
55
  inputKeys?: string[];
@@ -112,6 +113,7 @@ export interface CatalogDescription {
112
113
  guideRequiredReasons?: GuideRequiredReason[];
113
114
  inputSchema?: unknown;
114
115
  outputSchema?: unknown;
116
+ outputSchemaSource?: "observed";
115
117
  annotations?: ToolDef["annotations"];
116
118
  error?: string;
117
119
  errorDetails?: CatalogDescriptionFailureDetail;
@@ -174,6 +176,7 @@ export declare class CatalogService {
174
176
  }, purpose: string): NonNullable<CallErrorDetails["nextAction"]>;
175
177
  loadConnector(id: string, callOptions?: ConnectorOperationOptions): Promise<ToolDef[]>;
176
178
  private loadForDiscovery;
179
+ private outputSchema;
177
180
  resolveTool(address: string, callOptions?: ConnectorOperationOptions): Promise<CatalogResolution>;
178
181
  /**
179
182
  * Resolve the JavaScript-safe property used by a lazy code-mode namespace
@@ -242,6 +245,7 @@ export declare function flatSearchResult(page: CatalogSearchPage): {
242
245
  description?: string;
243
246
  inputSchema?: unknown;
244
247
  outputSchema?: unknown;
248
+ outputSchemaSource?: "observed";
245
249
  inputSchemaTruncated?: true;
246
250
  outputSchemaTruncated?: true;
247
251
  inputKeys?: string[];
@@ -266,6 +266,14 @@ export class CatalogService {
266
266
  timeoutMs: this.probeTimeoutMs,
267
267
  }), this.probeTimeoutMs, label);
268
268
  }
269
+ outputSchema(connectorId, tool) {
270
+ if (tool.outputSchema)
271
+ return { schema: tool.outputSchema };
272
+ const observed = this.registry.observedOutputSchema(connectorId, tool);
273
+ return observed
274
+ ? { schema: observed, source: "observed" }
275
+ : {};
276
+ }
269
277
  async resolveTool(address, callOptions = {}) {
270
278
  const resolved = this.registry.resolveAddress(address);
271
279
  if (!resolved) {
@@ -487,15 +495,18 @@ export class CatalogService {
487
495
  });
488
496
  const pageMatches = matches.slice(offset, offset + limit);
489
497
  const entries = pageMatches.map((match) => {
498
+ const output = args.includeSchemas
499
+ ? this.outputSchema(match.connector.id, match.tool)
500
+ : {};
490
501
  const input = match.tool.inputSchema ?? { type: "object" };
491
502
  const renderedInput = args.includeSchemas
492
503
  ? renderSearchSchema(input, args.includeSchemas)
493
504
  : undefined;
494
- const renderedOutput = args.includeSchemas && match.tool.outputSchema
495
- ? renderSearchSchema(match.tool.outputSchema, args.includeSchemas)
505
+ const renderedOutput = args.includeSchemas && output.schema
506
+ ? renderSearchSchema(output.schema, args.includeSchemas)
496
507
  : undefined;
497
508
  const schemaKeys = args.includeSchemas && args.includeSchemaKeys
498
- ? schemaKeyMetadata(input, match.tool.outputSchema)
509
+ ? schemaKeyMetadata(input, output.schema)
499
510
  : undefined;
500
511
  const description = summarizeDiscoveryDescription(match.tool.description, args.fullDescriptions === true);
501
512
  const requiredReasons = guideRequiredReasons(match.connector, match.tool, renderedInput?.truncated === true || renderedOutput?.truncated === true);
@@ -520,11 +531,14 @@ export class CatalogService {
520
531
  ...(renderedInput?.truncated
521
532
  ? { inputSchemaTruncated: true }
522
533
  : {}),
523
- ...(args.includeSchemas && match.tool.outputSchema
534
+ ...(args.includeSchemas && output.schema
524
535
  ? {
525
536
  outputSchema: renderedOutput?.schema,
526
537
  }
527
538
  : {}),
539
+ ...(args.includeSchemas && output.source
540
+ ? { outputSchemaSource: output.source }
541
+ : {}),
528
542
  ...(renderedOutput?.truncated
529
543
  ? { outputSchemaTruncated: true }
530
544
  : {}),
@@ -813,6 +827,7 @@ export class CatalogService {
813
827
  };
814
828
  }
815
829
  const input = tool.inputSchema ?? { type: "object" };
830
+ const output = this.outputSchema(addressResolution.connector.id, tool);
816
831
  const description = summarizeDescription(tool.description, args.fullDescriptions === true);
817
832
  const requiredReasons = guideRequiredReasons(addressResolution.connector, tool, false);
818
833
  const guideSummary = connectorGuideSummary(addressResolution.connector);
@@ -833,11 +848,12 @@ export class CatalogService {
833
848
  }
834
849
  : {}),
835
850
  inputSchema: renderSchema(input, format),
836
- ...(tool.outputSchema
851
+ ...(output.schema
837
852
  ? {
838
- outputSchema: renderSchema(tool.outputSchema, format),
853
+ outputSchema: renderSchema(output.schema, format),
839
854
  }
840
855
  : {}),
856
+ ...(output.source ? { outputSchemaSource: output.source } : {}),
841
857
  ...(tool.annotations ? { annotations: tool.annotations } : {}),
842
858
  };
843
859
  });
@@ -1,12 +1,58 @@
1
1
  import type { FetchLike, Transport } from "@modelcontextprotocol/client";
2
2
  import { ConnectorCallError } from "../errors.js";
3
- import type { Connector, ConnectorCallAdmissionPolicy, ConnectorContext, ConnectorUsageGuide, Logger } from "../types.js";
3
+ import type { Connector, ConnectorCallAdmissionPolicy, ConnectorContext, ConnectorCredentialConfig, ConnectorUsageGuide, Logger } from "../types.js";
4
+ /**
5
+ * A static downstream credential the operator supplies at `/credentials`
6
+ * rather than the deployment baking into its source.
7
+ *
8
+ * The connector, its endpoint, and the credential *slot* stay declared in
9
+ * code; only the secret arrives through the operator route, exactly as for
10
+ * `api()`. One reserved `value` field, deliberately: a header is assembled
11
+ * from a name, a framing scheme, and one secret, and anything that needs two
12
+ * secrets composed into one header is a provider integration, not a proxy
13
+ * config ([#439](https://github.com/zackbart/connecta/issues/439)).
14
+ */
15
+ interface RemoteMcpCredentialAuth {
16
+ type: "credential";
17
+ /**
18
+ * Operator-facing slot description rendered on `/credentials`. Defaults to
19
+ * `{ label: "API key" }`; a maintained provider passes the name the provider
20
+ * itself uses. Named `fields` are refused — this shape reads the reserved
21
+ * `value` field only.
22
+ */
23
+ credential?: ConnectorCredentialConfig;
24
+ /** Header the credential rides. Defaults to `Authorization`. */
25
+ header?: string;
26
+ /**
27
+ * Framing token placed before the value. Defaults to `"Bearer"`. `null` (or
28
+ * an empty string) sends the stored value verbatim, for an endpoint that
29
+ * reads a bare key. A scheme whose last token is `Basic` declares
30
+ * HTTP Basic credentials: the stored `user:secret` is base64-encoded first,
31
+ * so `"Basic"` produces `Basic <base64>` and Mixpanel's documented
32
+ * `"Bearer Basic"` produces `Bearer Basic <base64>`.
33
+ */
34
+ scheme?: string | null;
35
+ }
4
36
  export type RemoteMcpAuth = {
5
37
  type: "headers";
6
38
  headers: Record<string, string>;
7
- } | {
39
+ } | RemoteMcpCredentialAuth | {
8
40
  type: "oauth";
9
41
  };
42
+ /**
43
+ * Apply a maintained provider's slot copy and header framing to credential
44
+ * auth the deployment left bare.
45
+ *
46
+ * A provider knows what its own key is called and how the endpoint expects it
47
+ * framed; a deployment that states either one keeps its answer. Every other
48
+ * auth shape passes through untouched, so a provider can hand this its whole
49
+ * `auth` option without branching first.
50
+ */
51
+ export declare function withCredentialDefaults(auth: RemoteMcpAuth, defaults: {
52
+ credential: ConnectorCredentialConfig;
53
+ /** Provider framing; omit to leave the `Bearer` default in place. */
54
+ scheme?: string | null;
55
+ }): RemoteMcpAuth;
10
56
  export type RemoteMcpRedirectPolicy = "none" | "same-origin";
11
57
  export interface RemoteMcpOptions {
12
58
  url: string;
@@ -90,3 +136,4 @@ export declare function redirectSafeFetch(connectorId: string, policy?: RemoteMc
90
136
  * server or hide other connectors).
91
137
  */
92
138
  export declare function remoteMcp(id: string, opts: RemoteMcpOptions): Connector;
139
+ export {};