@zackbart/connecta 0.9.1 → 0.10.1

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 (111) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +49 -92
  3. package/dist/access-tokens.d.ts +32 -0
  4. package/dist/access-tokens.d.ts.map +1 -0
  5. package/dist/access-tokens.js +225 -0
  6. package/dist/access-tokens.js.map +1 -0
  7. package/dist/catalog-service.d.ts.map +1 -1
  8. package/dist/catalog-service.js +1 -4
  9. package/dist/catalog-service.js.map +1 -1
  10. package/dist/errors.d.ts +5 -0
  11. package/dist/errors.d.ts.map +1 -1
  12. package/dist/errors.js +26 -0
  13. package/dist/errors.js.map +1 -1
  14. package/dist/execute.d.ts +3 -1
  15. package/dist/execute.d.ts.map +1 -1
  16. package/dist/execute.js +79 -18
  17. package/dist/execute.js.map +1 -1
  18. package/dist/executor-result.d.ts.map +1 -1
  19. package/dist/executor-result.js +37 -6
  20. package/dist/executor-result.js.map +1 -1
  21. package/dist/executors/quickjs-protocol.d.ts +12 -0
  22. package/dist/executors/quickjs-protocol.d.ts.map +1 -1
  23. package/dist/executors/quickjs-protocol.js +14 -0
  24. package/dist/executors/quickjs-protocol.js.map +1 -1
  25. package/dist/executors/quickjs-runtime.d.ts.map +1 -1
  26. package/dist/executors/quickjs-runtime.js +6 -3
  27. package/dist/executors/quickjs-runtime.js.map +1 -1
  28. package/dist/executors/quickjs.d.ts.map +1 -1
  29. package/dist/executors/quickjs.js +10 -4
  30. package/dist/executors/quickjs.js.map +1 -1
  31. package/dist/index.d.ts +27 -6
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +43 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/invocation.d.ts.map +1 -1
  36. package/dist/invocation.js +1 -4
  37. package/dist/invocation.js.map +1 -1
  38. package/dist/meta-tools.d.ts +26 -5
  39. package/dist/meta-tools.d.ts.map +1 -1
  40. package/dist/meta-tools.js +84 -40
  41. package/dist/meta-tools.js.map +1 -1
  42. package/dist/operator-ui/generated.d.ts +2 -2
  43. package/dist/operator-ui/generated.d.ts.map +1 -1
  44. package/dist/operator-ui/generated.js +2 -2
  45. package/dist/operator-ui/generated.js.map +1 -1
  46. package/dist/operator-ui/model.d.ts +2 -0
  47. package/dist/operator-ui/model.d.ts.map +1 -1
  48. package/dist/operator-ui/model.js.map +1 -1
  49. package/dist/routes/access-tokens.d.ts +7 -0
  50. package/dist/routes/access-tokens.d.ts.map +1 -0
  51. package/dist/routes/access-tokens.js +84 -0
  52. package/dist/routes/access-tokens.js.map +1 -0
  53. package/dist/routes/mcp.d.ts.map +1 -1
  54. package/dist/routes/mcp.js +8 -2
  55. package/dist/routes/mcp.js.map +1 -1
  56. package/dist/routes/shared.d.ts +11 -2
  57. package/dist/routes/shared.d.ts.map +1 -1
  58. package/dist/routes/shared.js.map +1 -1
  59. package/dist/routes/ui.d.ts.map +1 -1
  60. package/dist/routes/ui.js +9 -1
  61. package/dist/routes/ui.js.map +1 -1
  62. package/dist/server.d.ts.map +1 -1
  63. package/dist/server.js +5 -0
  64. package/dist/server.js.map +1 -1
  65. package/dist/skills.d.ts +15 -3
  66. package/dist/skills.d.ts.map +1 -1
  67. package/dist/skills.js +63 -10
  68. package/dist/skills.js.map +1 -1
  69. package/dist/storage/file.d.ts.map +1 -1
  70. package/dist/storage/file.js +5 -0
  71. package/dist/storage/file.js.map +1 -1
  72. package/dist/storage/memory.d.ts.map +1 -1
  73. package/dist/storage/memory.js +8 -0
  74. package/dist/storage/memory.js.map +1 -1
  75. package/dist/types.d.ts +19 -0
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/ui.d.ts +4 -4
  78. package/dist/ui.d.ts.map +1 -1
  79. package/dist/ui.js +44 -1
  80. package/dist/ui.js.map +1 -1
  81. package/dist/version.d.ts +1 -1
  82. package/dist/version.d.ts.map +1 -1
  83. package/dist/version.js +1 -1
  84. package/dist/version.js.map +1 -1
  85. package/package.json +2 -2
  86. package/src/access-tokens.ts +289 -0
  87. package/src/catalog-service.ts +1 -5
  88. package/src/errors.ts +28 -0
  89. package/src/execute.ts +123 -48
  90. package/src/executor-result.ts +50 -6
  91. package/src/executors/quickjs-protocol.ts +19 -0
  92. package/src/executors/quickjs-runtime.ts +6 -2
  93. package/src/executors/quickjs.ts +10 -3
  94. package/src/index.ts +85 -5
  95. package/src/invocation.ts +1 -5
  96. package/src/meta-tools.ts +116 -53
  97. package/src/operator-ui/browser.css +63 -0
  98. package/src/operator-ui/browser.ts +288 -2
  99. package/src/operator-ui/generated.ts +2 -2
  100. package/src/operator-ui/model.ts +6 -0
  101. package/src/routes/access-tokens.ts +115 -0
  102. package/src/routes/mcp.ts +8 -2
  103. package/src/routes/shared.ts +11 -1
  104. package/src/routes/ui.ts +9 -0
  105. package/src/server.ts +5 -0
  106. package/src/skills.ts +79 -9
  107. package/src/storage/file.ts +5 -0
  108. package/src/storage/memory.ts +8 -0
  109. package/src/types.ts +20 -0
  110. package/src/ui.ts +50 -1
  111. package/src/version.ts +1 -1
