@topolo/mcp 0.9.11 → 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.
package/README.md CHANGED
@@ -217,6 +217,9 @@ sent.
217
217
  catalog. They do not expose the global generated platform catalog.
218
218
  - **Credential-scoped action discovery.** `topolo_search_actions` reads only one
219
219
  app partition and returns at most 100 permission-filtered actions per page.
220
+ Search, capability discovery, validation, planning, and application audit
221
+ return compact agent guidance by default; pass `detail: true` only when the
222
+ full contracts or findings are needed.
220
223
  - **Audit headers.** Every request sends `X-Topolo-Client: topolo-mcp/<ver>`,
221
224
  `X-Topolo-Agent: <label>`, `X-Topolo-Request-Id: <uuid>`.
222
225
  - **Write-action confirmation.** The SDK refuses mutating HTTP methods unless
package/dist/dispatch.js CHANGED
@@ -5,11 +5,16 @@ import { TopoloAuthError, TopoloPermissionError } from "@topolo/sdk";
5
5
  import {
6
6
  APPLICATION_REQUIREMENTS,
7
7
  APPLICATION_REQUIREMENTS_VERSION,
8
- APPLICATIONS,
9
- DEFAULT_APP_URLS,
10
- auditAllApplicationRequirements,
8
+ auditApplicationEntries,
11
9
  applicationRequirementScopes,
10
+ compactActionCatalogEntry,
11
+ compactActionPlan,
12
+ compactActionValidation,
13
+ compactApplicationAudit,
14
+ friendlyAppId,
15
+ listApplicationDirectory,
12
16
  requirementsForApplication,
17
+ resolveApplicationDirectoryEntry,
13
18
  resolveCatalogServiceUrl
14
19
  } from "@topolo/sdk";
15
20
 
@@ -62,7 +67,6 @@ function publicMcpErrorMessage(error, fallback) {
62
67
  }
63
68
 
64
69
  // src/tools.ts
65
- var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
66
70
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
67
71
  function requiredString(args, name) {
68
72
  const value = typeof args[name] === "string" ? args[name].trim() : "";
@@ -78,6 +82,20 @@ function optionalResourceContext(args) {
78
82
  }
79
83
  return { resourceType, resourceId };
80
84
  }
