@topolo/mcp 0.9.10 → 0.10.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.
package/README.md CHANGED
@@ -153,6 +153,10 @@ deletion, encryption, and audit storage for the data they own.
153
153
  | `topolo_get_action` | (credential-scoped) | no |
154
154
  | `topolo_read_action` | catalog or target policy | no |
155
155
  | `topolo_call_action` | catalog or target policy | yes |
156
+ | `topolo_list_workspaces` | target app runtime policy | no |
157
+ | `topolo_create_workspace` | target app runtime policy | yes |
158
+ | `topolo_rename_workspace` | target app runtime policy | yes |
159
+ | `topolo_set_default_workspace` | target app runtime policy | yes |
156
160
  | `topolo_api_call` | (service-enforced) | yes |
157
161
 
158
162
  Applications publish machine-action definitions through Topolo Developers.
@@ -171,6 +175,12 @@ capability without inventing a global permission, and the target service checks
171
175
  the current database-backed policy for the supplied organization/application on
172
176
  every call.
173
177
 
178
+ Workspace lifecycle is first-class so agents do not have to know that Auth is
179
+ the canonical owner. The tools accept the target application, derive the
180
+ organization from the credential, and retain Auth's per-app runtime decision.
181
+ `topolo_list_workspaces` exposes `policy.canCreate` and `policy.reason`; the
182
+ write tools never infer access from organization ownership.
183
+
174
184
  `topolo_api_call` is the escape hatch for endpoints without typed MCP tools
175
185
  yet. GET requests work without confirmation; mutating HTTP methods must pass
176
186
  `confirm: true` or the SDK rejects the call. Backing services still enforce
package/dist/dispatch.js CHANGED
@@ -5,11 +5,12 @@ 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
+ friendlyAppId,
11
+ listApplicationDirectory,
12
12
  requirementsForApplication,
13
+ resolveApplicationDirectoryEntry,
13
14
  resolveCatalogServiceUrl
14
15
  } from "@topolo/sdk";
15
16
 
@@ -62,8 +63,12 @@ function publicMcpErrorMessage(error, fallback) {
62
63
  }
63
64
 
64
65
  // src/tools.ts
65
- var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
66
66
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
67
+ function requiredString(args, name) {
68
+ const value = typeof args[name] === "string" ? args[name].trim() : "";
69
+ if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
70
+ return value;
71
+ }
67
72
  function optionalResourceContext(args) {
68
73
  const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
69
74
  const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
@@ -463,6 +468,78 @@ var TOOLS = [
463
468
  return topolo.client.listBindableResources(application);
464
469
  }
465
470
  },
471
+ {
472
+ name: "topolo_list_workspaces",
473
+ title: "List workspaces for one Topolo application",
474
+ description: "Lists caller-visible workspaces and the caller-specific creation policy for one target application. Use this before creating a workspace; organization ownership alone does not imply creation rights for every app.",
475
+ requiredScopes: [],
476
+ destructive: false,
477
+ inputSchema: {
478
+ type: "object",
479
+ properties: { application: { type: "string", minLength: 1 } },
480
+ required: ["application"],
481
+ additionalProperties: false
482
+ },
483
+ handler: async (topolo, args) => {
484
+ const application = requiredString(args, "application");
485
+ return topolo.workspaces.list(application);
486
+ }
487
+ },
488
+ {
489
+ name: "topolo_create_workspace",
490
+ title: "Create a workspace for one Topolo application",
491
+ description: "Creates a workspace only after reading the target application policy and confirming it permits this caller. The canonical Auth runtime still authorizes the concrete app and organization. Set confirm:true only after approval.",
492
+ requiredScopes: [],
493
+ destructive: true,
494
+ inputSchema: {
495
+ type: "object",
496
+ properties: { application: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, isDefault: { type: "boolean" }, confirm: { type: "boolean" } },
497
+ required: ["application", "name"],
498
+ additionalProperties: false
499
+ },
500
+ handler: async (topolo, args) => topolo.workspaces.create(requiredString(args, "application"), {
501
+ name: requiredString(args, "name"),
502
+ ...typeof args["isDefault"] === "boolean" ? { isDefault: args["isDefault"] } : {},
503
+ confirm: args["confirm"] === true
504
+ })
505
+ },
506
+ {
507
+ name: "topolo_rename_workspace",
508
+ title: "Rename one Topolo application workspace",
509
+ description: "Renames a caller-authorized workspace without changing its stable id, slug, ownership, membership, or app data.",
510
+ requiredScopes: [],
511
+ destructive: true,
512
+ inputSchema: {
513
+ type: "object",
514
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, confirm: { type: "boolean" } },
515
+ required: ["application", "workspaceId", "name"],
516
+ additionalProperties: false
517
+ },
518
+ handler: async (topolo, args) => topolo.workspaces.rename(
519
+ requiredString(args, "application"),
520
+ requiredString(args, "workspaceId"),
521
+ requiredString(args, "name"),
522
+ args["confirm"] === true
523
+ )
524
+ },
525
+ {
526
+ name: "topolo_set_default_workspace",
527
+ title: "Set an application default workspace",
528
+ description: "Sets exactly one caller-authorized workspace as the default for the target application and current organization.",
529
+ requiredScopes: [],
530
+ destructive: true,
531
+ inputSchema: {
532
+ type: "object",
533
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, confirm: { type: "boolean" } },
534
+ required: ["application", "workspaceId"],
535
+ additionalProperties: false
536
+ },
537
+ handler: async (topolo, args) => topolo.workspaces.setDefault(
538
+ requiredString(args, "application"),
539
+ requiredString(args, "workspaceId"),
540
+ args["confirm"] === true
541
+ )
542
+ },
466
543
  {
467
544
  name: "topolo_list_application_requirements",
468
545
  title: "List Topolo application build requirements",
@@ -479,7 +556,7 @@ var TOOLS = [
479
556
  },
480
557
  additionalProperties: false
481
558
  },
