@topolo/mcp 0.9.9 → 0.9.11

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
@@ -64,6 +64,11 @@ function publicMcpErrorMessage(error, fallback) {
64
64
  // src/tools.ts
65
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() : "";
@@ -467,6 +472,78 @@ var TOOLS = [
467
472
  return topolo.client.listBindableResources(application);
468
473
  }
469
474
  },
475
+ {
476
+ name: "topolo_list_workspaces",
477
+ title: "List workspaces for one Topolo application",
478
+ 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.",
479
+ requiredScopes: [],
480
+ destructive: false,
481
+ inputSchema: {
482
+ type: "object",
483
+ properties: { application: { type: "string", minLength: 1 } },
484
+ required: ["application"],
485
+ additionalProperties: false
486
+ },
487
+ handler: async (topolo, args) => {
488
+ const application = requiredString(args, "application");
489
+ return topolo.workspaces.list(application);
490
+ }
491
+ },
492
+ {
493
+ name: "topolo_create_workspace",
494
+ title: "Create a workspace for one Topolo application",
495
+ 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.",
496
+ requiredScopes: [],
497
+ destructive: true,
498
+ inputSchema: {
499
+ type: "object",
500
+ properties: { application: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, isDefault: { type: "boolean" }, confirm: { type: "boolean" } },
501
+ required: ["application", "name"],
502
+ additionalProperties: false
503
+ },
504
+ handler: async (topolo, args) => topolo.workspaces.create(requiredString(args, "application"), {
505
+ name: requiredString(args, "name"),
506
+ ...typeof args["isDefault"] === "boolean" ? { isDefault: args["isDefault"] } : {},
507
+ confirm: args["confirm"] === true
508
+ })
509
+ },
510
+ {
511
+ name: "topolo_rename_workspace",
512
+ title: "Rename one Topolo application workspace",
513
+ description: "Renames a caller-authorized workspace without changing its stable id, slug, ownership, membership, or app data.",
514
+ requiredScopes: [],
515
+ destructive: true,
516
+ inputSchema: {
517
+ type: "object",
518
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, confirm: { type: "boolean" } },
519
+ required: ["application", "workspaceId", "name"],
520
+ additionalProperties: false
521
+ },
522
+ handler: async (topolo, args) => topolo.workspaces.rename(
523
+ requiredString(args, "application"),
524
+ requiredString(args, "workspaceId"),
525
+ requiredString(args, "name"),
526
+ args["confirm"] === true
527
+ )
528
+ },
529
+ {
530
+ name: "topolo_set_default_workspace",
531
+ title: "Set an application default workspace",
532
+ description: "Sets exactly one caller-authorized workspace as the default for the target application and current organization.",
533
+ requiredScopes: [],
534
+ destructive: true,
535
+ inputSchema: {
536
+ type: "object",
537
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, confirm: { type: "boolean" } },
538
+ required: ["application", "workspaceId"],
539
+ additionalProperties: false
540
+ },
541
+ handler: async (topolo, args) => topolo.workspaces.setDefault(
542
+ requiredString(args, "application"),
543
+ requiredString(args, "workspaceId"),
544
+ args["confirm"] === true
545
+ )
546
+ },
470
547
  {
471
548
  name: "topolo_list_application_requirements",
472
549
  title: "List Topolo application build requirements",
@@ -616,11 +693,21 @@ var TOOLS = [
616
693
  Object.entries(query).map(([k, v]) => [k, String(v)])
617
694
  ) : void 0;
618
695
  const resource = optionalResourceContext(args);
696
+ let body = args["body"];
697
+ if (typeof body === "string") {
698
+ const candidate = body.trim();
699
+ if (candidate.startsWith("{") || candidate.startsWith("[")) {
700
+ try {
701
+ body = JSON.parse(candidate);
702
+ } catch {
703
+ }
704
+ }
705
+ }
619
706
  return topolo.client.request({
620
707
  service: service.trim(),
621
708
  method,
622
709
  path,
623
- body: args["body"],
710
+ body,
624
711
  ...normalisedQuery ? { query: normalisedQuery } : {},
625
712
  ...resource ? { resource } : {},
626
713
  confirm: Boolean(args["confirm"])
package/dist/http.js CHANGED
@@ -15878,6 +15878,11 @@ function splitService(servicePart) {
15878
15878
  // src/tools.ts
15879
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() : "";
@@ -16281,6 +16286,78 @@ var TOOLS = [
16281
16286
  return topolo.client.listBindableResources(application);
16282
16287
  }
16283
16288
  },
16289
+ {
16290
+ name: "topolo_list_workspaces",
16291
+ title: "List workspaces for one Topolo application",
16292
+ 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.",
16293
+ requiredScopes: [],
16294
+ destructive: false,
16295
+ inputSchema: {
16296
+ type: "object",
16297
+ properties: { application: { type: "string", minLength: 1 } },
16298
+ required: ["application"],
16299
+ additionalProperties: false
16300
+ },
16301
+ handler: async (topolo, args) => {
16302
+ const application = requiredString(args, "application");
16303
+ return topolo.workspaces.list(application);
16304
+ }
16305
+ },
16306
+ {
16307
+ name: "topolo_create_workspace",
16308
+ title: "Create a workspace for one Topolo application",
16309
+ 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.",
16310
+ requiredScopes: [],
16311
+ destructive: true,
16312
+ inputSchema: {
16313
+ type: "object",
16314
+ properties: { application: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, isDefault: { type: "boolean" }, confirm: { type: "boolean" } },
16315
+ required: ["application", "name"],
16316
+ additionalProperties: false
16317
+ },
16318
+ handler: async (topolo, args) => topolo.workspaces.create(requiredString(args, "application"), {
16319
+ name: requiredString(args, "name"),
16320
+ ...typeof args["isDefault"] === "boolean" ? { isDefault: args["isDefault"] } : {},
16321
+ confirm: args["confirm"] === true
16322
+ })
16323
+ },
16324
+ {
16325
+ name: "topolo_rename_workspace",
16326
+ title: "Rename one Topolo application workspace",
16327
+ description: "Renames a caller-authorized workspace without changing its stable id, slug, ownership, membership, or app data.",
16328
+ requiredScopes: [],
16329
+ destructive: true,
16330
+ inputSchema: {
16331
+ type: "object",
16332
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, confirm: { type: "boolean" } },
16333
+ required: ["application", "workspaceId", "name"],
16334
+ additionalProperties: false
16335
+ },
16336
+ handler: async (topolo, args) => topolo.workspaces.rename(
16337
+ requiredString(args, "application"),
16338
+ requiredString(args, "workspaceId"),
16339
+ requiredString(args, "name"),
16340
+ args["confirm"] === true
16341
+ )
16342
+ },
16343
+ {
16344
+ name: "topolo_set_default_workspace",
16345
+ title: "Set an application default workspace",
16346
+ description: "Sets exactly one caller-authorized workspace as the default for the target application and current organization.",
16347
+ requiredScopes: [],
16348
+ destructive: true,
16349
+ inputSchema: {
16350
+ type: "object",
16351
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, confirm: { type: "boolean" } },
16352
+ required: ["application", "workspaceId"],
16353
+ additionalProperties: false
16354
+ },
16355
+ handler: async (topolo, args) => topolo.workspaces.setDefault(
16356
+ requiredString(args, "application"),
16357
+ requiredString(args, "workspaceId"),
16358
+ args["confirm"] === true
16359
+ )
16360
+ },
16284
16361
  {
16285
16362
  name: "topolo_list_application_requirements",
16286
16363
  title: "List Topolo application build requirements",
@@ -16430,11 +16507,21 @@ var TOOLS = [
16430
16507
  Object.entries(query).map(([k, v]) => [k, String(v)])
16431
16508
  ) : void 0;
16432
16509
  const resource = optionalResourceContext(args);
16510
+ let body = args["body"];
16511
+ if (typeof body === "string") {
16512
+ const candidate = body.trim();
16513
+ if (candidate.startsWith("{") || candidate.startsWith("[")) {
16514
+ try {
16515
+ body = JSON.parse(candidate);
16516
+ } catch {
16517
+ }
16518
+ }
16519
+ }
16433
16520
  return topolo.client.request({
16434
16521
  service: service.trim(),
16435
16522
  method,
16436
16523
  path,
16437
- body: args["body"],
16524
+ body,
16438
16525
  ...normalisedQuery ? { query: normalisedQuery } : {},
16439
16526
  ...resource ? { resource } : {},
16440
16527
  confirm: Boolean(args["confirm"])
package/dist/index.js CHANGED
@@ -3729,10 +3729,19 @@ function mapActionInput(action, input) {
3729
3729
  delete remaining[key];
3730
3730
  return encodeURIComponent(String(value));
3731
3731
  });
3732
- const explicitQuery = plainObject(remaining.query);
3733
- delete remaining.query;
3734
- const explicitBody = Object.prototype.hasOwnProperty.call(remaining, "body") ? remaining.body : void 0;
3735
- delete remaining.body;
3732
+ const schemaProperties = plainObject(plainObject(action.inputSchema).properties);
3733
+ const schemaOwnsQuery = Object.prototype.hasOwnProperty.call(schemaProperties, "query");
3734
+ const schemaOwnsBody = Object.prototype.hasOwnProperty.call(schemaProperties, "body");
3735
+ let explicitQuery = {};
3736
+ if (!schemaOwnsQuery) {
3737
+ explicitQuery = plainObject(remaining.query);
3738
+ delete remaining.query;
3739
+ }
3740
+ let explicitBody;
3741
+ if (!schemaOwnsBody) {
3742
+ explicitBody = Object.prototype.hasOwnProperty.call(remaining, "body") ? remaining.body : void 0;
3743
+ delete remaining.body;
3744
+ }
3736
3745
  if (action.method === "GET") {
3737
3746
  return {
3738
3747
  path,
@@ -3856,11 +3865,71 @@ var IdentityModule = class {
3856
3865
  return this.client.introspect();
3857
3866
  }
3858
3867
  };
3868
+ var ACTIONS = {
3869
+ list: "app_topolo_auth.workspaces.list",
3870
+ create: "app_topolo_auth.workspaces.create",
3871
+ rename: "app_topolo_auth.workspaces.rename",
3872
+ setDefault: "app_topolo_auth.workspaces.default.set"
3873
+ };
3874
+ var WorkspacesModule = class {
3875
+ constructor(client) {
3876
+ this.client = client;
3877
+ }
3878
+ client;
3879
+ async list(application) {
3880
+ const context = await this.resolveContext(application);
3881
+ return this.client.callAction(ACTIONS.list, {
3882
+ org_id: context.organizationId,
3883
+ app_id: context.appId
3884
+ });
3885
+ }
3886
+ async create(application, input) {
3887
+ const current = await this.list(application);
3888
+ if (!current.data.policy.canCreate) {
3889
+ throw new TopoloSdkError(
3890
+ "workspace_creation_not_allowed",
3891
+ `Workspace creation is not allowed for ${current.data.appId}: ${current.data.policy.reason}`
3892
+ );
3893
+ }
3894
+ return this.client.callAction(ACTIONS.create, {
3895
+ organizationId: current.data.organizationId,
3896
+ appId: current.data.appId,
3897
+ name: input.name,
3898
+ ...input.isDefault === void 0 ? {} : { isDefault: input.isDefault }
3899
+ }, { confirm: input.confirm === true });
3900
+ }
3901
+ async rename(application, workspaceId, name, confirm = false) {
3902
+ const context = await this.resolveContext(application);
3903
+ return this.client.callAction(ACTIONS.rename, {
3904
+ workspaceId,
3905
+ organizationId: context.organizationId,
3906
+ appId: context.appId,
3907
+ name
3908
+ }, { confirm });
3909
+ }
3910
+ async setDefault(application, workspaceId, confirm = false) {
3911
+ const context = await this.resolveContext(application);
3912
+ return this.client.callAction(ACTIONS.setDefault, {
3913
+ workspaceId,
3914
+ organizationId: context.organizationId,
3915
+ appId: context.appId
3916
+ }, { confirm });
3917
+ }
3918
+ async resolveContext(application) {
3919
+ const identity = await this.client.introspect();
3920
+ if (!identity.organization?.id) {
3921
+ throw new TopoloSdkError("organization_required", "Workspace lifecycle requires an organization-scoped credential.");
3922
+ }
3923
+ const service = await this.client.getService(application);
3924
+ return { organizationId: identity.organization.id, appId: service.appId };
3925
+ }
3926
+ };
3859
3927
  function createTopolo(options) {
3860
3928
  const client = new TopoloClient(options);
3861
3929
  return {
3862
3930
  client,
3863
3931
  identity: new IdentityModule(client),
3932
+ workspaces: new WorkspacesModule(client),
3864
3933
  crm: new CrmModule(client)
3865
3934
  };
3866
3935
  }
@@ -4345,6 +4414,11 @@ function publicMcpErrorMessage(error, fallback) {
4345
4414
  // src/tools.ts
4346
4415
  var APPLICATION_IDS = Object.keys(APPLICATIONS).sort();
4347
4416
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
4417
+ function requiredString(args, name) {
4418
+ const value = typeof args[name] === "string" ? args[name].trim() : "";
4419
+ if (!value) throw new TopoloMcpPublicError(`${name} is required.`);
4420
+ return value;
4421
+ }
4348
4422
  function optionalResourceContext(args) {
4349
4423
  const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
4350
4424
  const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
@@ -4748,6 +4822,78 @@ var TOOLS = [
4748
4822
  return topolo.client.listBindableResources(application);
4749
4823
  }
4750
4824
  },
4825
+ {
4826
+ name: "topolo_list_workspaces",
4827
+ title: "List workspaces for one Topolo application",
4828
+ 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.",
4829
+ requiredScopes: [],
4830
+ destructive: false,
4831
+ inputSchema: {
4832
+ type: "object",
4833
+ properties: { application: { type: "string", minLength: 1 } },
4834
+ required: ["application"],
4835
+ additionalProperties: false
4836
+ },
4837
+ handler: async (topolo, args) => {
4838
+ const application = requiredString(args, "application");
4839
+ return topolo.workspaces.list(application);
4840
+ }
4841
+ },
4842
+ {
4843
+ name: "topolo_create_workspace",
4844
+ title: "Create a workspace for one Topolo application",
4845
+ 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.",
4846
+ requiredScopes: [],
4847
+ destructive: true,
4848
+ inputSchema: {
4849
+ type: "object",
4850
+ properties: { application: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, isDefault: { type: "boolean" }, confirm: { type: "boolean" } },
4851
+ required: ["application", "name"],
4852
+ additionalProperties: false
4853
+ },
4854
+ handler: async (topolo, args) => topolo.workspaces.create(requiredString(args, "application"), {
4855
+ name: requiredString(args, "name"),
4856
+ ...typeof args["isDefault"] === "boolean" ? { isDefault: args["isDefault"] } : {},
4857
+ confirm: args["confirm"] === true
4858
+ })
4859
+ },
4860
+ {
4861
+ name: "topolo_rename_workspace",
4862
+ title: "Rename one Topolo application workspace",
4863
+ description: "Renames a caller-authorized workspace without changing its stable id, slug, ownership, membership, or app data.",
4864
+ requiredScopes: [],
4865
+ destructive: true,
4866
+ inputSchema: {
4867
+ type: "object",
4868
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, name: { type: "string", minLength: 1, maxLength: 120 }, confirm: { type: "boolean" } },
4869
+ required: ["application", "workspaceId", "name"],
4870
+ additionalProperties: false
4871
+ },
4872
+ handler: async (topolo, args) => topolo.workspaces.rename(
4873
+ requiredString(args, "application"),
4874
+ requiredString(args, "workspaceId"),
4875
+ requiredString(args, "name"),
4876
+ args["confirm"] === true
4877
+ )
4878
+ },
4879
+ {
4880
+ name: "topolo_set_default_workspace",
4881
+ title: "Set an application default workspace",
4882
+ description: "Sets exactly one caller-authorized workspace as the default for the target application and current organization.",
4883
+ requiredScopes: [],
4884
+ destructive: true,
4885
+ inputSchema: {
4886
+ type: "object",
4887
+ properties: { application: { type: "string", minLength: 1 }, workspaceId: { type: "string", minLength: 1 }, confirm: { type: "boolean" } },
4888
+ required: ["application", "workspaceId"],
4889
+ additionalProperties: false
4890
+ },
4891
+ handler: async (topolo, args) => topolo.workspaces.setDefault(
4892
+ requiredString(args, "application"),
4893
+ requiredString(args, "workspaceId"),
4894
+ args["confirm"] === true
4895
+ )
4896
+ },
4751
4897
  {
4752
4898
  name: "topolo_list_application_requirements",
4753
4899
  title: "List Topolo application build requirements",
@@ -4897,11 +5043,21 @@ var TOOLS = [
4897
5043
  Object.entries(query).map(([k, v]) => [k, String(v)])
4898
5044
  ) : void 0;
4899
5045
  const resource = optionalResourceContext(args);
5046
+ let body = args["body"];
5047
+ if (typeof body === "string") {
5048
+ const candidate = body.trim();
5049
+ if (candidate.startsWith("{") || candidate.startsWith("[")) {
5050
+ try {
5051
+ body = JSON.parse(candidate);
5052
+ } catch {
5053
+ }
5054
+ }
5055
+ }
4900
5056
  return topolo.client.request({
4901
5057
  service: service.trim(),
4902
5058
  method,
4903
5059
  path,
4904
- body: args["body"],
5060
+ body,
4905
5061
  ...normalisedQuery ? { query: normalisedQuery } : {},
4906
5062
  ...resource ? { resource } : {},
4907
5063
  confirm: Boolean(args["confirm"])
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.9.9",
4
- "topoloPublishedVersion": "0.9.9",
3
+ "version": "0.9.11",
4
+ "topoloPublishedVersion": "0.9.11",
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.8"
40
+ "@topolo/sdk": "^0.9.10"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@topolo/sdk": "file:../topolo-sdk",