package/src/skills.ts CHANGED
@@ -1,8 +1,19 @@
1
- import type { Connector } from "./types.js";
1
+ import type { Connector, ConnectaSurface } from "./types.js";
2
2
 
3
3
  export const CONNECTA_INSTRUCTIONS =
4
4
  'Connecta exposes integrations behind meta-tools. Unknown address: use search_tools with 2–4 distinctive action/object terms, no initial limit, and includeSchemas="compact"; describe_tools only if that shape is ambiguous or exact JSON constraints are needed. Use call_tool for one explicitly read-only call, batch_call for 2–10 independent read-only calls, and execute_code (when available) only for dependencies, loops, joins, or substantial reduction — searching inside that one run rather than searching first. Use call_destructive_tool individually for unannotated, write-capable, or destructive tools. authorize_connector follows auth_required; get_result follows truncation. If this routing is unfamiliar, fetch skills({ name: "usage" }).';
5
5
 
6
+ /**
7
+ * The instructions a code-first deployment loads (#224). It never names
8
+ * `list_connectors`, `describe_tools`, or `batch_call` — not even to say they
9
+ * are gone. Always-loaded text describes the surface that exists; a sentence
10
+ * about three tools this deployment does not have is context paid for the past,
11
+ * and a model that names one anyway gets an unknown-tool error, which is a
12
+ * cheaper correction than the tokens the disclaimer costs every request.
13
+ */
14
+ export const CODE_FIRST_INSTRUCTIONS =
15
+ 'Connecta exposes integrations behind seven meta-tools, and execute_code is the primary one: write an async arrow function and use connecta.search (empty query browses every catalog), connecta.describe, connecta.call, and connecta.batch inside it for discovery, two or more calls, dependent steps, loops, joins, and reducing large results before they reach you. For a single read at an unknown address, search_tools with 2–4 distinctive action/object terms and includeSchemas="compact", then one call_tool — a lone cold call is cheaper direct than through a program. Use call_destructive_tool individually for unannotated, write-capable, or destructive tools; authorize_connector follows auth_required; get_result follows truncation. If this routing is unfamiliar, fetch skills({ name: "usage" }).';
16
+
6
17
  export const USAGE_SKILL = `# Connecta usage
7
18
 
8
19
  ## Choose the smallest execution tool
@@ -29,6 +40,33 @@ Connector namespace calls and \`connecta.call\` use the same read-only gate and
29
40
  Skip code mode for one call, calls suited to \`batch_call\`, or tools lacking \`readOnlyHint: true\`. Return only the needed reduction.
30
41
  `;
