@zackbart/connecta 0.10.6 → 0.12.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 (97) hide show
  1. package/AGENTS.md +8 -6
  2. package/CHANGELOG.md +80 -0
  3. package/README.md +5 -4
  4. package/bin/connecta.mjs +0 -7
  5. package/dist/activity.d.ts.map +1 -1
  6. package/dist/activity.js.map +1 -1
  7. package/dist/apps-shell.d.ts +38 -0
  8. package/dist/apps-shell.d.ts.map +1 -0
  9. package/dist/apps-shell.js +175 -0
  10. package/dist/apps-shell.js.map +1 -0
  11. package/dist/catalog-service.d.ts +2 -17
  12. package/dist/catalog-service.d.ts.map +1 -1
  13. package/dist/catalog-service.js +4 -6
  14. package/dist/catalog-service.js.map +1 -1
  15. package/dist/connectors/api.d.ts +2 -2
  16. package/dist/connectors/remote-mcp.d.ts +1 -1
  17. package/dist/errors.d.ts +1 -3
  18. package/dist/errors.d.ts.map +1 -1
  19. package/dist/errors.js +1 -1
  20. package/dist/errors.js.map +1 -1
  21. package/dist/execute.d.ts +37 -8
  22. package/dist/execute.d.ts.map +1 -1
  23. package/dist/execute.js +137 -42
  24. package/dist/execute.js.map +1 -1
  25. package/dist/executor-admission.d.ts +8 -0
  26. package/dist/executor-admission.d.ts.map +1 -1
  27. package/dist/executor-admission.js +11 -0
  28. package/dist/executor-admission.js.map +1 -1
  29. package/dist/executors/quickjs.d.ts.map +1 -1
  30. package/dist/executors/quickjs.js +2 -2
  31. package/dist/executors/quickjs.js.map +1 -1
  32. package/dist/index.d.ts +15 -31
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +23 -37
  35. package/dist/index.js.map +1 -1
  36. package/dist/invocation.js +2 -2
  37. package/dist/invocation.js.map +1 -1
  38. package/dist/meta-tools.d.ts +19 -58
  39. package/dist/meta-tools.d.ts.map +1 -1
  40. package/dist/meta-tools.js +34 -431
  41. package/dist/meta-tools.js.map +1 -1
  42. package/dist/registry.d.ts +1 -10
  43. package/dist/registry.d.ts.map +1 -1
  44. package/dist/registry.js +3 -15
  45. package/dist/registry.js.map +1 -1
  46. package/dist/routes/mcp.d.ts.map +1 -1
  47. package/dist/routes/mcp.js +67 -30
  48. package/dist/routes/mcp.js.map +1 -1
  49. package/dist/routes/shared.d.ts +5 -11
  50. package/dist/routes/shared.d.ts.map +1 -1
  51. package/dist/routes/shared.js.map +1 -1
  52. package/dist/server.js +5 -4
  53. package/dist/server.js.map +1 -1
  54. package/dist/skills.d.ts +8 -18
  55. package/dist/skills.d.ts.map +1 -1
  56. package/dist/skills.js +13 -60
  57. package/dist/skills.js.map +1 -1
  58. package/dist/types.d.ts +6 -20
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/version.d.ts +1 -1
  61. package/dist/version.js +1 -1
  62. package/documentation/code-first-exploration.md +16 -16
  63. package/documentation/code-mode.md +130 -41
  64. package/documentation/connectors.md +1 -1
  65. package/documentation/mcp-2026-07-28.md +1 -1
  66. package/documentation/mcp-ui-design.md +382 -0
  67. package/documentation/meta-tools.md +30 -43
  68. package/documentation/rich-output-design.md +4 -4
  69. package/ethos.md +7 -2
  70. package/examples/node/README.md +1 -2
  71. package/examples/node/src/index.ts +1 -3
  72. package/examples/worker/README.md +8 -13
  73. package/examples/worker/src/index.ts +6 -14
  74. package/examples/worker/wrangler.jsonc +3 -6
  75. package/package.json +1 -1
  76. package/src/activity.ts +5 -0
  77. package/src/apps-shell.ts +179 -0
  78. package/src/catalog-service.ts +6 -26
  79. package/src/connectors/api.ts +2 -2
  80. package/src/connectors/remote-mcp.ts +1 -1
  81. package/src/errors.ts +2 -2
  82. package/src/execute.ts +150 -49
  83. package/src/executor-admission.ts +12 -0
  84. package/src/executors/quickjs.ts +2 -1
  85. package/src/index.ts +40 -69
  86. package/src/invocation.ts +2 -2
  87. package/src/meta-tools.ts +38 -565
  88. package/src/registry.ts +2 -33
  89. package/src/routes/mcp.ts +79 -30
  90. package/src/routes/shared.ts +4 -11
  91. package/src/server.ts +7 -7
  92. package/src/skills.ts +11 -74
  93. package/src/types.ts +6 -21
  94. package/src/version.ts +1 -1
  95. package/templates/node/README.md +2 -1
  96. package/templates/node/package.json +1 -1
  97. package/templates/node/src/index.ts +1 -1