85
+ function coerceObjectParam(value, name) {
86
+ let candidate = value;
87
+ if (typeof candidate === "string" && candidate.trim().startsWith("{")) {
88
+ try {
89
+ candidate = JSON.parse(candidate);
90
+ } catch {
91
+ }
92
+ }
93
+ if (candidate === void 0) return {};
94
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
95
+ throw new TopoloMcpPublicError(`${name} must be an object.`);
96
+ }
97
+ return candidate;
98
+ }
81
99
  var TOOLS = [
82
100
  {
83
101
  name: "topolo_whoami",
@@ -103,7 +121,8 @@ var TOOLS = [
103
121
  properties: {
104
122
  query: { type: "string" },
105
123
  cursor: { type: "string" },
106
- limit: { type: "integer", minimum: 1, maximum: 100, default: 25 }
124
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 25 },
125
+ detail: { type: "boolean", description: "Return full schemas and agent contracts. Defaults to compact action guidance." }
107
126
  },
108
127
  additionalProperties: false
109
128
  },
@@ -157,12 +176,13 @@ var TOOLS = [
157
176
  handler: async (topolo, args) => {
158
177
  const service = typeof args["service"] === "string" ? args["service"].trim() : "";
159
178
  if (!service) throw new TopoloMcpPublicError("service is required for bounded action discovery.");
160
- return topolo.client.searchActions({
179
+ const result = await topolo.client.searchActions({
161
180
  service,
162
181
  query: typeof args["query"] === "string" ? args["query"] : void 0,
163
182
  cursor: typeof args["cursor"] === "string" ? args["cursor"] : void 0,
164
183
  limit: typeof args["limit"] === "number" ? args["limit"] : void 0
165
184
  });
185
+ return args["detail"] === true ? result : { actions: result.actions.map(compactActionCatalogEntry), page: result.page };
166
186
  }
167
187
  },
168
188
  {
@@ -228,14 +248,11 @@ var TOOLS = [
228
248
  handler: async (topolo, args) => {
229
249
  const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
230
250
  if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
231
- const input = args["input"];
232
- if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
233
- throw new TopoloMcpPublicError("input must be an object.");
234
- }
251
+ const input = coerceObjectParam(args["input"], "input");
235
252
  const resource = optionalResourceContext(args);
236
253
  return topolo.client.readAction(
237
254
  actionId,
238
- input ?? {},
255
+ input,
239
256
  resource ? { resource } : {}
240
257
  );
241
258
  }
@@ -261,14 +278,11 @@ var TOOLS = [
261
278
  handler: async (topolo, args) => {
262
279
  const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
263
280
  if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
264
- const input = args["input"];
265
- if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
266
- throw new TopoloMcpPublicError("input must be an object.");
267
- }
281
+ const input = coerceObjectParam(args["input"], "input");
268
282
  const resource = optionalResourceContext(args);
269
283
  return topolo.client.callAction(
270
284
  actionId,
271
- input ?? {},
285
+ input,
272
286
  {
273
287
  confirm: args["confirm"] === true,
274
288
  ...resource ? { resource } : {}
@@ -307,23 +321,17 @@ var TOOLS = [
307
321
  if (!isCanonicalBase64(contentBase64)) throw new TopoloMcpPublicError("contentBase64 must be canonical base64.");
308
322
  if (!contentType) throw new TopoloMcpPublicError("contentType is required.");
309
323
  if (!fileName) throw new TopoloMcpPublicError("fileName is required.");
310
- const input = args["input"];
311
- if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
312
- throw new TopoloMcpPublicError("input must be an object.");
313
- }
314
- const completionInput = args["completionInput"];
315
- if (completionInput !== void 0 && (!completionInput || typeof completionInput !== "object" || Array.isArray(completionInput))) {
316
- throw new TopoloMcpPublicError("completionInput must be an object.");
317
- }
324
+ const input = coerceObjectParam(args["input"], "input");
325
+ const completionInput = coerceObjectParam(args["completionInput"], "completionInput");
318
326
  const resource = optionalResourceContext(args);
319
327
  return topolo.client.uploadActionBase64(
320
328
  actionId,
321
329
  contentBase64,
322
- input ?? {},
330
+ input,
323
331
  {
324
332
  fileName,
325
333
  contentType,
326
- completionInput: completionInput ?? {},
334
+ completionInput,
327
335
  confirm: args["confirm"] === true,
328
336
  ...resource ? { resource } : {}
329
337
  }
@@ -340,7 +348,8 @@ var TOOLS = [
340
348
  type: "object",
341
349
  properties: {
342
350
  actionId: { type: "string", minLength: 1 },
343
- input: { type: "object", additionalProperties: true }
351
+ input: { type: "object", additionalProperties: true },
352
+ detail: { type: "boolean", description: "Include the full action contract. Defaults to a compact validation result." }
344
353
  },
345
354
  required: ["actionId"],
346
355
  additionalProperties: false
@@ -348,11 +357,9 @@ var TOOLS = [
348
357
  handler: async (topolo, args) => {
349
358
  const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
350
359
  if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
351
- const input = args["input"];
352
- if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
353
- throw new TopoloMcpPublicError("input must be an object.");
354
- }
355
- return topolo.client.validateActionInput(actionId, input ?? {});
360
+ const input = coerceObjectParam(args["input"], "input");
361
+ const result = await topolo.client.validateActionInput(actionId, input);
362
+ return args["detail"] === true ? result : compactActionValidation(result.action.actionId, result.validation);
356
363
  }
357
364
  },
358
365
  {
@@ -368,7 +375,8 @@ var TOOLS = [
368
375
  input: { type: "object", additionalProperties: true },
369
376
  resourceType: { type: "string", description: "Optional resource-scoping alias published in the app resource catalog (e.g. workspace, brand, merchant)." },
370
377
  resourceId: { type: "string", description: "Resource id. Required when resourceType is provided." },
371
- confirm: { type: "boolean", description: "Include confirmation in the plan without executing the action." }
378
+ confirm: { type: "boolean", description: "Include confirmation in the plan without executing the action." },
379
+ detail: { type: "boolean", description: "Include the full action contract. Defaults to the executable request and verification plan." }
372
380
  },
373
381
  required: ["actionId"],
374
382
  additionalProperties: false
@@ -376,19 +384,17 @@ var TOOLS = [
376
384
  handler: async (topolo, args) => {
377
385
  const actionId = typeof args["actionId"] === "string" ? args["actionId"].trim() : "";
378
386
  if (!actionId) throw new TopoloMcpPublicError("actionId is required.");
379
- const input = args["input"];
380
- if (input !== void 0 && (!input || typeof input !== "object" || Array.isArray(input))) {
381
- throw new TopoloMcpPublicError("input must be an object.");
382
- }
387
+ const input = coerceObjectParam(args["input"], "input");
383
388
  const resource = optionalResourceContext(args);
384
- return topolo.client.planAction(
389
+ const plan = await topolo.client.planAction(
385
390
  actionId,
386
- input ?? {},
391
+ input,
387
392
  {
388
393
  confirm: args["confirm"] === true,
389
394
  ...resource ? { resource } : {}
390
395
  }
391
396
  );
397
+ return args["detail"] === true ? plan : compactActionPlan(plan);
392
398
  }
393
399
  },
394
400
  {
@@ -422,14 +428,18 @@ var TOOLS = [
422
428
  destructive: false,
423
429
  inputSchema: {
424
430
  type: "object",
425
- properties: { service: { type: "string", minLength: 1 } },
431
+ properties: {
432
+ service: { type: "string", minLength: 1 },
433
+ detail: { type: "boolean", description: "Return full schemas and agent contracts. Defaults to compact capability guidance." }
434
+ },
426
435
  required: ["service"],
427
436
  additionalProperties: false
428
437
  },
429
438
  handler: async (topolo, args) => {
430
439
  const service = typeof args["service"] === "string" ? args["service"].trim() : "";
431
440
  if (!service) throw new TopoloMcpPublicError("service is required.");
432
- return topolo.client.searchActions({ service, limit: 100 });
441
+ const result = await topolo.client.searchActions({ service, limit: 100 });
442
+ return args["detail"] === true ? result : { actions: result.actions.map(compactActionCatalogEntry), page: result.page };
433
443
  }
434
444
  },
435
445
  {
@@ -560,7 +570,7 @@ var TOOLS = [
560
570
  },
561
571
  additionalProperties: false
562
572
  },
563
- handler: async (_topolo, args) => {
573
+ handler: async (topolo, args) => {
564
574
  const application = args["application"];
565
575
  if (application === void 0) {
566
576
  return {
@@ -570,12 +580,8 @@ var TOOLS = [
570
580
  requirements: APPLICATION_REQUIREMENTS
571
581
  };
572
582
  }
573
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
574
- throw new TopoloMcpPublicError(
575
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
576
- );
577
- }
578
- const app = APPLICATIONS[application];
583
+ const resolved = await resolveDirectoryApplication(topolo, application);
584
+ const app = resolved.application;
579
585
  return {
580
586
  version: APPLICATION_REQUIREMENTS_VERSION,
581
587
  application: app,
@@ -601,11 +607,15 @@ var TOOLS = [
601
607
  type: "string",
602
608
  enum: ["missing", "needs_review", "partial"],
603
609
  description: "Optional conformance gate. Returns conformanceGate.passed=false when findings at this severity or worse exist."
610
+ },
611
+ detail: {
612
+ type: "boolean",
613
+ description: "Include every finding and migration item. Defaults to per-app scores and a queue count."
604
614
  }
605
615
  },
606
616
  additionalProperties: false
607
617
  },
608
- handler: async (_topolo, args) => {
618
+ handler: async (topolo, args) => {
609
619
  const application = args["application"];
610
620
  const failOn = args["failOn"];
611
621
  if (failOn !== void 0 && typeof failOn !== "string") {
@@ -614,16 +624,15 @@ var TOOLS = [
614
624
  const gate = failOn ? normalizeFailOn(failOn) : null;
615
625
  let report;
616
626
  if (application === void 0) {
617
- report = auditAllApplicationRequirements();
618
- return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
619
- }
620
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
621
- throw new TopoloMcpPublicError(
622
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
623
- );
627
+ const directory = await listApplicationDirectory(topolo.client);
628
+ report = auditApplicationEntries(directory.applications.map((app) => app.application));
629
+ const output2 = args["detail"] === true ? report : compactApplicationAudit(report);
630
+ return gate ? { ...output2, conformanceGate: evaluateApplicationAuditGate(report, gate) } : output2;
624
631
  }
625
- report = auditAllApplicationRequirements([application]);
626
- return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
632
+ const resolved = await resolveDirectoryApplication(topolo, application);
633
+ report = auditApplicationEntries([resolved.application]);
634
+ const output = args["detail"] === true ? report : compactApplicationAudit(report);
635
+ return gate ? { ...output, conformanceGate: evaluateApplicationAuditGate(report, gate) } : output;
627
636
  }
628
637
  },
629
638
  {
@@ -762,31 +771,19 @@ function statusesAtOrWorse(threshold) {
762
771
  return ["missing", "needs_review", "partial"];
763
772
  }
764
773
  function displayAppId(entry) {
765
- for (const value of [entry.slug, entry.name, entry.appId]) {
766
- if (typeof value !== "string" || !value.trim()) continue;
767
- for (const candidate of appIdCandidates(value)) {
768
- if (Object.prototype.hasOwnProperty.call(DEFAULT_APP_URLS, candidate)) {
769
- return candidate;
770
- }
771
- }
772
- }
773
- return appIdCandidates(entry.slug || entry.appId)[0] || entry.appId;
774
- }
775
- function appIdCandidates(value) {
776
- const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
777
- const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
778
- return [.../* @__PURE__ */ new Set([
779
- normalized,
780
- normalized.replace(/-/g, "_"),
781
- withoutTopolo,
782
- withoutTopolo.replace(/-/g, "_")
783
- ])].filter(Boolean);
774
+ return friendlyAppId(entry);
784
775
  }
785
- function formatKnownValues(values, label) {
786
- const sorted = [...values].sort();
787
- const preview = sorted.slice(0, 25).join(", ");
788
- const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
789
- return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
776
+ async function resolveDirectoryApplication(topolo, application) {
777
+ if (typeof application !== "string" || !application.trim()) {
778
+ throw new TopoloMcpPublicError("`application` must be a non-empty string.");
779
+ }
780
+ try {
781
+ return await resolveApplicationDirectoryEntry(topolo.client, application);
782
+ } catch (error) {
783
+ throw new TopoloMcpPublicError(
784
+ error instanceof Error ? error.message : `Unknown application "${application}".`
785
+ );
786
+ }
790
787
  }
791
788
 
792
789
  // src/dispatch.ts