482
- handler: async (_topolo, args) => {
559
+ handler: async (topolo, args) => {
483
560
  const application = args["application"];
484
561
  if (application === void 0) {
485
562
  return {
@@ -489,12 +566,8 @@ var TOOLS = [
489
566
  requirements: APPLICATION_REQUIREMENTS
490
567
  };
491
568
  }
492
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
493
- throw new TopoloMcpPublicError(
494
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
495
- );
496
- }
497
- const app = APPLICATIONS[application];
569
+ const resolved = await resolveDirectoryApplication(topolo, application);
570
+ const app = resolved.application;
498
571
  return {
499
572
  version: APPLICATION_REQUIREMENTS_VERSION,
500
573
  application: app,
@@ -524,7 +597,7 @@ var TOOLS = [
524
597
  },
525
598
  additionalProperties: false
526
599
  },
527
- handler: async (_topolo, args) => {
600
+ handler: async (topolo, args) => {
528
601
  const application = args["application"];
529
602
  const failOn = args["failOn"];
530
603
  if (failOn !== void 0 && typeof failOn !== "string") {
@@ -533,15 +606,12 @@ var TOOLS = [
533
606
  const gate = failOn ? normalizeFailOn(failOn) : null;
534
607
  let report;
535
608
  if (application === void 0) {
536
- report = auditAllApplicationRequirements();
609
+ const directory = await listApplicationDirectory(topolo.client);
610
+ report = auditApplicationEntries(directory.applications.map((app) => app.application));
537
611
  return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
538
612
  }
539
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
540
- throw new TopoloMcpPublicError(
541
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
542
- );
543
- }
544
- report = auditAllApplicationRequirements([application]);
613
+ const resolved = await resolveDirectoryApplication(topolo, application);
614
+ report = auditApplicationEntries([resolved.application]);
545
615
  return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
546
616
  }
547
617
  },
@@ -681,31 +751,19 @@ function statusesAtOrWorse(threshold) {
681
751
  return ["missing", "needs_review", "partial"];
682
752
  }
683
753
  function displayAppId(entry) {
684
- for (const value of [entry.slug, entry.name, entry.appId]) {
685
- if (typeof value !== "string" || !value.trim()) continue;
686
- for (const candidate of appIdCandidates(value)) {
687
- if (Object.prototype.hasOwnProperty.call(DEFAULT_APP_URLS, candidate)) {
688
- return candidate;
689
- }
690
- }
691
- }
692
- return appIdCandidates(entry.slug || entry.appId)[0] || entry.appId;
693
- }
694
- function appIdCandidates(value) {
695
- const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
696
- const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
697
- return [.../* @__PURE__ */ new Set([
698
- normalized,
699
- normalized.replace(/-/g, "_"),
700
- withoutTopolo,
701
- withoutTopolo.replace(/-/g, "_")
702
- ])].filter(Boolean);
754
+ return friendlyAppId(entry);
703
755
  }
704
- function formatKnownValues(values, label) {
705
- const sorted = [...values].sort();
706
- const preview = sorted.slice(0, 25).join(", ");
707
- const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
708
- return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
756
+ async function resolveDirectoryApplication(topolo, application) {
757
+ if (typeof application !== "string" || !application.trim()) {
758
+ throw new TopoloMcpPublicError("`application` must be a non-empty string.");
759
+ }
760
+ try {
761
+ return await resolveApplicationDirectoryEntry(topolo.client, application);
762
+ } catch (error) {
763
+ throw new TopoloMcpPublicError(
764
+ error instanceof Error ? error.message : `Unknown application "${application}".`
765
+ );
766
+ }
709
767
  }
710
768
 
711
769
  // src/dispatch.ts
package/dist/http.js CHANGED
@@ -15830,11 +15830,12 @@ import { TopoloAuthError, TopoloHttpError, TopoloPermissionError } from "@topolo
15830
15830
  import {
15831
15831
  APPLICATION_REQUIREMENTS,
15832
15832
  APPLICATION_REQUIREMENTS_VERSION,
15833
- APPLICATIONS,
15834
- DEFAULT_APP_URLS,
15835
- auditAllApplicationRequirements,
15833
+ auditApplicationEntries,
15836
15834
  applicationRequirementScopes,
15835
+ friendlyAppId,
15836
+ listApplicationDirectory,
15837
15837
  requirementsForApplication,
15838
+ resolveApplicationDirectoryEntry,
15838
15839
  resolveCatalogServiceUrl
15839
15840
  } from "@topolo/sdk";
15840
15841
 
@@ -15876,8 +15877,12 @@ function splitService(servicePart) {
15876
15877
  }
15877
15878
 
15878
15879
  // src/tools.ts
15879
- var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
15880
15880
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
15881
+ function requiredString(args, name) {
15882
+ const value = typeof args[name] === "string" ? args[name].trim() : "";
15883
+ if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
15884
+ return value;
15885
+ }
15881
15886
  function optionalResourceContext(args) {
15882
15887
  const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
15883
15888
  const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
@@ -16277,6 +16282,78 @@ var TOOLS = [
16277
16282
  return topolo.client.listBindableResources(application);
16278
16283
  }
16279
16284
  },
16285
+ {
16286
+ name: "topolo_list_workspaces",
16287
+ title: "List workspaces for one Topolo application",
16288
+ description: "Lists caller-visible workspaces and the caller-specific creation policy for one target application. Use this before creating a workspace; organization ownership alone does not imply creation rights for every app.",
16289
+ requiredScopes: [],
16290
+ destructive: false,
16291
+ inputSchema: {
16292
+ type: "object",
16293
+ properties: { application: { type: "string", minLength: 1 } },
16294
+ required: ["application"],
16295
+ additionalProperties: false
16296
+ },
16297
+ handler: async (topolo, args) => {
16298
+ const application = requiredString(args, "application");
16299
+ return topolo.workspaces.list(application);
16300
+ }
16301
+ },
16302
+ {
16303
+ name: "topolo_create_workspace",
16304
+ title: "Create a workspace for one Topolo application",
16305
+ description: "Creates a workspace only after reading the target application policy and confirming it permits this caller. The canonical Auth runtime still authorizes the concrete app and organization. Set confirm:true only after approval.",
16306
+ requiredScopes: [],
16307
+ destructive: true,
16308
+ inputSchema: {
16309
+ type: "object",
16310
+ properties: { application: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, isDefault: { type: "boolean" }, confirm: { type: "boolean" } },
16311
+ required: ["application", "name"],
16312
+ additionalProperties: false
16313
+ },
16314
+ handler: async (topolo, args) => topolo.workspaces.create(requiredString(args, "application"), {
16315
+ name: requiredString(args, "name"),
16316
+ ...typeof args["isDefault"] === "boolean" ? { isDefault: args["isDefault"] } : {},
16317
+ confirm: args["confirm"] === true
16318
+ })
16319
+ },
16320
+ {
16321
+ name: "topolo_rename_workspace",
16322
+ title: "Rename one Topolo application workspace",
16323
+ description: "Renames a caller-authorized workspace without changing its stable id, slug, ownership, membership, or app data.",
16324
+ requiredScopes: [],
16325
+ destructive: true,
16326
+ inputSchema: {
16327
+ type: "object",
16328
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, confirm: { type: "boolean" } },
16329
+ required: ["application", "workspaceId", "name"],
16330
+ additionalProperties: false
16331
+ },
16332
+ handler: async (topolo, args) => topolo.workspaces.rename(
16333
+ requiredString(args, "application"),
16334
+ requiredString(args, "workspaceId"),
16335
+ requiredString(args, "name"),
16336
+ args["confirm"] === true
16337
+ )
16338
+ },
16339
+ {
16340
+ name: "topolo_set_default_workspace",
16341
+ title: "Set an application default workspace",
16342
+ description: "Sets exactly one caller-authorized workspace as the default for the target application and current organization.",
16343
+ requiredScopes: [],
16344
+ destructive: true,
16345
+ inputSchema: {
16346
+ type: "object",
16347
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, confirm: { type: "boolean" } },
16348
+ required: ["application", "workspaceId"],
16349
+ additionalProperties: false
16350
+ },
16351
+ handler: async (topolo, args) => topolo.workspaces.setDefault(
16352
+ requiredString(args, "application"),
16353
+ requiredString(args, "workspaceId"),
16354
+ args["confirm"] === true
16355
+ )
16356
+ },
16280
16357
  {
16281
16358
  name: "topolo_list_application_requirements",
16282
16359
  title: "List Topolo application build requirements",
@@ -16293,7 +16370,7 @@ var TOOLS = [
16293
16370
  },
16294
16371
  additionalProperties: false
16295
16372
  },
16296
- handler: async (_topolo, args) => {
16373
+ handler: async (topolo, args) => {
16297
16374
  const application = args["application"];
16298
16375
  if (application === void 0) {
16299
16376
  return {
@@ -16303,12 +16380,8 @@ var TOOLS = [
16303
16380
  requirements: APPLICATION_REQUIREMENTS
16304
16381
  };
16305
16382
  }
16306
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
16307
- throw new TopoloMcpPublicError(
16308
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
16309
- );
16310
- }
16311
- const app = APPLICATIONS[application];
16383
+ const resolved = await resolveDirectoryApplication(topolo, application);
16384
+ const app = resolved.application;
16312
16385
  return {
16313
16386
  version: APPLICATION_REQUIREMENTS_VERSION,
16314
16387
  application: app,
@@ -16338,7 +16411,7 @@ var TOOLS = [
16338
16411
  },
16339
16412
  additionalProperties: false
16340
16413
  },
16341
- handler: async (_topolo, args) => {
16414
+ handler: async (topolo, args) => {
16342
16415
  const application = args["application"];
16343
16416
  const failOn = args["failOn"];
16344
16417
  if (failOn !== void 0 && typeof failOn !== "string") {
@@ -16347,15 +16420,12 @@ var TOOLS = [
16347
16420
  const gate = failOn ? normalizeFailOn(failOn) : null;
16348
16421
  let report;
16349
16422
  if (application === void 0) {
16350
- report = auditAllApplicationRequirements();
16423
+ const directory = await listApplicationDirectory(topolo.client);
16424
+ report = auditApplicationEntries(directory.applications.map((app) => app.application));
16351
16425
  return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
16352
16426
  }
16353
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
16354
- throw new TopoloMcpPublicError(
16355
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
16356
- );
16357
- }
16358
- report = auditAllApplicationRequirements([application]);
16427
+ const resolved = await resolveDirectoryApplication(topolo, application);
16428
+ report = auditApplicationEntries([resolved.application]);
16359
16429
  return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
16360
16430
  }
16361
16431
  },
@@ -16495,31 +16565,19 @@ function statusesAtOrWorse(threshold) {
16495
16565
  return ["missing", "needs_review", "partial"];
16496
16566
  }
16497
16567
  function displayAppId(entry) {
16498
- for (const value of [entry.slug, entry.name, entry.appId]) {
16499
- if (typeof value !== "string" || !value.trim()) continue;
16500
- for (const candidate of appIdCandidates(value)) {
16501
- if (Object.prototype.hasOwnProperty.call(DEFAULT_APP_URLS, candidate)) {
16502
- return candidate;
16503
- }
16504
- }
16505
- }
16506
- return appIdCandidates(entry.slug || entry.appId)[0] || entry.appId;
16507
- }
16508
- function appIdCandidates(value) {
16509
- const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
16510
- const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
16511
- return [.../* @__PURE__ */ new Set([
16512
- normalized,
16513
- normalized.replace(/-/g, "_"),
16514
- withoutTopolo,
16515
- withoutTopolo.replace(/-/g, "_")
16516
- ])].filter(Boolean);
16568
+ return friendlyAppId(entry);
16517
16569
  }
16518
- function formatKnownValues(values, label) {
16519
- const sorted = [...values].sort();
16520
- const preview = sorted.slice(0, 25).join(", ");
16521
- const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
16522
- return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
16570
+ async function resolveDirectoryApplication(topolo, application) {
16571
+ if (typeof application !== "string" || !application.trim()) {
16572
+ throw new TopoloMcpPublicError("`application` must be a non-empty string.");
16573
+ }
16574
+ try {
16575
+ return await resolveApplicationDirectoryEntry(topolo.client, application);
16576
+ } catch (error2) {
16577
+ throw new TopoloMcpPublicError(
16578
+ error2 instanceof Error ? error2.message : `Unknown application "${application}".`
16579
+ );
16580
+ }
16523
16581
  }
16524
16582
 
16525
16583
  // src/version.ts
package/dist/index.js CHANGED
@@ -104,57 +104,8 @@ var TopoloHttpError = class extends TopoloSdkError {
104
104
  }
105
105
  };
106
106
  var APP_API_URLS = {
107
- agent: "https://agent.topolo.app",
108
107
  auth: "https://auth.topolo.app",
109
- backup: "https://backup.topolo.app",
110
- blog: "https://blog.topolo.app",
111
- books: "https://books.topolo.app",
112
- brand: "https://brand.topolo.app",
113
- bugfix: "https://bugfix.topolo.app",
114
- bytes: "https://bytes.topolo.app",
115
- calendar: "https://calendar.topolo.app",
116
- campaigns: "https://campaigns.topolo.app",
117
- capacity: "https://capacity.topolo.app",
118
- chat: "https://chat.topolo.app",
119
- commerce: "https://commerce.topolo.app",
120
- compose: "https://compose.topolo.app",
121
- consent: "https://consent.topolo.app",
122
- crm: "https://crm.topolo.app",
123
- design: "https://design.topolo.app",
124
- developers: "https://developers.topolo.app",
125
- director: "https://director.topolo.app",
126
- feed: "https://feed.topolo.app",
127
- flow: "https://flow.topolo.app",
128
- forecast: "https://forecast.topolo.app",
129
- forms: "https://forms.topolo.app",
130
- home: "https://home.topolo.app",
131
- insights: "https://insights.topolo.app",
132
- inventory: "https://inventory.topolo.app",
133
- learn: "https://learn.topolo.app",
134
- localize: "https://localize.topolo.app",
135
- mail: "https://mail.topolo.app",
136
- mdm: "https://mdm.topolo.app",
137
- messages: "https://messages.topolo.app",
138
- nexus: "https://nexus.topolo.app",
139
- notify: "https://notify.topolo.app",
140
- observability_watch: "https://observability-watch.topolo.app",
141
- one: "https://www.topolo.app",
142
- p2p: "https://p2p.topolo.app",
143
- pay: "https://pay.topolo.app",
144
- people: "https://people.topolo.app",
145
- "quro.api": "https://api.ol0.me",
146
- "quro.redirect": "https://ol0.me",
147
- roadmapper: "https://roadmapper.topolo.app",
148
- sign: "https://sign.topolo.app",
149
- social_studio: "https://studio.topolo.app",
150
- socialize: "https://socialize.topolo.app",
151
- spaces: "https://spaces.topolo.app",
152
- status: "https://status.topolo.app",
153
- success: "https://success.topolo.app",
154
- support: "https://support.topolo.app",
155
- survey: "https://survey.topolo.app",
156
- voice: "https://voice.topolo.app",
157
- web: "https://web.topolo.app"
108
+ developers: "https://developers.topolo.app"
158
109
  };
159
110
  var APPLICATIONS = {
160
111
  "admin": {
@@ -1880,7 +1831,145 @@ var APPLICATIONS = {
1880
1831
  ]
1881
1832
  }
1882
1833
  };
1883
- var APPLICATION_REQUIREMENTS_VERSION = "2026-06-07.1";
1834
+ function friendlyAppId(entry) {
1835
+ for (const value of [entry.slug, entry.name, entry.appId]) {
1836
+ if (typeof value !== "string" || !value.trim()) continue;
1837
+ const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
1838
+ const withoutPrefix = normalized.replace(/^(?:app[_-])?topolo[-_]+/, "");
1839
+ if (withoutPrefix) return withoutPrefix;
1840
+ }
1841
+ return entry.appId;
1842
+ }
1843
+ function idVariants(value) {
1844
+ const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
1845
+ return [.../* @__PURE__ */ new Set([
1846
+ normalized,
1847
+ normalized.replace(/-/g, "_"),
1848
+ normalized.replace(/_/g, "-")
1849
+ ])].filter(Boolean);
1850
+ }
1851
+ var STATIC_APPLICATIONS = APPLICATIONS;
1852
+ function staticApplicationForId(id) {
1853
+ for (const variant of idVariants(id)) {
1854
+ const entry = STATIC_APPLICATIONS[variant];
1855
+ if (entry) return entry;
1856
+ }
1857
+ return null;
1858
+ }
1859
+ function applicationEntryFromCatalog(live) {
1860
+ const id = friendlyAppId(live);
1861
+ return {
1862
+ id,
1863
+ appDir: "-",
1864
+ name: live.name ?? live.slug ?? live.appId,
1865
+ packageName: null,
1866
+ productionUrl: live.launchUrl ?? live.apiBaseUrl ?? null,
1867
+ services: live.apiBaseUrl ? [id] : [],
1868
+ deployTargets: []
1869
+ };
1870
+ }
1871
+ var DIRECTORY_PAGE_LIMIT = 100;
1872
+ var DIRECTORY_MAX_PAGES = 100;
1873
+ function staticApplicationDirectory() {
1874
+ const applications = Object.entries(STATIC_APPLICATIONS).map(([id, application]) => ({
1875
+ id,
1876
+ application,
1877
+ source: "static",
1878
+ live: null
1879
+ })).sort((left, right) => left.id.localeCompare(right.id));
1880
+ return { source: "static_fallback", applications };
1881
+ }
1882
+ function directoryApplicationFromLive(live) {
1883
+ const id = friendlyAppId(live);
1884
+ return {
1885
+ id,
1886
+ // Prefer the static snapshot's richer repo/deploy metadata when the live
1887
+ // registry confirms the app; synthesize from the live entry otherwise.
1888
+ application: staticApplicationForId(id) ?? applicationEntryFromCatalog(live),
1889
+ source: "live",
1890
+ live
1891
+ };
1892
+ }
1893
+ async function listApplicationDirectory(client) {
1894
+ if (!client) return staticApplicationDirectory();
1895
+ let services;
1896
+ try {
1897
+ services = [];
1898
+ let cursor;
1899
+ for (let page = 0; page < DIRECTORY_MAX_PAGES; page += 1) {
1900
+ const result = await client.searchServices({
1901
+ ...cursor ? { cursor } : {},
1902
+ limit: DIRECTORY_PAGE_LIMIT
1903
+ });
1904
+ services.push(...result.services);
1905
+ if (!result.page.nextCursor) break;
1906
+ cursor = result.page.nextCursor;
1907
+ }
1908
+ } catch {
1909
+ return staticApplicationDirectory();
1910
+ }
1911
+ const seen = /* @__PURE__ */ new Map();
1912
+ for (const live of services) {
1913
+ const entry = directoryApplicationFromLive(live);
1914
+ if (!seen.has(entry.id)) seen.set(entry.id, entry);
1915
+ }
1916
+ return {
1917
+ source: "live",
1918
+ applications: [...seen.values()].sort((left, right) => left.id.localeCompare(right.id))
1919
+ };
1920
+ }
1921
+ function matchesDirectoryApplication(app, needle) {
1922
+ const candidates = new Set(idVariants(needle));
1923
+ const keys = [
1924
+ app.id,
1925
+ app.application.id,
1926
+ app.application.name,
1927
+ app.live?.appId,
1928
+ app.live?.slug,
1929
+ app.live?.name
1930
+ ];
1931
+ for (const key of keys) {
1932
+ if (typeof key !== "string" || !key.trim()) continue;
1933
+ for (const variant of idVariants(key)) {
1934
+ if (candidates.has(variant)) return true;
1935
+ }
1936
+ for (const variant of idVariants(friendlyAppId({ appId: key }))) {
1937
+ if (candidates.has(variant)) return true;
1938
+ }
1939
+ }
1940
+ return false;
1941
+ }
1942
+ function formatDirectoryApplicationIds(directory) {
1943
+ const ids = directory.applications.map((app) => app.id).sort();
1944
+ const preview = ids.slice(0, 50).join(", ");
1945
+ const suffix = ids.length > 50 ? `, ... +${ids.length - 50} more` : "";
1946
+ const label = directory.source === "live" ? "Applications available to this credential" : "Applications in the bundled static snapshot (live registry unreachable)";
1947
+ return `${label}: ${preview}${suffix}`;
1948
+ }
1949
+ async function resolveApplicationDirectoryEntry(client, application) {
1950
+ const needle = typeof application === "string" ? application.trim() : "";
1951
+ if (!needle) {
1952
+ throw new TopoloSdkError("unknown_application", "application is required");
1953
+ }
1954
+ if (client) {
1955
+ let live = null;
1956
+ try {
1957
+ live = await client.getService(needle);
1958
+ } catch {
1959
+ live = null;
1960
+ }
1961
+ if (live) return directoryApplicationFromLive(live);
1962
+ }
1963
+ const directory = await listApplicationDirectory(client);
1964
+ const match = directory.applications.find((app) => matchesDirectoryApplication(app, needle));
1965
+ if (match) return match;
1966
+ throw new TopoloSdkError(
1967
+ "unknown_application",
1968
+ `Unknown application "${application}". ${formatDirectoryApplicationIds(directory)}`
1969
+ );
1970
+ }
1971
+ var STATIC_APPLICATIONS2 = APPLICATIONS;
1972
+ var APPLICATION_REQUIREMENTS_VERSION = "2026-08-11.1";
1884
1973
  var APPLICATION_REQUIREMENTS = [
1885
1974
  {
1886
1975
  id: "platform-metadata",
@@ -1890,10 +1979,10 @@ var APPLICATION_REQUIREMENTS = [
1890
1979
  evidence: [
1891
1980
  "topolo.cloudcontrol.json exists for every deployed app surface",
1892
1981
  "package.json names the package or app when the repo ships code",
1893
- "The SDK application catalog exposes the app through topolo apps and MCP discovery"
1982
+ "The live platform registry exposes the app through topolo apps and MCP discovery"
1894
1983
  ],
1895
1984
  implementation: [
1896
- "Add the app to the SDK application catalog generator before exposing it to agents",
1985
+ "Register the app in the live platform registry (Auth service catalog) before exposing it to agents",
1897
1986
  "Keep production URLs, Worker names, Pages projects, routes, domains, and bindings current in CloudControl metadata",
1898
1987
  "Do not leave a new Topolo* app directory without an explicit application ID"
1899
1988
  ]
@@ -1968,7 +2057,7 @@ var APPLICATION_REQUIREMENTS = [
1968
2057
  implementation: [
1969
2058
  "Add production Worker api_url metadata and sdk_app_id for every agent-callable API target",
1970
2059
  "Mark browser, documentation, and runtime-only Worker targets with agent_callable:false",
1971
- "Expose stable app IDs through generated DEFAULT_APP_URLS and live catalog discovery",
2060
+ "Expose stable app IDs through live catalog discovery",
1972
2061
  "Keep typed SDK/CLI/MCP surfaces in sync when an API contract stabilizes"
1973
2062
  ]
1974
2063
  },
@@ -2088,15 +2177,24 @@ function applicationRequirementScopes(application) {
2088
2177
  }
2089
2178
  return Array.from(scopes);
2090
2179
  }
2180
+ function fallbackApplication(id) {
2181
+ const entry = STATIC_APPLICATIONS2[id];
2182
+ if (!entry) {
2183
+ throw new Error(
2184
+ `Unknown application "${id}" in the static fallback snapshot. Resolve applications from the live registry (resolveApplicationDirectoryEntry) and pass the entry directly.`
2185
+ );
2186
+ }
2187
+ return entry;
2188
+ }
2091
2189
  function requirementsForApplication(applicationOrId) {
2092
- const application = typeof applicationOrId === "string" ? APPLICATIONS[applicationOrId] : applicationOrId;
2190
+ const application = typeof applicationOrId === "string" ? fallbackApplication(applicationOrId) : applicationOrId;
2093
2191
  const scopes = new Set(applicationRequirementScopes(application));
2094
2192
  return APPLICATION_REQUIREMENTS.filter(
2095
2193
  (requirement) => requirement.appliesTo.some((scope) => scopes.has(scope))
2096
2194
  );
2097
2195
  }
2098
2196
  function auditApplicationRequirements(applicationOrId) {
2099
- const application = typeof applicationOrId === "string" ? APPLICATIONS[applicationOrId] : applicationOrId;
2197
+ const application = typeof applicationOrId === "string" ? fallbackApplication(applicationOrId) : applicationOrId;
2100
2198
  const scopes = applicationRequirementScopes(application);
2101
2199
  const findings = requirementsForApplication(application).map(
2102
2200
  (requirement) => evaluateRequirement(application, requirement)
@@ -2109,10 +2207,12 @@ function auditApplicationRequirements(applicationOrId) {
2109
2207
  findings
2110
2208
  };
2111
2209
  }
2112
- function auditAllApplicationRequirements(applicationIds = Object.keys(APPLICATIONS).sort()) {
2113
- const applications = applicationIds.map(
2114
- (applicationId) => auditApplicationRequirements(applicationId)
2210
+ function auditApplicationEntries(applications) {
2211
+ return buildAuditReport(
2212
+ applications.map((application) => auditApplicationRequirements(application))
2115
2213
  );
2214
+ }
2215
+ function buildAuditReport(applications) {
2116
2216
  const migrationQueue = applications.flatMap(
2117
2217
  (audit) => audit.findings.filter((finding2) => finding2.status !== "met").map((finding2) => ({
2118
2218
  applicationId: audit.application.id,
@@ -3415,8 +3515,9 @@ var TopoloClient = class _TopoloClient {
3415
3515
  });
3416
3516
  }
3417
3517
  /**
3418
- * Bundled generated registry for tooling that audits platform packaging, not
3419
- * for deciding what the current credential may use.
3518
+ * @deprecated Static fallback snapshot only. Use `searchServices()` (the
3519
+ * live credential-scoped registry) for anything that decides behavior; this
3520
+ * exists for offline packaging audits when the registry is unreachable.
3420
3521
  */
3421
3522
  listBundledServices() {
3422
3523
  const apps = Object.values(APPLICATIONS);
@@ -3865,11 +3966,71 @@ var IdentityModule = class {
3865
3966
  return this.client.introspect();
3866
3967
  }
3867
3968
  };
3969
+ var ACTIONS = {
3970
+ list: "app_topolo_auth.workspaces.list",
3971
+ create: "app_topolo_auth.workspaces.create",
3972
+ rename: "app_topolo_auth.workspaces.rename",
3973
+ setDefault: "app_topolo_auth.workspaces.default.set"
3974
+ };
3975
+ var WorkspacesModule = class {
3976
+ constructor(client) {
3977
+ this.client = client;
3978
+ }
3979
+ client;
3980
+ async list(application) {
3981
+ const context = await this.resolveContext(application);
3982
+ return this.client.callAction(ACTIONS.list, {
3983
+ org_id: context.organizationId,
3984
+ app_id: context.appId
3985
+ });
3986
+ }
3987
+ async create(application, input) {
3988
+ const current = await this.list(application);
3989
+ if (!current.data.policy.canCreate) {
3990
+ throw new TopoloSdkError(
3991
+ "workspace_creation_not_allowed",
3992
+ `Workspace creation is not allowed for ${current.data.appId}: ${current.data.policy.reason}`
3993
+ );
3994
+ }
3995
+ return this.client.callAction(ACTIONS.create, {
3996
+ organizationId: current.data.organizationId,
3997
+ appId: current.data.appId,
3998
+ name: input.name,
3999
+ ...input.isDefault === void 0 ? {} : { isDefault: input.isDefault }
4000
+ }, { confirm: input.confirm === true });
4001
+ }
4002
+ async rename(application, workspaceId, name, confirm = false) {
4003
+ const context = await this.resolveContext(application);
4004
+ return this.client.callAction(ACTIONS.rename, {
4005
+ workspaceId,
4006
+ organizationId: context.organizationId,
4007
+ appId: context.appId,
4008
+ name
4009
+ }, { confirm });
4010
+ }
4011
+ async setDefault(application, workspaceId, confirm = false) {
4012
+ const context = await this.resolveContext(application);
4013
+ return this.client.callAction(ACTIONS.setDefault, {
4014
+ workspaceId,
4015
+ organizationId: context.organizationId,
4016
+ appId: context.appId
4017
+ }, { confirm });
4018
+ }
4019
+ async resolveContext(application) {
4020
+ const identity = await this.client.introspect();
4021
+ if (!identity.organization?.id) {
4022
+ throw new TopoloSdkError("organization_required", "Workspace lifecycle requires an organization-scoped credential.");
4023
+ }
4024
+ const service = await this.client.getService(application);
4025
+ return { organizationId: identity.organization.id, appId: service.appId };
4026
+ }
4027
+ };
3868
4028
  function createTopolo(options) {
3869
4029
  const client = new TopoloClient(options);
3870
4030
  return {
3871
4031
  client,
3872
4032
  identity: new IdentityModule(client),
4033
+ workspaces: new WorkspacesModule(client),
3873
4034
  crm: new CrmModule(client)
3874
4035
  };
3875
4036
  }
@@ -3935,57 +4096,8 @@ var TopoloHttpError2 = class extends TopoloSdkError2 {
3935
4096
  }
3936
4097
  };
3937
4098
  var APP_API_URLS2 = {
3938
- agent: "https://agent.topolo.app",
3939
4099
  auth: "https://auth.topolo.app",
3940
- backup: "https://backup.topolo.app",
3941
- blog: "https://blog.topolo.app",
3942
- books: "https://books.topolo.app",
3943
- brand: "https://brand.topolo.app",
3944
- bugfix: "https://bugfix.topolo.app",
3945
- bytes: "https://bytes.topolo.app",
3946
- calendar: "https://calendar.topolo.app",
3947
- campaigns: "https://campaigns.topolo.app",
3948
- capacity: "https://capacity.topolo.app",
3949
- chat: "https://chat.topolo.app",
3950
- commerce: "https://commerce.topolo.app",
3951
- compose: "https://compose.topolo.app",
3952
- consent: "https://consent.topolo.app",
3953
- crm: "https://crm.topolo.app",
3954
- design: "https://design.topolo.app",
3955
- developers: "https://developers.topolo.app",
3956
- director: "https://director.topolo.app",
3957
- feed: "https://feed.topolo.app",
3958
- flow: "https://flow.topolo.app",
3959
- forecast: "https://forecast.topolo.app",
3960
- forms: "https://forms.topolo.app",
3961
- home: "https://home.topolo.app",
3962
- insights: "https://insights.topolo.app",
3963
- inventory: "https://inventory.topolo.app",
3964
- learn: "https://learn.topolo.app",
3965
- localize: "https://localize.topolo.app",
3966
- mail: "https://mail.topolo.app",
3967
- mdm: "https://mdm.topolo.app",
3968
- messages: "https://messages.topolo.app",
3969
- nexus: "https://nexus.topolo.app",
3970
- notify: "https://notify.topolo.app",
3971
- observability_watch: "https://observability-watch.topolo.app",
3972
- one: "https://www.topolo.app",
3973
- p2p: "https://p2p.topolo.app",
3974
- pay: "https://pay.topolo.app",
3975
- people: "https://people.topolo.app",
3976
- "quro.api": "https://api.ol0.me",
3977
- "quro.redirect": "https://ol0.me",
3978
- roadmapper: "https://roadmapper.topolo.app",
3979
- sign: "https://sign.topolo.app",
3980
- social_studio: "https://studio.topolo.app",
3981
- socialize: "https://socialize.topolo.app",
3982
- spaces: "https://spaces.topolo.app",
3983
- status: "https://status.topolo.app",
3984
- success: "https://success.topolo.app",
3985
- support: "https://support.topolo.app",
3986
- survey: "https://survey.topolo.app",
3987
- voice: "https://voice.topolo.app",
3988
- web: "https://web.topolo.app"
4100
+ developers: "https://developers.topolo.app"
3989
4101
  };
3990
4102
  var DEFAULT_APP_URLS2 = APP_API_URLS2;
3991
4103
  function resolveServiceUrl2(service, overrides) {
@@ -4352,8 +4464,12 @@ function publicMcpErrorMessage(error, fallback) {
4352
4464
  }
4353
4465
 
4354
4466
  // src/tools.ts
4355
- var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
4356
4467
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
4468
+ function requiredString(args, name) {
4469
+ const value = typeof args[name] === "string" ? args[name].trim() : "";
4470
+ if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
4471
+ return value;
4472
+ }
4357
4473
  function optionalResourceContext(args) {
4358
4474
  const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
4359
4475
  const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
@@ -4753,6 +4869,78 @@ var TOOLS = [
4753
4869
  return topolo.client.listBindableResources(application);
4754
4870
  }
4755
4871
  },
4872
+ {
4873
+ name: "topolo_list_workspaces",
4874
+ title: "List workspaces for one Topolo application",
4875
+ description: "Lists caller-visible workspaces and the caller-specific creation policy for one target application. Use this before creating a workspace; organization ownership alone does not imply creation rights for every app.",
4876
+ requiredScopes: [],
4877
+ destructive: false,
4878
+ inputSchema: {
4879
+ type: "object",
4880
+ properties: { application: { type: "string", minLength: 1 } },
4881
+ required: ["application"],
4882
+ additionalProperties: false
4883
+ },
4884
+ handler: async (topolo, args) => {
4885
+ const application = requiredString(args, "application");
4886
+ return topolo.workspaces.list(application);
4887
+ }
4888
+ },
4889
+ {
4890
+ name: "topolo_create_workspace",
4891
+ title: "Create a workspace for one Topolo application",
4892
+ description: "Creates a workspace only after reading the target application policy and confirming it permits this caller. The canonical Auth runtime still authorizes the concrete app and organization. Set confirm:true only after approval.",
4893
+ requiredScopes: [],
4894
+ destructive: true,
4895
+ inputSchema: {
4896
+ type: "object",
4897
+ properties: { application: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, isDefault: { type: "boolean" }, confirm: { type: "boolean" } },
4898
+ required: ["application", "name"],
4899
+ additionalProperties: false
4900
+ },
4901
+ handler: async (topolo, args) => topolo.workspaces.create(requiredString(args, "application"), {
4902
+ name: requiredString(args, "name"),
4903
+ ...typeof args["isDefault"] === "boolean" ? { isDefault: args["isDefault"] } : {},
4904
+ confirm: args["confirm"] === true
4905
+ })
4906
+ },
4907
+ {
4908
+ name: "topolo_rename_workspace",
4909
+ title: "Rename one Topolo application workspace",
4910
+ description: "Renames a caller-authorized workspace without changing its stable id, slug, ownership, membership, or app data.",
4911
+ requiredScopes: [],
4912
+ destructive: true,
4913
+ inputSchema: {
4914
+ type: "object",
4915
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, confirm: { type: "boolean" } },
4916
+ required: ["application", "workspaceId", "name"],
4917
+ additionalProperties: false
4918
+ },
4919
+ handler: async (topolo, args) => topolo.workspaces.rename(
4920
+ requiredString(args, "application"),
4921
+ requiredString(args, "workspaceId"),
4922
+ requiredString(args, "name"),
4923
+ args["confirm"] === true
4924
+ )
4925
+ },
4926
+ {
4927
+ name: "topolo_set_default_workspace",
4928
+ title: "Set an application default workspace",
4929
+ description: "Sets exactly one caller-authorized workspace as the default for the target application and current organization.",
4930
+ requiredScopes: [],
4931
+ destructive: true,
4932
+ inputSchema: {
4933
+ type: "object",
4934
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, confirm: { type: "boolean" } },
4935
+ required: ["application", "workspaceId"],
4936
+ additionalProperties: false
4937
+ },
4938
+ handler: async (topolo, args) => topolo.workspaces.setDefault(
4939
+ requiredString(args, "application"),
4940
+ requiredString(args, "workspaceId"),
4941
+ args["confirm"] === true
4942
+ )
4943
+ },
4756
4944
  {
4757
4945
  name: "topolo_list_application_requirements",
4758
4946
  title: "List Topolo application build requirements",
@@ -4769,7 +4957,7 @@ var TOOLS = [
4769
4957
  },
4770
4958
  additionalProperties: false
4771
4959
  },
4772
- handler: async (_topolo, args) => {
4960
+ handler: async (topolo, args) => {
4773
4961
  const application = args["application"];
4774
4962
  if (application === void 0) {
4775
4963
  return {
@@ -4779,12 +4967,8 @@ var TOOLS = [
4779
4967
  requirements: APPLICATION_REQUIREMENTS
4780
4968
  };
4781
4969
  }
4782
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
4783
- throw new TopoloMcpPublicError(
4784
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
4785
- );
4786
- }
4787
- const app = APPLICATIONS[application];
4970
+ const resolved = await resolveDirectoryApplication(topolo, application);
4971
+ const app = resolved.application;
4788
4972
  return {
4789
4973
  version: APPLICATION_REQUIREMENTS_VERSION,
4790
4974
  application: app,
@@ -4814,7 +4998,7 @@ var TOOLS = [
4814
4998
  },
4815
4999
  additionalProperties: false
4816
5000
  },
4817
- handler: async (_topolo, args) => {
5001
+ handler: async (topolo, args) => {
4818
5002
  const application = args["application"];
4819
5003
  const failOn = args["failOn"];
4820
5004
  if (failOn !== void 0 && typeof failOn !== "string") {
@@ -4823,15 +5007,12 @@ var TOOLS = [
4823
5007
  const gate = failOn ? normalizeFailOn(failOn) : null;
4824
5008
  let report;
4825
5009
  if (application === void 0) {
4826
- report = auditAllApplicationRequirements();
5010
+ const directory = await listApplicationDirectory(topolo.client);
5011
+ report = auditApplicationEntries(directory.applications.map((app) => app.application));
4827
5012
  return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
4828
5013
  }
4829
- if (typeof application !== "string" || !APPLICATION_IDS.includes(application)) {
4830
- throw new TopoloMcpPublicError(
4831
- `Unknown application "${String(application)}". ${formatKnownValues(APPLICATION_IDS, "applications")}`
4832
- );
4833
- }
4834
- report = auditAllApplicationRequirements([application]);
5014
+ const resolved = await resolveDirectoryApplication(topolo, application);
5015
+ report = auditApplicationEntries([resolved.application]);
4835
5016
  return gate ? { ...report, conformanceGate: evaluateApplicationAuditGate(report, gate) } : report;
4836
5017
  }
4837
5018
  },
@@ -4971,31 +5152,19 @@ function statusesAtOrWorse(threshold) {
4971
5152
  return ["missing", "needs_review", "partial"];
4972
5153
  }
4973
5154
  function displayAppId(entry) {
4974
- for (const value of [entry.slug, entry.name, entry.appId]) {
4975
- if (typeof value !== "string" || !value.trim()) continue;
4976
- for (const candidate of appIdCandidates(value)) {
4977
- if (Object.prototype.hasOwnProperty.call(DEFAULT_APP_URLS, candidate)) {
4978
- return candidate;
4979
- }
4980
- }
4981
- }
4982
- return appIdCandidates(entry.slug || entry.appId)[0] || entry.appId;
5155
+ return friendlyAppId(entry);
4983
5156
  }
4984
- function appIdCandidates(value) {
4985
- const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
4986
- const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
4987
- return [.../* @__PURE__ */ new Set([
4988
- normalized,
4989
- normalized.replace(/-/g, "_"),
4990
- withoutTopolo,
4991
- withoutTopolo.replace(/-/g, "_")
4992
- ])].filter(Boolean);
4993
- }
4994
- function formatKnownValues(values, label) {
4995
- const sorted = [...values].sort();
4996
- const preview = sorted.slice(0, 25).join(", ");
4997
- const suffix = sorted.length > 25 ? `, ... +${sorted.length - 25} more` : "";
4998
- return `Run discovery for the full ${label} list. Known snapshot sample: ${preview}${suffix}`;
5157
+ async function resolveDirectoryApplication(topolo, application) {
5158
+ if (typeof application !== "string" || !application.trim()) {
5159
+ throw new TopoloMcpPublicError("`application` must be a non-empty string.");
5160
+ }
5161
+ try {
5162
+ return await resolveApplicationDirectoryEntry(topolo.client, application);
5163
+ } catch (error) {
5164
+ throw new TopoloMcpPublicError(
5165
+ error instanceof Error ? error.message : `Unknown application "${application}".`
5166
+ );
5167
+ }
4999
5168
  }
5000
5169
 
5001
5170
  // src/version.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.9.10",
4
- "topoloPublishedVersion": "0.9.10",
3
+ "version": "0.10.0",
4
+ "topoloPublishedVersion": "0.10.0",
5
5
  "description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "@modelcontextprotocol/sdk": "^1.0.0"
38
38
  },
39
39
  "peerDependencies": {
40
- "@topolo/sdk": "^0.9.9"
40
+ "@topolo/sdk": "^0.10.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@topolo/sdk": "file:../topolo-sdk",