package/src/registry.ts CHANGED
@@ -34,8 +34,6 @@ const DEFAULT_STALE_SECONDS = 3600;
34
34
  const CATALOG_CHUNK_TTL_GRACE_SECONDS = 300;
35
35
  const DEFAULT_MAX_RESULT_BYTES = 50_000;
36
36
  const encoder = new TextEncoder();
37
- /** Independent final-envelope boundary for `batch_call`. */
38
- const DEFAULT_MAX_BATCH_RESULT_BYTES = 100_000;
39
37
 
40
38
  /**
41
39
  * Split `"<connectorId>.<toolName>"` on the first dot. Connector ids contain
@@ -189,11 +187,6 @@ export interface RegistryOptions {
189
187
  * to the default 50_000.
190
188
  */
191
189
  maxResultBytes?: number;
192
- /**
193
- * Cap on the complete serialized batch_call envelope. Must be a whole number
194
- * of bytes >= 1; anything else warns and falls back to 100_000.
195
- */
196
- maxBatchResultBytes?: number;
197
190
  }
198
191
 
199
192
  function namespaced(storage: KVStorage, prefix: string): KVStorage {
@@ -225,8 +218,6 @@ export type ConnectorOperationOptions = Pick<
225
218
  export interface RegistryView {
226
219
  /** Deployment-wide result-size cap threaded to the meta-tools. */
227
220
  readonly maxResultBytes: number;
228
- /** Independent cap for the complete serialized batch_call envelope. */
229
- readonly maxBatchResultBytes: number;
230
221
  listConnectors(): Connector[];
231
222
  getConnector(id: string): Connector | undefined;
232
223
  resolveAddress(
@@ -302,8 +293,6 @@ export class Registry implements RegistryView {
302
293
  private readonly persistToolCatalog: boolean;
303
294
  /** Result-size guard cap threaded to the meta-tools. */
304
295
  readonly maxResultBytes: number;
305
- /** Final batch envelope cap threaded to the meta-tools. */
306
- readonly maxBatchResultBytes: number;
307
296
 
308
297
  constructor(
309
298
  connectors: Connector[],
@@ -318,10 +307,6 @@ export class Registry implements RegistryView {
318
307
  opts.maxResultBytes,
319
308
  DEFAULT_MAX_RESULT_BYTES,
320
309
  );
321
- this.maxBatchResultBytes = resolveMaxResultBytes(
322
- opts.maxBatchResultBytes,
323
- DEFAULT_MAX_BATCH_RESULT_BYTES,
324
- );
325
310
  for (const c of connectors) {
326
311
  if (!ID_RE.test(c.id)) {
327
312
  throw new Error(
@@ -340,11 +325,7 @@ export class Registry implements RegistryView {
340
325
  }
341
326
  }
342
327
  this.checkConventions(opts.logger);
343
- this.checkResultCaps(
344
- opts.logger,
345
- opts.maxResultBytes,
346
- opts.maxBatchResultBytes,
347
- );
328
+ this.checkResultCaps(opts.logger, opts.maxResultBytes);
348
329
  }
349
330
 
350
331
  /**
@@ -358,7 +339,6 @@ export class Registry implements RegistryView {
358
339
  private checkResultCaps(
359
340
  logger: Logger,
360
341
  configured: number | undefined,
361
- configuredBatch: number | undefined,
362
342
  ): void {
363
343
  if (configured !== undefined && !isValidMaxResultBytes(configured)) {
364
344
  logger.warn(
@@ -368,17 +348,6 @@ export class Registry implements RegistryView {
368
348
  `default ${DEFAULT_MAX_RESULT_BYTES} instead.`,
369
349
  );
370
350
  }
371
- if (
372
- configuredBatch !== undefined &&
373
- !isValidMaxResultBytes(configuredBatch)
374
- ) {
375
- logger.warn(
376
- `[connecta] calls.maxBatchResultBytes ${configuredBatch} is not a whole ` +
377
- `number of bytes >= ${MIN_MAX_RESULT_BYTES}: it would leave the final ` +
378
- "batch envelope unbounded or serve an unusable page. Using the " +
379
- `default ${DEFAULT_MAX_BATCH_RESULT_BYTES} instead.`,
380
- );
381
- }
382
351
  for (const c of this.connectors.values()) {
383
352
  if (
384
353
  c.maxResultBytes !== undefined &&
@@ -1059,7 +1028,7 @@ export class Registry implements RegistryView {
1059
1028
  }
1060
1029
  }
1061
1030
 
1062
- /** Best-effort connector status for list_connectors. */
1031
+ /** Best-effort connector status for the operator UI. */
1063
1032
  async statusFor(
1064
1033
  id: string,
1065
1034
  baseUrl: string,
package/src/routes/mcp.ts CHANGED
@@ -5,6 +5,12 @@ import {
5
5
  WebStandardStreamableHTTPServerTransport,
6
6
  } from "@modelcontextprotocol/server";
7
7
  import type { ActivityActor, ActivityRequestContext } from "../activity.js";
8
+ import {
9
+ MCP_APPS_EXTENSION,
10
+ PROGRAM_UI_MIME_TYPE,
11
+ PROGRAM_UI_RESOURCE_URI,
12
+ PROGRAM_UI_SHELL_HTML,
13
+ } from "../apps-shell.js";
8
14
  import { registerExecuteTool } from "../execute.js";
9
15
  import {
10
16
  ExecutorAdmissionError,
@@ -184,6 +190,42 @@ function toolkitRetired(logger: Logger): Response {
184
190
  );
185
191
  }
186
192
 
193
+ /**
194
+ * U5: one static template, served by a handler that answers exactly one URI
195
+ * and fails on every other. Registering it is also what declares the
196
+ * `resources` capability — which is why `resources/list` has to answer, and
197
+ * why it answers with nothing. That is the Apps spec's permitted omission of
198
+ * UI-only resources from listing, taken exactly: the capability stays honest
199
+ * because the method answers, and nothing downstream is ever listed or
200
+ * aggregated. Widening this handler to proxy downstream templates is a
201
+ * decision (see the design record), not a diff.
202
+ */
203
+ function registerProgramUiResource(server: McpServer): void {
204
+ server.registerResource(
205
+ "connecta-program-ui",
206
+ PROGRAM_UI_RESOURCE_URI,
207
+ {
208
+ title: "connecta program view",
209
+ description:
210
+ "The MCP Apps shell that renders HTML an execute_code program handed connecta.ui.",
211
+ mimeType: PROGRAM_UI_MIME_TYPE,
212
+ },
213
+ (uri) => ({
214
+ contents: [
215
+ {
216
+ uri: uri.href,
217
+ mimeType: PROGRAM_UI_MIME_TYPE,
218
+ text: PROGRAM_UI_SHELL_HTML,
219
+ },
220
+ ],
221
+ }),
222
+ );
223
+ // The SDK's generated listing would advertise the template it just
224
+ // registered. Replace it rather than accept that: the URI reaches the host
225
+ // through tool metadata, so the listing has nothing to carry.
226
+ server.server.setRequestHandler("resources/list", () => ({ resources: [] }));
227
+ }
228
+
187
229
  async function serveMcp(
188
230
  request: Request,
189
231
  opts: ServerOptions,
@@ -192,13 +234,26 @@ async function serveMcp(
192
234
  registry: RegistryView,
193
235
  runtimeContext?: RuntimeExecutionContext,
194
236
  ): Promise<Response> {
195
- // One deployment-wide value, read once here so the instructions, the
196
- // registered tools, and the guidance the `skills` tool serves cannot
197
- // disagree about which surface this deployment advertises.
198
- const surface = opts.surface ?? "classic";
199
237
  const createServer = (): McpServer => {
200
238
  const server = new McpServer(opts.serverInfo, {
201
- instructions: instructionsFor(surface),
239
+ instructions: instructionsFor(),
240
+ // U11: the Apps extension must be explicitly negotiated, and a
241
+ // conforming client acts on an extension only when both sides declare
242
+ // it — without this line no host reads execute_code's _meta.ui, no host
243
+ // fetches the shell, and the whole design is inert. This is the one
244
+ // extension connecta advertises; the versioned extensions framework
245
+ // stays declined as a general surface (documentation/mcp-2026-07-28.md).
246
+ capabilities: {
247
+ extensions: {
248
+ [MCP_APPS_EXTENSION]: { mimeTypes: [PROGRAM_UI_MIME_TYPE] },
249
+ },
250
+ // Registering the shell below declares `resources` on its own, but it
251
+ // would default `listChanged` to true. Connecta serves one build-time
252
+ // template and never sends a list_changed notification, so say so:
253
+ // a client that subscribes on the strength of that flag would wait
254
+ // forever for an event this server has no way to produce.
255
+ resources: { listChanged: false },
256
+ },
202
257
  cacheHints: {
203
258
  "tools/list": {
204
259
  ttlMs: 3_600_000,
@@ -206,6 +261,7 @@ async function serveMcp(
206
261
  },
207
262
  },
208
263
  });
264
+ registerProgramUiResource(server);
209
265
  const activity: ActivityRequestContext | undefined = opts.activity
210
266
  ? {
211
267
  sink: opts.activity,
@@ -223,7 +279,6 @@ async function serveMcp(
223
279
  : undefined;
224
280
  registerMetaTools(server, registry, {
225
281
  baseUrl,
226
- surface,
227
282
  ...(activity ? { activity } : {}),
228
283
  ...(opts.defaultToolTimeoutMs !== undefined
229
284
  ? { defaultToolTimeoutMs: opts.defaultToolTimeoutMs }
@@ -235,32 +290,26 @@ async function serveMcp(
235
290
  ? { discoveryConcurrency: opts.discoveryConcurrency }
236
291
  : {}),
237
292
  requestSignal: request.signal,
238
- ...(runtimeContext
239
- ? { defer: runtimeContext.waitUntil.bind(runtimeContext) }
293
+ });
294
+ registerExecuteTool(server, registry, {
295
+ baseUrl,
296
+ executor: opts.executor,
297
+ logger: opts.logger,
298
+ ...(activity ? { activity } : {}),
299
+ requestSignal: request.signal,
300
+ ...(opts.discoveryConcurrency !== undefined
301
+ ? { discoveryConcurrency: opts.discoveryConcurrency }
302
+ : {}),
303
+ ...(opts.probeTimeoutMs !== undefined
304
+ ? { probeTimeoutMs: opts.probeTimeoutMs }
305
+ : {}),
306
+ ...(opts.maxEmittedBytes !== undefined
307
+ ? { maxEmittedBytes: opts.maxEmittedBytes }
308
+ : {}),
309
+ ...(opts.maxEmittedBlocks !== undefined
310
+ ? { maxEmittedBlocks: opts.maxEmittedBlocks }
240
311
  : {}),
241
312
  });
242
- if (opts.executor) {
243
- registerExecuteTool(server, registry, {
244
- baseUrl,
245
- surface,
246
- executor: opts.executor,
247
- logger: opts.logger,
248
- ...(activity ? { activity } : {}),
249
- requestSignal: request.signal,
250
- ...(opts.discoveryConcurrency !== undefined
251
- ? { discoveryConcurrency: opts.discoveryConcurrency }
252
- : {}),
253
- ...(opts.probeTimeoutMs !== undefined
254
- ? { probeTimeoutMs: opts.probeTimeoutMs }
255
- : {}),
256
- ...(opts.maxEmittedBytes !== undefined
257
- ? { maxEmittedBytes: opts.maxEmittedBytes }
258
- : {}),
259
- ...(opts.maxEmittedBlocks !== undefined
260
- ? { maxEmittedBlocks: opts.maxEmittedBlocks }
261
- : {}),
262
- });
263
- }
264
313
  return server;
265
314
  };
266
315
 
@@ -7,7 +7,6 @@ import type { AdmissionController } from "../executor-admission.js";
7
7
  import type { Registry } from "../registry.js";
8
8
  import type {
9
9
  ConnectaBranding,
10
- ConnectaSurface,
11
10
  Executor,
12
11
  InboundAuth,
13
12
  Logger,
@@ -26,9 +25,9 @@ export interface ServerOptions {
26
25
  activityReadGate?: ActivityReadGate;
27
26
  activityDeploymentId?: string;
28
27
  deploymentInfo?: Record<string, unknown>;
29
- /** Deadline for call_tool/batch_call calls that pass no timeoutMs. Off when unset. */
28
+ /** Deadline for call_tool/call_destructive_tool calls that pass no timeoutMs. Off when unset. */
30
29
  defaultToolTimeoutMs?: number;
31
- /** Per-connector deadline for the list/search/describe probe fan-out. Default 30_000. */
30
+ /** Per-connector deadline for the search/describe probe fan-out. Default 30_000. */
32
31
  probeTimeoutMs?: number;
33
32
  /** Maximum simultaneous connector discovery operations. Default 4. */
34
33
  discoveryConcurrency?: number;
@@ -36,14 +35,8 @@ export interface ServerOptions {
36
35
  maxEmittedBytes?: number;
37
36
  /** Block-count budget for connecta.emit per run. Default 32. */
38
37
  maxEmittedBlocks?: number;
39
- /** When set, the execute_code meta-tool is registered on top of the base surface. */
40
- executor?: Executor;
41
- /**
42
- * The advertised model-facing surface. createConnecta() always resolves it
43
- * from the executor; absent (a direct createFetchHandler() caller) is
44
- * classic, and `code-first` is only ever set alongside an `executor`.
45
- */
46
- surface?: ConnectaSurface;
38
+ /** Required sandbox backing the execute_code meta-tool. */
39
+ executor: Executor;
47
40
  /** Global FIFO boundary for all non-preflight `/mcp` requests. */
48
41
  requestAdmission: AdmissionController;
49
42
  /** Encrypted connector-credential storage backing the Credentials page. */
package/src/server.ts CHANGED
@@ -122,10 +122,12 @@ export function createFetchHandler(
122
122
  }
123
123
 
124
124
  if (path === "/health") {
125
- const codeAdmission =
126
- opts.executor && isAdmittingExecutor(opts.executor)
127
- ? opts.executor.admissionSnapshot?.()
128
- : undefined;
125
+ // The executor is required, so code admission always has a shape to
126
+ // report: either the executor's own pool or the fallback controller
127
+ // wrapped around it at construction.
128
+ const codeAdmission = isAdmittingExecutor(opts.executor)
129
+ ? opts.executor.admissionSnapshot?.()
130
+ : undefined;
129
131
  return Response.json({
130
132
  status: "ok",
131
133
  connectors: registry.listConnectors().length,
@@ -133,9 +135,7 @@ export function createFetchHandler(
133
135
  admission: {
134
136
  policy: "global-fifo",
135
137
  requests: opts.requestAdmission.snapshot(),
136
- code: opts.executor
137
- ? (codeAdmission ?? { managedByExecutor: true })
138
- : null,
138
+ code: codeAdmission ?? { managedByExecutor: true },
139
139
  downstreamCalls: {
140
140
  policy: "connector-partitioned-per-runtime",
141
141
  connectors: registry.callAdmissionSnapshot(),
package/src/skills.ts CHANGED
@@ -1,47 +1,10 @@
1
- import type { Connector, ConnectaSurface } from "./types.js";
1
+ import type { Connector } from "./types.js";
2
2
 
3
3
  export const CONNECTA_INSTRUCTIONS =
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
-
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
4
  '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
5
 
17
6
  export const USAGE_SKILL = `# Connecta usage
18
7
 
19
- ## Choose the smallest execution tool
20
-
21
- Use exact addresses returned by discovery; never invent one. Search with 2–4 distinctive action/object terms rather than the full request, and omit \`limit\` initially so the default page stays small.
22
-
23
- - Unknown address: \`search_tools({ query, includeSchemas: "compact" })\`; every match then includes its input shape plus any declared output shape and annotations.
24
- - Compact shape still ambiguous: \`describe_tools({ addresses: [...] })\`; use \`format: "json"\` only for exact constraints.
25
- - One explicitly read-only call: \`call_tool\`.
26
- - Two to ten independent explicitly read-only calls: \`batch_call\`.
27
- - Dependent read-only calls, loops, joins, branching, or large-result reduction: \`execute_code\` when available.
28
- - Any unannotated, write-capable, or destructive call: \`call_destructive_tool\`, individually and only after reviewing its schema and consequences.
29
- - Truncated result: retry with \`fields\` when possible; otherwise page it with \`get_result\`.
30
- - \`auth_required\`: use \`authorize_connector\`, give its recovery handoff to the operator, then retry the original call.
31
-
32
- Use \`list_connectors({ probe: false })\` for a fast observed-health inventory; use \`probe: true\` only to diagnose live health or authorization.
33
-
34
- ## Code mode
35
-
36
- Unknown addresses plus dependent calls: search inside the run, not in an outer \`search_tools\`. Parallelize independent calls with \`Promise.all\` or \`connecta.batch\`.
37
-
38
- Connector namespace calls and \`connecta.call\` use the same read-only gate and throw on downstream errors. Catch only failures the workflow can handle; let authorization failures return to the agent for recovery.
39
-
40
- Skip code mode for one call, calls suited to \`batch_call\`, or tools lacking \`readOnlyHint: true\`. Return only the needed reduction.
41
- `;
42
-
43
- export const CODE_FIRST_USAGE_SKILL = `# Connecta usage
44
-
45
8
  ## The surface
46
9
 
47
10
  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.
@@ -76,21 +39,12 @@ One async arrow function. The only capabilities are one global per connector (\`
76
39
  export const CONNECTOR_GUIDES_SECTION = `
77
40
  ## Per-connector guides
78
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.
80
- `;
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
42
  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
43
  `;
88
44
 
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;
45
+ /** The always-loaded MCP `instructions` string. */
46
+ export function instructionsFor(): string {
47
+ return CONNECTA_INSTRUCTIONS;
94
48
  }
95
49
 
96
50
  /** True when at least one of `connectors` carries a usage guide. */
@@ -101,27 +55,15 @@ export function hasConnectorGuides(connectors: readonly Connector[]): boolean {
101
55
  }
102
56
 
103
57
  /** The built-in usage guide, plus the guides section when there is one to point at. */
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
- );
58
+ function usageSkill(connectors: readonly Connector[]): string {
59
+ if (!hasConnectorGuides(connectors)) return USAGE_SKILL;
60
+ return USAGE_SKILL + CONNECTOR_GUIDES_SECTION;
117
61
  }
118
62
 
119
63
  const AVAILABLE_SKILLS = [
120
64
  {
121
65
  name: "usage",
122
66
  description:
123
- "How to choose among Connecta discovery, direct, batch, destructive, and code-mode tools.",
124
- codeFirstDescription:
125
67
  "How to route work between one execute_code program and Connecta's explicit call, authorization, and result tools.",
126
68
  content: usageSkill,
127
69
  },
@@ -212,14 +154,10 @@ export interface SkillListing {
212
154
  * carries a usage guide. Derived from the connector list passed in — the single
213
155
  * place guide visibility is decided.
214
156
  */
215
- export function listSkills(
216
- connectors: readonly Connector[],
217
- surface: ConnectaSurface = "classic",
218
- ): SkillListing[] {
157
+ export function listSkills(connectors: readonly Connector[]): SkillListing[] {
219
158
  const listing: SkillListing[] = AVAILABLE_SKILLS.map((skill) => ({
220
159
  name: skill.name,
221
- description:
222
- surface === "code-first" ? skill.codeFirstDescription : skill.description,
160
+ description: skill.description,
223
161
  }));
224
162
  for (const connector of connectors) {
225
163
  const guide = connectorGuide(connector);
@@ -244,14 +182,13 @@ export type SkillLookup =
244
182
  export function resolveSkill(
245
183
  name: string,
246
184
  connectors: readonly Connector[],
247
- surface: ConnectaSurface = "classic",
248
185
  ): SkillLookup {
249
186
  const builtIn = AVAILABLE_SKILLS.find((skill) => skill.name === name);
250
187
  if (builtIn) {
251
- return { found: true, content: builtIn.content(connectors, surface) };
188
+ return { found: true, content: builtIn.content(connectors) };
252
189
  }
253
190
  const available = () =>
254
- listSkills(connectors, surface)
191
+ listSkills(connectors)
255
192
  .map((skill) => skill.name)
256
193
  .join(", ");
257
194
  if (name.startsWith(CONNECTOR_SKILL_PREFIX)) {
package/src/types.ts CHANGED
@@ -35,8 +35,8 @@ export interface ToolDef {
35
35
  /**
36
36
  * Standard MCP tool behavior hints plus provider-specific extensions.
37
37
  * Connecta fails closed: only readOnlyHint === true (without a contradictory
38
- * destructiveHint) may use call_tool, batch_call, or execute_code. Every
39
- * other tool must cross the call_destructive_tool approval boundary.
38
+ * destructiveHint) may use call_tool or execute_code. Every other tool must
39
+ * cross the call_destructive_tool approval boundary.
40
40
  */
41
41
  annotations?: ToolAnnotations;
42
42
  }
@@ -198,7 +198,7 @@ export interface Connector {
198
198
  description?: string;
199
199
  /**
200
200
  * Max inline result size (bytes) for this connector's tools before
201
- * call_tool/batch_call truncate and stash the full text for get_result
201
+ * call_tool truncates and stashes the full text for get_result
202
202
  * paging. Overrides `ConnectaConfig.calls.maxResultBytes`;
203
203
  * omit to inherit it (which itself defaults to 50_000). Must be a whole
204
204
  * number of bytes >= 1; anything else warns at startup and is ignored, so
@@ -207,8 +207,8 @@ export interface Connector {
207
207
  maxResultBytes?: number;
208
208
  /**
209
209
  * Optional per-runtime admission policy for downstream tool calls. It covers
210
- * call_tool, every batch_call child, and execute_code host calls, but not
211
- * catalog/status/auth operations.
210
+ * call_tool, call_destructive_tool, and every execute_code host call, but
211
+ * not catalog/status/auth operations.
212
212
  */
213
213
  callAdmission?: ConnectorCallAdmissionPolicy;
214
214
  /**
@@ -255,7 +255,7 @@ export interface Connector {
255
255
  * request-local reuse remains in force until the request boundary.
256
256
  */
257
257
  closeScope?(ctx: ConnectorContext): Promise<void>;
258
- /** Optional connector-level health/auth status for list_connectors. */
258
+ /** Optional connector-level health/auth status for the operator UI. */
259
259
  status?(ctx: ConnectorContext): Promise<ConnectorStatus>;
260
260
  /**
261
261
  * Optional: start (or with force, restart from scratch) a downstream OAuth
@@ -309,21 +309,6 @@ export interface Connector {
309
309
  ): Promise<Response | null>;
310
310
  }
311
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
-
327
312
  /** Result of one sandboxed code execution. */
328
313
  export interface ExecuteResult {
329
314
  result: unknown;
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.10.6";
7
+ export const CONNECTA_VERSION = "0.12.0";
@@ -13,7 +13,8 @@ Then point an MCP client at `http://localhost:8787/mcp` with
13
13
  ## Deployment contract
14
14
 
15
15
  - Edit `src/index.ts` for connectors, auth, storage, and the public URL.
16
- - Keep `executor: quickJsExecutor()` for the seven-tool code-first surface.
16
+ - Keep the required `executor: quickJsExecutor()` configuration; a deployment
17
+ without an executor refuses to boot.
17
18
  - Keep secrets in environment variables or an external secret store.
18
19
  - Add application code only inside deliberate `api()` connector handlers.
19
20
  - Do not copy Connecta package internals into this deployment.
@@ -12,7 +12,7 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@zackbart/connecta": "0.10.6",
15
+ "@zackbart/connecta": "0.12.0",
16
16
  "quickjs-emscripten": "0.32.0"
17
17
  },
18
18
  "devDependencies": {
@@ -21,7 +21,7 @@ const connecta = createConnecta({
21
21
  storage: fileStorage("./.connecta-state.json"),
22
22
  auth: bearerToken(token, { subjectId: "operator" }),
23
23
  publicUrl: `http://localhost:${port}`,
24
- // Keep this for the prescribed seven-tool code-first surface.
24
+ // Required: model-written programs run in a bounded QuickJS child.
25
25
  executor: quickJsExecutor(),
26
26
  connectors: [
27
27
  api("time", {