@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 +10 -0
- package/dist/dispatch.js +101 -43
- package/dist/http.js +101 -43
- package/dist/index.js +320 -151
- package/package.json +3 -3
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
|
-
|
|
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 (
|
|
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
|
-
|
|
493
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
540
|
-
|
|
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
|
-
|
|
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
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
16307
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
16354
|
-
|
|
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
|
-
|
|
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
|
|
16519
|
-
|
|
16520
|
-
|
|
16521
|
-
|
|
16522
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1982
|
+
"The live platform registry exposes the app through topolo apps and MCP discovery"
|
|
1894
1983
|
],
|
|
1895
1984
|
implementation: [
|
|
1896
|
-
"
|
|
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
|
|
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" ?
|
|
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" ?
|
|
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
|
|
2113
|
-
|
|
2114
|
-
(
|
|
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
|
-
*
|
|
3419
|
-
*
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
4783
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
4830
|
-
|
|
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
|
-
|
|
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
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
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.
|
|
4
|
-
"topoloPublishedVersion": "0.
|
|
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.
|
|
40
|
+
"@topolo/sdk": "^0.10.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@topolo/sdk": "file:../topolo-sdk",
|