31
42
 
43
+ export const CODE_FIRST_USAGE_SKILL = `# Connecta usage
44
+
45
+ ## The surface
46
+
47
+ Seven tools: \`execute_code\`, \`search_tools\`, \`call_tool\`, \`call_destructive_tool\`, \`authorize_connector\`, \`get_result\`, \`skills\`. Broad discovery and multi-call work live inside a program rather than in top-level tools.
48
+
49
+ ## Choose the smallest execution tool
50
+
51
+ Use exact addresses returned by discovery; never invent one. Search with 2–4 distinctive action/object terms rather than the full request.
52
+
53
+ - One read at an unknown address: \`search_tools({ query, includeSchemas: "compact" })\`, then \`call_tool\` once. A lone cold call is cheaper direct than through a program.
54
+ - Anything wider — two or more calls, dependent steps, loops, joins, branching, browsing a whole catalog, or a result that must be reduced: one \`execute_code\` run.
55
+ - Any unannotated, write-capable, or destructive call: \`call_destructive_tool\`, individually and only after reviewing its schema and consequences. Generated code cannot make one.
56
+ - Truncated result: retry with \`fields\` when possible; otherwise page it with \`get_result\`.
57
+ - \`auth_required\`: use \`authorize_connector\`, give its recovery handoff to the operator, then retry the original call.
58
+
59
+ ## Inside a program
60
+
61
+ One async arrow function. The only capabilities are one global per connector (\`<connectorId>.<toolName>(args)\`), the four \`connecta\` functions, and \`console.log\`.
62
+
63
+ - What exists: \`connecta.search({})\` browses every catalog and \`connecta.search({ connector: "<id>" })\` browses one — that inventory is what a program discovers with, and each match carries its \`address\` and annotations.
64
+ - Exact schemas for known addresses: \`connecta.describe({ addresses: [...] })\`; \`format: "json"\` only for exact constraints.
65
+ - Two to ten independent calls: \`connecta.batch([...])\`. Each outcome is \`{ address, ok: true, data }\` or \`{ address, ok: false, error, errorDetails: { code, retryable } }\`, which is also how a program tells a policy refusal from a transient failure.
66
+ - Search inside the run rather than searching first, and return only the reduction the answer needs — never raw payloads.
67
+ - Only tools annotated \`readOnlyHint: true\` are reachable; the read-only gate, credentials, and admission are enforced below the sandbox, so nothing a program does widens what it can reach.
68
+ `;
69
+
32
70
  /**
33
71
  * Appended to USAGE_SKILL only when the deployment actually has at least one
34
72
  * connector guide. A deployment with none — every deployment that has not
@@ -41,6 +79,20 @@ export const CONNECTOR_GUIDES_SECTION = `
41
79
  Some connectors here ship their own usage guide — preferred tools, address quirks, pagination conventions, rate-limit etiquette, query patterns. \`skills({})\` lists each one as \`connector:<connectorId>\`; fetch it with \`skills({ name: "connector:<connectorId>" })\`. \`search_tools\` and \`describe_tools\` set \`guide\` on matches whose connector has one. Read a connector's guide before working with it for the first time in a task.
42
80
  `;
43
81
 
82
+ /** The same section, naming only surfaces a code-first deployment has. */
83
+ const CODE_FIRST_CONNECTOR_GUIDES_SECTION = `
84
+ ## Per-connector guides
85
+
86
+ Some connectors here ship their own usage guide — preferred tools, address quirks, pagination conventions, rate-limit etiquette, query patterns. \`skills({})\` lists each one as \`connector:<connectorId>\`; fetch it with \`skills({ name: "connector:<connectorId>" })\`. \`search_tools\`, \`connecta.search\`, and \`connecta.describe\` set \`guide\` on matches whose connector has one. Read a connector's guide before working with it for the first time in a task.
87
+ `;
88
+
89
+ /** The always-loaded MCP `instructions` string for `surface`. */
90
+ export function instructionsFor(surface: ConnectaSurface): string {
91
+ return surface === "code-first"
92
+ ? CODE_FIRST_INSTRUCTIONS
93
+ : CONNECTA_INSTRUCTIONS;
94
+ }
95
+
44
96
  /** True when at least one of `connectors` carries a usage guide. */
