@topolo/mcp 0.13.3 → 0.14.2

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/README.md CHANGED
@@ -145,6 +145,7 @@ deletion, encryption, and audit storage for the data they own.
145
145
  | `topolo_prepare_action` | credential-scoped | no |
146
146
  | `topolo_read_action` | catalog or target policy | no |
147
147
  | `topolo_call_action` | catalog or target policy | yes |
148
+ | `topolo_run_agent` | Agent runtime policy | yes |
148
149
  | `topolo_upload_action` | catalog or target policy | yes |
149
150
  | `topolo_list_workspaces` | target app runtime policy | no |
150
151
  | `topolo_create_workspace` | target app runtime policy | yes |
@@ -194,6 +195,13 @@ personal or organization context. This lets agents address many workspaces
194
195
  independently; providing only one of the pair is rejected before the request is
195
196
  sent.
196
197
 
198
+ `topolo_run_agent` is the bounded external-harness path for Codex, Claude, and
199
+ other MCP clients. It creates or replays one invocation using the caller's
200
+ stable `idempotencyKey`, polls the same invocation, and returns either its
201
+ terminal result or `state: "approval_required"` with the exact Topolo
202
+ verification URL. Calling it again with the same input resumes the durable
203
+ invocation instead of starting duplicate work.
204
+
197
205
  ## Safety rails
198
206
 
199
207
  - **No `orgId` parameter** on any tool. The organization is derived entirely
package/dist/dispatch.js CHANGED
@@ -65,6 +65,8 @@ function publicMcpErrorMessage(error, fallback) {
65
65
 
66
66
  // src/tools.ts
67
67
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
68
+ var AGENT_INVOCATIONS_CREATE = "app_topolo_agent.agent_invocations.create";
69
+ var AGENT_INVOCATIONS_GET = "app_topolo_agent.agent_invocations.get";
68
70
  function requiredString(args, name) {
69
71
  const value = typeof args[name] === "string" ? args[name].trim() : "";
70
72
  if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
@@ -104,6 +106,40 @@ function coerceObjectParam(value, name) {
104
106
  }
105
107
  return candidate;
106
108
  }
109
+ function recordValue(value) {
110
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
111
+ }
112
+ function invocationFrom(value) {
113
+ const invocation = recordValue(recordValue(value)?.["invocation"]);
114
+ if (!invocation || typeof invocation["invocationId"] !== "string") {
115
+ throw new TopoloMcpPublicError("Topolo Agent returned an invalid invocation response.");
116
+ }
117
+ return invocation;
118
+ }
119
+ function agentRunResult(invocation) {
120
+ const status = typeof invocation["status"] === "string" ? invocation["status"] : "running";
121
+ const terminal = invocation["terminal"] === true;
122
+ const approval = recordValue(invocation["approval"]);
123
+ if (status === "awaiting_approval" && approval) {
124
+ return {
125
+ state: "approval_required",
126
+ invocationId: invocation["invocationId"],
127
+ verificationUrl: approval["verificationUrl"],
128
+ continuation: {
129
+ tool: "topolo_run_agent",
130
+ reuseSameInvocation: true,
131
+ arguments: { waitForApproval: true, waitSeconds: 120 }
132
+ },
133
+ approval,
134
+ invocation
135
+ };
136
+ }
137
+ return {
138
+ state: terminal ? status : "running",
139
+ invocationId: invocation["invocationId"],
140
+ invocation
141
+ };
142
+ }
107
143
  var TOOLS = [
108
144
  {
109
145
  name: "topolo_whoami",
@@ -351,6 +387,70 @@ var TOOLS = [
351
387
  );
352
388
  }
353
389
  },
390
+ {
391
+ name: "topolo_run_agent",
392
+ title: "Run a Topolo agent to a durable checkpoint",
393
+ description: "Creates or replays one idempotent Topolo Agent invocation, polls its published status, and returns either the terminal result or an approval_required handoff with the exact Topolo verification URL. Reusing the same idempotencyKey never starts a duplicate invocation. After showing the verification URL, immediately call this tool again with the same invocation and waitForApproval=true so the terminal result returns here.",
394
+ requiredScopes: [],
395
+ destructive: true,
396
+ inputSchema: {
397
+ type: "object",
398
+ properties: {
399
+ invocation: {
400
+ type: "object",
401
+ description: "Input for app_topolo_agent.agent_invocations.create, including a stable idempotencyKey.",
402
+ additionalProperties: true
403
+ },
404
+ resourceType: { type: "string", description: "Optional resource-scoping alias for the Agent workspace." },
405
+ resourceId: { type: "string", description: "Resource id. Required when resourceType is provided." },
406
+ waitSeconds: {
407
+ type: "integer",
408
+ minimum: 0,
409
+ maximum: 120,
410
+ default: 30,
411
+ description: "Maximum time to poll. Returns state=running when the bounded wait elapses."
412
+ },
413
+ waitForApproval: {
414
+ type: "boolean",
415
+ default: false,
416
+ description: "Set only when resuming an approval_required handoff already shown to the human. Keeps polling the same idempotent invocation while approval is pending."
417
+ }
418
+ },
419
+ required: ["invocation"],
420
+ additionalProperties: false
421
+ },
422
+ handler: async (topolo, args) => {
423
+ const invocationInput = coerceObjectParam(args["invocation"], "invocation");
424
+ const idempotencyKey = typeof invocationInput["idempotencyKey"] === "string" ? invocationInput["idempotencyKey"].trim() : "";
425
+ if (!idempotencyKey) {
426
+ throw new TopoloMcpPublicError("invocation.idempotencyKey is required.");
427
+ }
428
+ const resource = optionalResourceContext(args);
429
+ const waitSeconds = typeof args["waitSeconds"] === "number" ? Math.max(0, Math.min(120, Math.floor(args["waitSeconds"]))) : 30;
430
+ const waitForApproval = args["waitForApproval"] === true;
431
+ const createResult = await topolo.client.callAction(
432
+ AGENT_INVOCATIONS_CREATE,
433
+ invocationInput,
434
+ { confirm: true, ...resource ? { resource } : {} }
435
+ );
436
+ let invocation = invocationFrom(createResult);
437
+ const deadline = Date.now() + waitSeconds * 1e3;
438
+ while (invocation["terminal"] !== true && (invocation["status"] !== "awaiting_approval" || waitForApproval) && Date.now() < deadline) {
439
+ const recommended = typeof invocation["recommendedPollAfterSeconds"] === "number" ? invocation["recommendedPollAfterSeconds"] : 2;
440
+ const delayMs = Math.min(
441
+ Math.max(250, recommended * 1e3),
442
+ Math.max(0, deadline - Date.now())
443
+ );
444
+ if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
445
+ invocation = invocationFrom(await topolo.client.readAction(
446
+ AGENT_INVOCATIONS_GET,
447
+ { invocationId: invocation["invocationId"] },
448
+ resource ? { resource } : {}
449
+ ));
450
+ }
451
+ return agentRunResult(invocation);
452
+ }
453
+ },
354
454
  {
355
455
  name: "topolo_validate_action",
356
456
  title: "Validate Topolo action input",
package/dist/http.js CHANGED
@@ -17792,6 +17792,8 @@ function splitService(servicePart) {
17792
17792
 
17793
17793
  // src/tools.ts
17794
17794
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
17795
+ var AGENT_INVOCATIONS_CREATE = "app_topolo_agent.agent_invocations.create";
17796
+ var AGENT_INVOCATIONS_GET = "app_topolo_agent.agent_invocations.get";
17795
17797
  function requiredString(args, name) {
17796
17798
  const value = typeof args[name] === "string" ? args[name].trim() : "";
17797
17799
  if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
@@ -17831,6 +17833,40 @@ function coerceObjectParam(value, name) {
17831
17833
  }
17832
17834
  return candidate;
17833
17835
  }
17836
+ function recordValue(value) {
17837
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
17838
+ }
17839
+ function invocationFrom(value) {
17840
+ const invocation = recordValue(recordValue(value)?.["invocation"]);
17841
+ if (!invocation || typeof invocation["invocationId"] !== "string") {
17842
+ throw new TopoloMcpPublicError("Topolo Agent returned an invalid invocation response.");
17843
+ }
17844
+ return invocation;
17845
+ }
17846
+ function agentRunResult(invocation) {
17847
+ const status = typeof invocation["status"] === "string" ? invocation["status"] : "running";
17848
+ const terminal = invocation["terminal"] === true;
17849
+ const approval = recordValue(invocation["approval"]);
17850
+ if (status === "awaiting_approval" && approval) {
17851
+ return {
17852
+ state: "approval_required",
17853
+ invocationId: invocation["invocationId"],
17854
+ verificationUrl: approval["verificationUrl"],
17855
+ continuation: {
17856
+ tool: "topolo_run_agent",
17857
+ reuseSameInvocation: true,
17858
+ arguments: { waitForApproval: true, waitSeconds: 120 }
17859
+ },
17860
+ approval,
17861
+ invocation
17862
+ };
17863
+ }
17864
+ return {
17865
+ state: terminal ? status : "running",
17866
+ invocationId: invocation["invocationId"],
17867
+ invocation
17868
+ };
17869
+ }
17834
17870
  var TOOLS = [
17835
17871
  {
17836
17872
  name: "topolo_whoami",
@@ -18078,6 +18114,70 @@ var TOOLS = [
18078
18114
  );
18079
18115
  }
18080
18116
  },
18117
+ {
18118
+ name: "topolo_run_agent",
18119
+ title: "Run a Topolo agent to a durable checkpoint",
18120
+ description: "Creates or replays one idempotent Topolo Agent invocation, polls its published status, and returns either the terminal result or an approval_required handoff with the exact Topolo verification URL. Reusing the same idempotencyKey never starts a duplicate invocation. After showing the verification URL, immediately call this tool again with the same invocation and waitForApproval=true so the terminal result returns here.",
18121
+ requiredScopes: [],
18122
+ destructive: true,
18123
+ inputSchema: {
18124
+ type: "object",
18125
+ properties: {
18126
+ invocation: {
18127
+ type: "object",
18128
+ description: "Input for app_topolo_agent.agent_invocations.create, including a stable idempotencyKey.",
18129
+ additionalProperties: true
18130
+ },
18131
+ resourceType: { type: "string", description: "Optional resource-scoping alias for the Agent workspace." },
18132
+ resourceId: { type: "string", description: "Resource id. Required when resourceType is provided." },
18133
+ waitSeconds: {
18134
+ type: "integer",
18135
+ minimum: 0,
18136
+ maximum: 120,
18137
+ default: 30,
18138
+ description: "Maximum time to poll. Returns state=running when the bounded wait elapses."
18139
+ },
18140
+ waitForApproval: {
18141
+ type: "boolean",
18142
+ default: false,
18143
+ description: "Set only when resuming an approval_required handoff already shown to the human. Keeps polling the same idempotent invocation while approval is pending."
18144
+ }
18145
+ },
18146
+ required: ["invocation"],
18147
+ additionalProperties: false
18148
+ },
18149
+ handler: async (topolo, args) => {
18150
+ const invocationInput = coerceObjectParam(args["invocation"], "invocation");
18151
+ const idempotencyKey = typeof invocationInput["idempotencyKey"] === "string" ? invocationInput["idempotencyKey"].trim() : "";
18152
+ if (!idempotencyKey) {
18153
+ throw new TopoloMcpPublicError("invocation.idempotencyKey is required.");
18154
+ }
18155
+ const resource = optionalResourceContext(args);
18156
+ const waitSeconds = typeof args["waitSeconds"] === "number" ? Math.max(0, Math.min(120, Math.floor(args["waitSeconds"]))) : 30;
18157
+ const waitForApproval = args["waitForApproval"] === true;
18158
+ const createResult = await topolo.client.callAction(
18159
+ AGENT_INVOCATIONS_CREATE,
18160
+ invocationInput,
18161
+ { confirm: true, ...resource ? { resource } : {} }
18162
+ );
18163
+ let invocation = invocationFrom(createResult);
18164
+ const deadline = Date.now() + waitSeconds * 1e3;
18165
+ while (invocation["terminal"] !== true && (invocation["status"] !== "awaiting_approval" || waitForApproval) && Date.now() < deadline) {
18166
+ const recommended = typeof invocation["recommendedPollAfterSeconds"] === "number" ? invocation["recommendedPollAfterSeconds"] : 2;
18167
+ const delayMs = Math.min(
18168
+ Math.max(250, recommended * 1e3),
18169
+ Math.max(0, deadline - Date.now())
18170
+ );
18171
+ if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
18172
+ invocation = invocationFrom(await topolo.client.readAction(
18173
+ AGENT_INVOCATIONS_GET,
18174
+ { invocationId: invocation["invocationId"] },
18175
+ resource ? { resource } : {}
18176
+ ));
18177
+ }
18178
+ return agentRunResult(invocation);
18179
+ }
18180
+ },
18081
18181
  {
18082
18182
  name: "topolo_validate_action",
18083
18183
  title: "Validate Topolo action input",
package/dist/index.js CHANGED
@@ -4154,6 +4154,8 @@ function publicMcpErrorMessage(error, fallback) {
4154
4154
 
4155
4155
  // src/tools.ts
4156
4156
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
4157
+ var AGENT_INVOCATIONS_CREATE = "app_topolo_agent.agent_invocations.create";
4158
+ var AGENT_INVOCATIONS_GET = "app_topolo_agent.agent_invocations.get";
4157
4159
  function requiredString(args, name) {
4158
4160
  const value = typeof args[name] === "string" ? args[name].trim() : "";
4159
4161
  if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
@@ -4193,6 +4195,40 @@ function coerceObjectParam(value, name) {
4193
4195
  }
4194
4196
  return candidate;
4195
4197
  }
4198
+ function recordValue(value) {
4199
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
4200
+ }
4201
+ function invocationFrom(value) {
4202
+ const invocation = recordValue(recordValue(value)?.["invocation"]);
4203
+ if (!invocation || typeof invocation["invocationId"] !== "string") {
4204
+ throw new TopoloMcpPublicError("Topolo Agent returned an invalid invocation response.");
4205
+ }
4206
+ return invocation;
4207
+ }
4208
+ function agentRunResult(invocation) {
4209
+ const status = typeof invocation["status"] === "string" ? invocation["status"] : "running";
4210
+ const terminal = invocation["terminal"] === true;
4211
+ const approval = recordValue(invocation["approval"]);
4212
+ if (status === "awaiting_approval" && approval) {
4213
+ return {
4214
+ state: "approval_required",
4215
+ invocationId: invocation["invocationId"],
4216
+ verificationUrl: approval["verificationUrl"],
4217
+ continuation: {
4218
+ tool: "topolo_run_agent",
4219
+ reuseSameInvocation: true,
4220
+ arguments: { waitForApproval: true, waitSeconds: 120 }
4221
+ },
4222
+ approval,
4223
+ invocation
4224
+ };
4225
+ }
4226
+ return {
4227
+ state: terminal ? status : "running",
4228
+ invocationId: invocation["invocationId"],
4229
+ invocation
4230
+ };
4231
+ }
4196
4232
  var TOOLS = [
4197
4233
  {
4198
4234
  name: "topolo_whoami",
@@ -4440,6 +4476,70 @@ var TOOLS = [
4440
4476
  );
4441
4477
  }
4442
4478
  },
4479
+ {
4480
+ name: "topolo_run_agent",
4481
+ title: "Run a Topolo agent to a durable checkpoint",
4482
+ description: "Creates or replays one idempotent Topolo Agent invocation, polls its published status, and returns either the terminal result or an approval_required handoff with the exact Topolo verification URL. Reusing the same idempotencyKey never starts a duplicate invocation. After showing the verification URL, immediately call this tool again with the same invocation and waitForApproval=true so the terminal result returns here.",
4483
+ requiredScopes: [],
4484
+ destructive: true,
4485
+ inputSchema: {
4486
+ type: "object",
4487
+ properties: {
4488
+ invocation: {
4489
+ type: "object",
4490
+ description: "Input for app_topolo_agent.agent_invocations.create, including a stable idempotencyKey.",
4491
+ additionalProperties: true
4492
+ },
4493
+ resourceType: { type: "string", description: "Optional resource-scoping alias for the Agent workspace." },
4494
+ resourceId: { type: "string", description: "Resource id. Required when resourceType is provided." },
4495
+ waitSeconds: {
4496
+ type: "integer",
4497
+ minimum: 0,
4498
+ maximum: 120,
4499
+ default: 30,
4500
+ description: "Maximum time to poll. Returns state=running when the bounded wait elapses."
4501
+ },
4502
+ waitForApproval: {
4503
+ type: "boolean",
4504
+ default: false,
4505
+ description: "Set only when resuming an approval_required handoff already shown to the human. Keeps polling the same idempotent invocation while approval is pending."
4506
+ }
4507
+ },
4508
+ required: ["invocation"],
4509
+ additionalProperties: false
4510
+ },
4511
+ handler: async (topolo, args) => {
4512
+ const invocationInput = coerceObjectParam(args["invocation"], "invocation");
4513
+ const idempotencyKey = typeof invocationInput["idempotencyKey"] === "string" ? invocationInput["idempotencyKey"].trim() : "";
4514
+ if (!idempotencyKey) {
4515
+ throw new TopoloMcpPublicError("invocation.idempotencyKey is required.");
4516
+ }
4517
+ const resource = optionalResourceContext(args);
4518
+ const waitSeconds = typeof args["waitSeconds"] === "number" ? Math.max(0, Math.min(120, Math.floor(args["waitSeconds"]))) : 30;
4519
+ const waitForApproval = args["waitForApproval"] === true;
4520
+ const createResult = await topolo.client.callAction(
4521
+ AGENT_INVOCATIONS_CREATE,
4522
+ invocationInput,
4523
+ { confirm: true, ...resource ? { resource } : {} }
4524
+ );
4525
+ let invocation = invocationFrom(createResult);
4526
+ const deadline = Date.now() + waitSeconds * 1e3;
4527
+ while (invocation["terminal"] !== true && (invocation["status"] !== "awaiting_approval" || waitForApproval) && Date.now() < deadline) {
4528
+ const recommended = typeof invocation["recommendedPollAfterSeconds"] === "number" ? invocation["recommendedPollAfterSeconds"] : 2;
4529
+ const delayMs = Math.min(
4530
+ Math.max(250, recommended * 1e3),
4531
+ Math.max(0, deadline - Date.now())
4532
+ );
4533
+ if (delayMs > 0) await new Promise((resolve2) => setTimeout(resolve2, delayMs));
4534
+ invocation = invocationFrom(await topolo.client.readAction(
4535
+ AGENT_INVOCATIONS_GET,
4536
+ { invocationId: invocation["invocationId"] },
4537
+ resource ? { resource } : {}
4538
+ ));
4539
+ }
4540
+ return agentRunResult(invocation);
4541
+ }
4542
+ },
4443
4543
  {
4444
4544
  name: "topolo_validate_action",
4445
4545
  title: "Validate Topolo action input",
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
3
  "packageManager": "pnpm@10.17.1",
4
- "version": "0.13.3",
5
- "topoloPublishedVersion": "0.13.3",
4
+ "version": "0.14.2",
5
+ "topoloPublishedVersion": "0.14.2",
6
6
  "description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
7
7
  "license": "UNLICENSED",
8
8
  "author": "Topolo",