45
97
  export function hasConnectorGuides(connectors: readonly Connector[]): boolean {
46
98
  return connectors.some(
@@ -49,10 +101,19 @@ export function hasConnectorGuides(connectors: readonly Connector[]): boolean {
49
101
  }
50
102
 
51
103
  /** The built-in usage guide, plus the guides section when there is one to point at. */
52
- function usageSkill(connectors: readonly Connector[]): string {
53
- return hasConnectorGuides(connectors)
54
- ? USAGE_SKILL + CONNECTOR_GUIDES_SECTION
55
- : USAGE_SKILL;
104
+ function usageSkill(
105
+ connectors: readonly Connector[],
106
+ surface: ConnectaSurface,
107
+ ): string {
108
+ const base =
109
+ surface === "code-first" ? CODE_FIRST_USAGE_SKILL : USAGE_SKILL;
110
+ if (!hasConnectorGuides(connectors)) return base;
111
+ return (
112
+ base +
113
+ (surface === "code-first"
114
+ ? CODE_FIRST_CONNECTOR_GUIDES_SECTION
115
+ : CONNECTOR_GUIDES_SECTION)
116
+ );
56
117
  }
57
118
 
58
119
  const AVAILABLE_SKILLS = [
@@ -60,6 +121,8 @@ const AVAILABLE_SKILLS = [
60
121
  name: "usage",
61
122
  description:
62
123
  "How to choose among Connecta discovery, direct, batch, destructive, and code-mode tools.",
124
+ codeFirstDescription:
125
+ "How to route work between one execute_code program and Connecta's explicit call, authorization, and result tools.",
63
126
  content: usageSkill,
64
127
  },
65
128
  ] as const;
@@ -149,10 +212,14 @@ export interface SkillListing {
149
212
  * carries a usage guide. Derived from the connector list passed in — the single
150
213
  * place guide visibility is decided.
151
214
  */
152
- export function listSkills(connectors: readonly Connector[]): SkillListing[] {
215
+ export function listSkills(
216
+ connectors: readonly Connector[],
217
+ surface: ConnectaSurface = "classic",
218
+ ): SkillListing[] {
153
219
  const listing: SkillListing[] = AVAILABLE_SKILLS.map((skill) => ({
154
220
  name: skill.name,
155
- description: skill.description,
221
+ description:
222
+ surface === "code-first" ? skill.codeFirstDescription : skill.description,
156
223
  }));
157
224
  for (const connector of connectors) {
158
225
  const guide = connectorGuide(connector);
@@ -177,11 +244,14 @@ export type SkillLookup =
177
244
  export function resolveSkill(
178
245
  name: string,
179
246
  connectors: readonly Connector[],
247
+ surface: ConnectaSurface = "classic",
180
248
  ): SkillLookup {
181
249
  const builtIn = AVAILABLE_SKILLS.find((skill) => skill.name === name);
182
- if (builtIn) return { found: true, content: builtIn.content(connectors) };
250
+ if (builtIn) {
251
+ return { found: true, content: builtIn.content(connectors, surface) };
252
+ }
183
253
  const available = () =>
184
- listSkills(connectors)
254
+ listSkills(connectors, surface)
185
255
  .map((skill) => skill.name)
186
256
  .join(", ");
187
257
  if (name.startsWith(CONNECTOR_SKILL_PREFIX)) {
@@ -114,5 +114,10 @@ export function fileStorage(
114
114
  delete data[key];
115
115
  persist();
116
116
  },
117
+ async list(prefix) {
118
+ return Object.keys(data)
119
+ .filter((key) => Boolean(fresh(key)) && key.startsWith(prefix))
120
+ .sort();
121
+ },
117
122
  };
118
123
  }
@@ -32,5 +32,13 @@ export function memoryStorage(): KVStorage {
32
32
  async delete(key) {
33
33
  map.delete(key);
34
34
  },
35
+ async list(prefix) {
36
+ return [...map.keys()]
37
+ .filter((key) => {
38
+ fresh(key);
39
+ return key.startsWith(prefix) && map.has(key);
40
+ })
41
+ .sort();
42
+ },
35
43
  };
36
44
  }
package/src/types.ts CHANGED
@@ -12,6 +12,11 @@ export interface KVStorage {
12
12
  opts?: { ttlSeconds?: number },
13
13
  ): Promise<void>;
14
14
  delete(key: string): Promise<void>;
15
+ /**
16
+ * Sorted keys beginning with `prefix`. Optional for existing adapters;
17
+ * subsystems that need independent, enumerable records require it explicitly.
18
+ */
19
+ list?(prefix: string): Promise<string[]>;
15
20
  }
16
21
 
17
22
  export interface Logger {
@@ -304,6 +309,21 @@ export interface Connector {
304
309
  ): Promise<Response | null>;
305
310
  }
306
311
 
312
+ /**
313
+ * Which model-facing surface a deployment advertises. The `executor` decides
314
+ * it; this type is how a deployment overrides that.
315
+ *
316
+ * - `code-first`: seven tools, the default wherever an executor is configured.
317
+ * `list_connectors`, `describe_tools`, and `batch_call` are not top-level
318
+ * tools; their behavior lives in `connecta.search`, `connecta.describe`, and
319
+ * `connecta.batch` inside a program.
320
+ * - `classic`: the nine base meta-tools, plus `execute_code` when an executor
321
+ * is configured. Without an executor it is what a deployment necessarily
322
+ * serves and the eval gate's control arm; with one it is the ten-tool shape
323
+ * the gate's incremental arm measures, and the only thing `surface` is for.
324
+ */
325
+ export type ConnectaSurface = "classic" | "code-first";
326
+
307
327
  /** Result of one sandboxed code execution. */
308
328
  export interface ExecuteResult {
309
329
  result: unknown;
package/src/ui.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "./concurrency.js";
15
15
  import {
16
16
  type CredentialManagementCapability,
17
+ type AccessTokenManagementCapability,
17
18
  type UiConnector,
18
19
  type UiData,
19
20
  type UiTool,
@@ -32,6 +33,7 @@ import { CONNECTA_VERSION } from "./version.js";
32
33
 
33
34
  export {
34
35
  filterUiConnectors,
36
+ type AccessTokenManagementCapability,
35
37
  type CredentialManagementCapability,
36
38
  type UiConnector,
37
39
  type UiData,
@@ -265,17 +267,23 @@ export function droppedUiAuthUrls(uiAuth?: UiAuthConfig): string[] {
265
267
  ];
266
268
  }
267
269
 
268
- export type OperatorPage = "connections" | "credentials" | "activity";
270
+ export type OperatorPage =
271
+ | "connections"
272
+ | "credentials"
273
+ | "tokens"
274
+ | "activity";
269
275
 
270
276
  const OPERATOR_PAGE_LABELS: Readonly<Record<OperatorPage, string>> = {
271
277
  connections: "Connections",
272
278
  credentials: "Credentials",
279
+ tokens: "Access tokens",
273
280
  activity: "Activity",
274
281
  };
275
282
 
276
283
  export function operatorPageForPath(path: string): OperatorPage | undefined {
277
284
  if (path === "/") return "connections";
278
285
  if (path === "/credentials") return "credentials";
286
+ if (path === "/tokens") return "tokens";
279
287
  if (path === "/activity") return "activity";
280
288
  return undefined;
281
289
  }
@@ -315,6 +323,7 @@ export async function buildUiData(
315
323
  defer?: DeferredWork,
316
324
  oauthManagement = false,
317
325
  discoveryConcurrency?: number,
326
+ accessTokenManagement: AccessTokenManagementCapability = "not_configured",
318
327
  ): Promise<UiData> {
319
328
  const requestScope = {};
320
329
  const connectorSet = registry.listConnectors();
@@ -474,6 +483,7 @@ export async function buildUiData(
474
483
  connectors,
475
484
  activityEnabled,
476
485
  credentialManagement,
486
+ accessTokenManagement,
477
487
  oauthManagement,
478
488
  };
479
489
  }
@@ -583,6 +593,8 @@ ${clerkScript}
583
593
  data-operator-page="connections"${page === "connections" ? ' aria-current="page"' : ""}>Connections</a>
584
594
  <a id="credentialsNav" class="navlink hidden" href="/credentials"
585
595
  data-operator-page="credentials"${page === "credentials" ? ' aria-current="page"' : ""}>Credentials</a>
596
+ <a id="tokensNav" class="navlink hidden" href="/tokens"
597
+ data-operator-page="tokens"${page === "tokens" ? ' aria-current="page"' : ""}>Access tokens</a>
586
598
  <a id="activityNav" class="navlink hidden" href="/activity"
587
599
  data-operator-page="activity"${page === "activity" ? ' aria-current="page"' : ""}>Activity</a>
588
600
  </nav>
@@ -656,6 +668,43 @@ ${clerkScript}
656
668
  </div>
657
669
  </section>
658
670
 
671
+ <section id="tokensView"${page === "tokens" ? "" : ' class="hidden"'}>
672
+ <div class="lead pgrid">
673
+ <h1 id="tokensHeading" class="pcap" tabindex="-1">Access tokens</h1>
674
+ <div class="pbody">
675
+ <p class="activity-copy">Create named Bearer tokens for MCP clients. Each secret is shown once; revoke it when that client should lose access.</p>
676
+ <p id="tokenNotice" class="meta" role="status" aria-live="polite"
677
+ tabindex="-1"></p>
678
+ <div id="tokenUnavailable" class="unavailable hidden"></div>
679
+ <div id="tokenAvailable" class="hidden">
680
+ <form id="tokenCreateForm" class="token-create">
681
+ <label for="tokenName">Client name</label>
682
+ <div class="row">
683
+ <input id="tokenName" type="text" maxlength="80"
684
+ placeholder="Claude desktop, ChatGPT production…"
685
+ autocomplete="off">
686
+ <button id="createToken" class="linklike" type="submit">Create token</button>
687
+ </div>
688
+ </form>
689
+ <section id="tokenReveal" class="token-reveal hidden"
690
+ aria-labelledby="tokenRevealHeading">
691
+ <div class="token-reveal-head">
692
+ <h2 id="tokenRevealHeading" tabindex="-1">Copy this token now</h2>
693
+ <span class="cap">Shown once</span>
694
+ </div>
695
+ <p class="meta">Store it in the MCP client before leaving this page. It cannot be displayed again.</p>
696
+ <div class="endpoint-row token-secret">
697
+ <code id="createdToken" class="mono"></code>
698
+ <button id="copyCreatedToken" class="linklike" type="button">Copy token</button>
699
+ </div>
700
+ <button id="dismissCreatedToken" class="linklike" type="button">I stored it</button>
701
+ </section>
702
+ <div id="tokenList" class="token-ledger" aria-busy="false"></div>
703
+ </div>
704
+ </div>
705
+ </div>
706
+ </section>
707
+
659
708
  <section id="activityView"${page === "activity" ? "" : ' class="hidden"'}>
660
709
  <div class="lead pgrid">
661
710
  <h1 id="activityHeading" class="pcap" tabindex="-1">Activity</h1>
package/src/version.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.9.1";
7
+ export const CONNECTA_VERSION = "0.10.1";