@topolo/mcp 0.4.7 → 0.4.9

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.
Files changed (3) hide show
  1. package/dist/dispatch.js +17 -0
  2. package/dist/index.js +187 -136
  3. package/package.json +13 -13
package/dist/dispatch.js CHANGED
@@ -91,6 +91,8 @@ var TOOLS = [
91
91
  effectiveUrl: resolveCatalogServiceUrl(entry, { serviceKey: id }),
92
92
  surfaceKind: entry.surfaceKind,
93
93
  launchable: entry.launchable,
94
+ apiKeyResource: entry.apiKeyResource,
95
+ apiKeyResources: [...entry.apiKeyResources],
94
96
  permissions: [...entry.permissions]
95
97
  };
96
98
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -284,6 +286,14 @@ var TOOLS = [
284
286
  additionalProperties: { type: "string" },
285
287
  description: "Query string parameters as string values."
286
288
  },
289
+ resourceType: {
290
+ type: "string",
291
+ description: "Optional workspace resource alias from topolo_list_services apiKeyResources. Aliases are app-defined, e.g. brand, mailbox, venue, merchant, or any other cataloged alias."
292
+ },
293
+ resourceId: {
294
+ type: "string",
295
+ description: "Resource id for resourceType. Required when resourceType is provided."
296
+ },
287
297
  confirm: {
288
298
  type: "boolean",
289
299
  description: "Required for non-GET methods. Explicit human-in-the-loop acknowledgement for writes."
@@ -310,12 +320,19 @@ var TOOLS = [
310
320
  const normalisedQuery = query && typeof query === "object" && !Array.isArray(query) ? Object.fromEntries(
311
321
  Object.entries(query).map(([k, v]) => [k, String(v)])
312
322
  ) : void 0;
323
+ const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
324
+ const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
325
+ if (resourceType && !resourceId || !resourceType && resourceId) {
326
+ throw new Error("resourceType and resourceId must be provided together.");
327
+ }
328
+ const resource = resourceType && resourceId ? { resourceType, resourceId } : void 0;
313
329
  return topolo.client.request({
314
330
  service: service.trim(),
315
331
  method,
316
332
  path,
317
333
  body: args["body"],
318
334
  ...normalisedQuery ? { query: normalisedQuery } : {},
335
+ ...resource ? { resource } : {},
319
336
  confirm: Boolean(args["confirm"])
320
337
  });
321
338
  }
package/dist/index.js CHANGED
@@ -59,54 +59,53 @@ var TopoloHttpError = class extends TopoloSdkError {
59
59
  }
60
60
  };
61
61
  var SERVICES = {
62
- agent: "https://agent-api.topolo.app",
62
+ agent: "https://agent.topolo.app",
63
63
  auth: "https://auth.topolo.app",
64
64
  backup: "https://backup.topolo.app",
65
65
  books: "https://books.topolo.app",
66
66
  bugfix: "https://bugfix.topolo.app",
67
- bytes: "https://bytes-api.topolo.app",
67
+ bytes: "https://bytes.topolo.app",
68
68
  calendar: "https://calendar.topolo.app",
69
69
  capacity: "https://capacity.topolo.app",
70
70
  chat: "https://chat.topolo.app",
71
- commerce: "https://commerce-api.topolo.app",
71
+ commerce: "https://commerce.topolo.app",
72
72
  compose: "https://compose.topolo.app",
73
73
  consent: "https://consent.topolo.app",
74
- crm: "https://crm-api.topolo.app",
74
+ crm: "https://crm.topolo.app",
75
75
  developers: "https://developers.topolo.app",
76
76
  director: "https://director.topolo.app",
77
- feed: "https://feed-api.topolo.app",
77
+ feed: "https://feed.topolo.app",
78
78
  flow: "https://flow.topolo.app",
79
- forecast: "https://forecast-api.topolo.app",
79
+ forecast: "https://forecast.topolo.app",
80
80
  forms: "https://forms.topolo.app",
81
81
  home: "https://home.topolo.app",
82
82
  insights: "https://insights.topolo.app",
83
83
  inventory: "https://inventory.topolo.app",
84
- learn: "https://learn-api.topolo.app",
84
+ learn: "https://learn.topolo.app",
85
85
  localize: "https://localize.topolo.app",
86
- mail: "https://mail-api.topolo.app",
87
- mdm: "https://mdm-api.topolo.app",
86
+ mail: "https://mail.topolo.app",
87
+ mdm: "https://mdm.topolo.app",
88
88
  messages: "https://messages.topolo.app",
89
- nexus: "https://nexus-api.topolo.app",
89
+ nexus: "https://nexus.topolo.app",
90
90
  notify: "https://notify.topolo.app",
91
91
  observability_watch: "https://observability-watch.topolo.app",
92
- one: "https://one.topolo.app",
92
+ one: "https://www.topolo.app",
93
93
  p2p: "https://p2p.topolo.app",
94
94
  pay: "https://pay.topolo.app",
95
95
  people: "https://people.topolo.app",
96
- pulse: "https://pulse.topolo.app",
97
96
  "quro.api": "https://api.ol0.me",
98
97
  "quro.redirect": "https://ol0.me",
99
- roadmapper: "https://roadmapper-api.topolo.app",
98
+ roadmapper: "https://roadmapper.topolo.app",
100
99
  sign: "https://sign.topolo.app",
101
- social_studio: "https://studio-api.topolo.app",
102
- socialize: "https://socialize-api.topolo.app",
100
+ social_studio: "https://studio.topolo.app",
101
+ socialize: "https://socialize.topolo.app",
103
102
  spaces: "https://spaces.topolo.app",
104
103
  status: "https://status.topolo.app",
105
104
  success: "https://success.topolo.app",
106
105
  support: "https://support.topolo.app",
107
106
  survey: "https://survey.topolo.app",
108
107
  voice: "https://voice.topolo.app",
109
- web: "https://web-api.topolo.app"
108
+ web: "https://web.topolo.app"
110
109
  };
111
110
  var APPLICATIONS = {
112
111
  "admin": {
@@ -155,17 +154,15 @@ var APPLICATIONS = {
155
154
  "agent.topolo.app"
156
155
  ],
157
156
  "routes": [],
158
- "serviceId": null
157
+ "serviceId": "agent"
159
158
  },
160
159
  {
161
160
  "name": "topolo-agent-api",
162
161
  "kind": "worker",
163
- "apiUrl": "https://agent-api.topolo.app",
164
- "domains": [
165
- "agent-api.topolo.app"
166
- ],
162
+ "apiUrl": null,
163
+ "domains": [],
167
164
  "routes": [],
168
- "serviceId": "agent"
165
+ "serviceId": null
169
166
  }
170
167
  ]
171
168
  },
@@ -285,17 +282,15 @@ var APPLICATIONS = {
285
282
  "bytes.topolo.app"
286
283
  ],
287
284
  "routes": [],
288
- "serviceId": null
285
+ "serviceId": "bytes"
289
286
  },
290
287
  {
291
288
  "name": "topolo-bytes-api",
292
289
  "kind": "worker",
293
- "apiUrl": "https://bytes-api.topolo.app",
294
- "domains": [
295
- "bytes-api.topolo.app"
296
- ],
290
+ "apiUrl": null,
291
+ "domains": [],
297
292
  "routes": [],
298
- "serviceId": "bytes"
293
+ "serviceId": null
299
294
  }
300
295
  ]
301
296
  },
@@ -394,12 +389,10 @@ var APPLICATIONS = {
394
389
  {
395
390
  "name": "topolo-commerce-api",
396
391
  "kind": "worker",
397
- "apiUrl": "https://commerce-api.topolo.app",
398
- "domains": [
399
- "commerce-api.topolo.app"
400
- ],
392
+ "apiUrl": null,
393
+ "domains": [],
401
394
  "routes": [],
402
- "serviceId": "commerce"
395
+ "serviceId": null
403
396
  },
404
397
  {
405
398
  "name": "topolo-commerce-guest-web",
@@ -419,7 +412,7 @@ var APPLICATIONS = {
419
412
  "commerce.topolo.app"
420
413
  ],
421
414
  "routes": [],
422
- "serviceId": null
415
+ "serviceId": "commerce"
423
416
  },
424
417
  {
425
418
  "name": "topolo-commerce-station-web",
@@ -506,12 +499,10 @@ var APPLICATIONS = {
506
499
  {
507
500
  "name": "topolo-crm-api",
508
501
  "kind": "worker",
509
- "apiUrl": "https://crm-api.topolo.app",
510
- "domains": [
511
- "crm-api.topolo.app"
512
- ],
502
+ "apiUrl": null,
503
+ "domains": [],
513
504
  "routes": [],
514
- "serviceId": "crm"
505
+ "serviceId": null
515
506
  },
516
507
  {
517
508
  "name": "topolo-crm-app",
@@ -521,7 +512,7 @@ var APPLICATIONS = {
521
512
  "crm.topolo.app"
522
513
  ],
523
514
  "routes": [],
524
- "serviceId": null
515
+ "serviceId": "crm"
525
516
  }
526
517
  ]
527
518
  },
@@ -649,7 +640,7 @@ var APPLICATIONS = {
649
640
  "feed-api.topolo.app"
650
641
  ],
651
642
  "routes": [],
652
- "serviceId": "feed"
643
+ "serviceId": null
653
644
  },
654
645
  {
655
646
  "name": "topolo-feed-web",
@@ -659,7 +650,7 @@ var APPLICATIONS = {
659
650
  "feed.topolo.app"
660
651
  ],
661
652
  "routes": [],
662
- "serviceId": null
653
+ "serviceId": "feed"
663
654
  }
664
655
  ]
665
656
  },
@@ -711,17 +702,15 @@ var APPLICATIONS = {
711
702
  "forecast.topolo.app"
712
703
  ],
713
704
  "routes": [],
714
- "serviceId": null
705
+ "serviceId": "forecast"
715
706
  },
716
707
  {
717
708
  "name": "topolo-forecast-api",
718
709
  "kind": "worker",
719
- "apiUrl": "https://forecast-api.topolo.app",
720
- "domains": [
721
- "forecast-api.topolo.app"
722
- ],
710
+ "apiUrl": null,
711
+ "domains": [],
723
712
  "routes": [],
724
- "serviceId": "forecast"
713
+ "serviceId": null
725
714
  }
726
715
  ]
727
716
  },
@@ -863,17 +852,15 @@ var APPLICATIONS = {
863
852
  "learn.topolo.app"
864
853
  ],
865
854
  "routes": [],
866
- "serviceId": null
855
+ "serviceId": "learn"
867
856
  },
868
857
  {
869
858
  "name": "topolo-learn-api",
870
859
  "kind": "worker",
871
- "apiUrl": "https://learn-api.topolo.app",
872
- "domains": [
873
- "learn-api.topolo.app"
874
- ],
860
+ "apiUrl": null,
861
+ "domains": [],
875
862
  "routes": [],
876
- "serviceId": "learn"
863
+ "serviceId": null
877
864
  }
878
865
  ]
879
866
  },
@@ -919,17 +906,15 @@ var APPLICATIONS = {
919
906
  "mail.topolo.app"
920
907
  ],
921
908
  "routes": [],
922
- "serviceId": null
909
+ "serviceId": "mail"
923
910
  },
924
911
  {
925
912
  "name": "topolo-mail-api",
926
913
  "kind": "worker",
927
- "apiUrl": "https://mail-api.topolo.app",
928
- "domains": [
929
- "mail-api.topolo.app"
930
- ],
914
+ "apiUrl": null,
915
+ "domains": [],
931
916
  "routes": [],
932
- "serviceId": "mail"
917
+ "serviceId": null
933
918
  }
934
919
  ]
935
920
  },
@@ -946,12 +931,10 @@ var APPLICATIONS = {
946
931
  {
947
932
  "name": "topolo-mdm-api",
948
933
  "kind": "worker",
949
- "apiUrl": "https://mdm-api.topolo.app",
950
- "domains": [
951
- "mdm-api.topolo.app"
952
- ],
934
+ "apiUrl": null,
935
+ "domains": [],
953
936
  "routes": [],
954
- "serviceId": "mdm"
937
+ "serviceId": null
955
938
  },
956
939
  {
957
940
  "name": "topolo-mdm-console",
@@ -961,7 +944,7 @@ var APPLICATIONS = {
961
944
  "mdm.topolo.app"
962
945
  ],
963
946
  "routes": [],
964
- "serviceId": null
947
+ "serviceId": "mdm"
965
948
  }
966
949
  ]
967
950
  },
@@ -1026,9 +1009,9 @@ var APPLICATIONS = {
1026
1009
  {
1027
1010
  "name": "topolo-nexus-gateway",
1028
1011
  "kind": "worker",
1029
- "apiUrl": "https://nexus-api.topolo.app",
1012
+ "apiUrl": "https://nexus.topolo.app",
1030
1013
  "domains": [
1031
- "nexus-api.topolo.app"
1014
+ "nexus.topolo.app"
1032
1015
  ],
1033
1016
  "routes": [],
1034
1017
  "serviceId": "nexus"
@@ -1094,20 +1077,23 @@ var APPLICATIONS = {
1094
1077
  {
1095
1078
  "name": "topolo-one",
1096
1079
  "kind": "worker",
1097
- "apiUrl": null,
1098
- "domains": [],
1099
- "routes": [],
1080
+ "apiUrl": "https://one.topolo.app",
1081
+ "domains": [
1082
+ "one.topolo.app"
1083
+ ],
1084
+ "routes": [
1085
+ "one.topolo.app/*"
1086
+ ],
1100
1087
  "serviceId": null
1101
1088
  },
1102
1089
  {
1103
1090
  "name": "topolo-one-api",
1104
1091
  "kind": "worker",
1105
- "apiUrl": "https://one.topolo.app",
1092
+ "apiUrl": "https://www.topolo.app",
1106
1093
  "domains": [
1107
- "one.topolo.app"
1094
+ "www.topolo.app"
1108
1095
  ],
1109
1096
  "routes": [
1110
- "one.topolo.app/*",
1111
1097
  "www.topolo.app/*"
1112
1098
  ],
1113
1099
  "serviceId": "one"
@@ -1217,36 +1203,6 @@ var APPLICATIONS = {
1217
1203
  }
1218
1204
  ]
1219
1205
  },
1220
- "pulse": {
1221
- "id": "pulse",
1222
- "appDir": "TopoloPulse",
1223
- "name": "topolo-pulse",
1224
- "packageName": "topolo-pulse",
1225
- "productionUrl": "https://pulse.topolo.app",
1226
- "services": [
1227
- "pulse"
1228
- ],
1229
- "deployTargets": [
1230
- {
1231
- "name": "topolo-pulse",
1232
- "kind": "worker",
1233
- "apiUrl": "https://pulse.topolo.app",
1234
- "domains": [
1235
- "pulse.topolo.app"
1236
- ],
1237
- "routes": [],
1238
- "serviceId": "pulse"
1239
- },
1240
- {
1241
- "name": "topolo-pulse-api",
1242
- "kind": "worker",
1243
- "apiUrl": null,
1244
- "domains": [],
1245
- "routes": [],
1246
- "serviceId": null
1247
- }
1248
- ]
1249
- },
1250
1206
  "quro": {
1251
1207
  "id": "quro",
1252
1208
  "appDir": "TopoloQuro",
@@ -1312,17 +1268,15 @@ var APPLICATIONS = {
1312
1268
  "roadmapper.topolo.app"
1313
1269
  ],
1314
1270
  "routes": [],
1315
- "serviceId": null
1271
+ "serviceId": "roadmapper"
1316
1272
  },
1317
1273
  {
1318
1274
  "name": "topolo-roadmapper-api",
1319
1275
  "kind": "worker",
1320
- "apiUrl": "https://roadmapper-api.topolo.app",
1321
- "domains": [
1322
- "roadmapper-api.topolo.app"
1323
- ],
1276
+ "apiUrl": null,
1277
+ "domains": [],
1324
1278
  "routes": [],
1325
- "serviceId": "roadmapper"
1279
+ "serviceId": null
1326
1280
  }
1327
1281
  ]
1328
1282
  },
@@ -1374,17 +1328,15 @@ var APPLICATIONS = {
1374
1328
  "studio.topolo.app"
1375
1329
  ],
1376
1330
  "routes": [],
1377
- "serviceId": null
1331
+ "serviceId": "social_studio"
1378
1332
  },
1379
1333
  {
1380
1334
  "name": "topolo-social-studio-api",
1381
1335
  "kind": "worker",
1382
- "apiUrl": "https://studio-api.topolo.app",
1383
- "domains": [
1384
- "studio-api.topolo.app"
1385
- ],
1336
+ "apiUrl": null,
1337
+ "domains": [],
1386
1338
  "routes": [],
1387
- "serviceId": "social_studio"
1339
+ "serviceId": null
1388
1340
  },
1389
1341
  {
1390
1342
  "name": "topolo-social-studio-queue",
@@ -1414,7 +1366,7 @@ var APPLICATIONS = {
1414
1366
  "socialize.topolo.app"
1415
1367
  ],
1416
1368
  "routes": [],
1417
- "serviceId": null
1369
+ "serviceId": "socialize"
1418
1370
  },
1419
1371
  {
1420
1372
  "name": "topolo-socialize-api",
@@ -1424,7 +1376,7 @@ var APPLICATIONS = {
1424
1376
  "socialize-api.topolo.app"
1425
1377
  ],
1426
1378
  "routes": [],
1427
- "serviceId": "socialize"
1379
+ "serviceId": null
1428
1380
  },
1429
1381
  {
1430
1382
  "name": "topolo-socialize-scheduler",
@@ -1625,12 +1577,10 @@ var APPLICATIONS = {
1625
1577
  {
1626
1578
  "name": "topolo-web-api",
1627
1579
  "kind": "worker",
1628
- "apiUrl": "https://web-api.topolo.app",
1629
- "domains": [
1630
- "web-api.topolo.app"
1631
- ],
1580
+ "apiUrl": null,
1581
+ "domains": [],
1632
1582
  "routes": [],
1633
- "serviceId": "web"
1583
+ "serviceId": null
1634
1584
  },
1635
1585
  {
1636
1586
  "name": "topolo-web-runtime",
@@ -1653,12 +1603,12 @@ var APPLICATIONS = {
1653
1603
  "web.topolo.app"
1654
1604
  ],
1655
1605
  "routes": [],
1656
- "serviceId": null
1606
+ "serviceId": "web"
1657
1607
  }
1658
1608
  ]
1659
1609
  }
1660
1610
  };
1661
- var APPLICATION_REQUIREMENTS_VERSION = "2026-05-01.1";
1611
+ var APPLICATION_REQUIREMENTS_VERSION = "2026-06-07.1";
1662
1612
  var APPLICATION_REQUIREMENTS = [
1663
1613
  {
1664
1614
  id: "platform-metadata",
@@ -1714,16 +1664,20 @@ var APPLICATION_REQUIREMENTS = [
1714
1664
  summary: "Browser applications should feel like one platform and should not create isolated navigation or account surfaces.",
1715
1665
  appliesTo: ["browser"],
1716
1666
  evidence: [
1717
- "@topolo/ui-kit is used for the platform shell where the app has authenticated UI",
1718
- "The shared launcher/app switcher is reachable from authenticated app chrome",
1719
- "Account menu and sign-out behavior use TopoloAppShell or TopoloAccountMenu",
1667
+ "@topolo-io/app-shell is used for the platform shell where the app has authenticated UI",
1668
+ "@topolo-io/app-launcher keeps the shared launcher/app switcher reachable from authenticated app chrome",
1669
+ "Account menu and sign-out behavior use TopoloAppShell or the ui-kit TopoloAccountMenu primitive",
1670
+ "Repeated app body surfaces use shared TopoloAppLayout primitives instead of local stat cards, empty states, or status badges",
1671
+ "Local preview supports deterministic visual QA fixtures for overview, list, detail, settings, and empty/loading states",
1720
1672
  "Authenticated app body content stays inside the viewport on mobile and desktop widths"
1721
1673
  ],
1722
1674
  implementation: [
1723
- "Use TopoloAppShell for authenticated app layouts",
1724
- "Use TopoloAccountMenu when a partial-shell surface still needs account chrome",
1675
+ "Use @topolo-io/app-shell TopoloAppShell for authenticated app layouts",
1676
+ "Use the @topolo-io/ui-kit TopoloAccountMenu primitive when a partial-shell surface still needs account chrome",
1725
1677
  "Pass app-specific account actions as additive menu items instead of forking dropdowns",
1726
1678
  "Use shared design tokens and platform UI primitives before creating local equivalents",
1679
+ "Use TopoloAppMetricCard, TopoloAppEmptyState, TopoloAppStatusBadge, TopoloAppGrid, and TopoloAppSection for repeated app body patterns",
1680
+ "Keep visual QA fixtures available through visualQa=1 or __devpreview=1 so screenshot checks do not depend on live API state",
1727
1681
  "Keep mobile and desktop shell behavior aligned with the platform UI kit",
1728
1682
  "Constrain app-owned workspace wrappers with min-width: 0, width: auto, and viewport-aware max-width so shell padding cannot create page-level horizontal overflow"
1729
1683
  ]
@@ -1802,11 +1756,13 @@ var APPLICATION_REQUIREMENTS = [
1802
1756
  evidence: [
1803
1757
  "The app-owned API exposes GET /api/widget",
1804
1758
  "The endpoint returns TopoloWidgetApiResponse from @topolo/sdk",
1759
+ "Every returned widget includes snapshot.summary with app-owned operational prose",
1805
1760
  "The endpoint derives organization/user context from the app auth boundary"
1806
1761
  ],
1807
1762
  implementation: [
1808
1763
  "Import createTopoloWidgetResponse from @topolo/sdk in the app backend",
1809
1764
  "Return native app metrics or action widgets instead of generic launch-card fallbacks",
1765
+ "Populate snapshot.summary in the producing app for every widget instead of synthesizing summaries in TopoloOne",
1810
1766
  "Add a route-level test that validates the response with validateTopoloWidgetResponse"
1811
1767
  ]
1812
1768
  },
@@ -2099,6 +2055,8 @@ function firstString(...values) {
2099
2055
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2100
2056
  var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
2101
2057
  var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
2058
+ var TOPOLO_RESOURCE_TYPE_HEADER = "X-Topolo-Resource-Type";
2059
+ var TOPOLO_RESOURCE_ID_HEADER = "X-Topolo-Resource-ID";
2102
2060
  var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
2103
2061
  var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
2104
2062
  var CATALOG_CACHE_TTL_MS = 60 * 1e3;
@@ -2177,6 +2135,18 @@ var TopoloClient = class _TopoloClient {
2177
2135
  if (opts.headers) {
2178
2136
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
2179
2137
  }
2138
+ if (opts.resource) {
2139
+ const resourceType = opts.resource.resourceType.trim();
2140
+ const resourceId = opts.resource.resourceId.trim();
2141
+ if (!resourceType || !resourceId) {
2142
+ throw new TopoloSdkError(
2143
+ "invalid_resource_context",
2144
+ "Both resource.resourceType and resource.resourceId are required when resource context is provided."
2145
+ );
2146
+ }
2147
+ headers.set(TOPOLO_RESOURCE_TYPE_HEADER, resourceType);
2148
+ headers.set(TOPOLO_RESOURCE_ID_HEADER, resourceId);
2149
+ }
2180
2150
  const controller = new AbortController();
2181
2151
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
2182
2152
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
@@ -2248,10 +2218,14 @@ var TopoloClient = class _TopoloClient {
2248
2218
  if (!serviceKey) {
2249
2219
  throw new TopoloSdkError("unknown_service", "service is required");
2250
2220
  }
2251
- if (serviceKey === "auth") {
2221
+ if (serviceKey === "auth" || serviceKey === "developers") {
2252
2222
  const overrideUrl = resolveConfiguredServiceUrl(serviceKey, this.serviceUrls);
2253
2223
  if (overrideUrl) return { serviceKey, baseUrl: overrideUrl };
2254
- return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS.auth };
2224
+ if (serviceKey === "developers") {
2225
+ const derived = deriveDevelopersFromAuth(resolveConfiguredServiceUrl("auth", this.serviceUrls));
2226
+ if (derived) return { serviceKey, baseUrl: derived };
2227
+ }
2228
+ return { serviceKey, baseUrl: DEFAULT_SERVICE_URLS[serviceKey] };
2255
2229
  }
2256
2230
  const live = await this.resolveLiveCallableService(serviceKey);
2257
2231
  if (live) {
@@ -2291,7 +2265,7 @@ var TopoloClient = class _TopoloClient {
2291
2265
  if (!normalized) return null;
2292
2266
  const configuredServiceId = resolveConfiguredServiceId(normalized, this.serviceIds);
2293
2267
  if (configuredServiceId) return configuredServiceId;
2294
- const authBase = resolveServiceUrl("auth", this.serviceUrls);
2268
+ const authBase = resolveServiceUrl("developers", this.serviceUrls);
2295
2269
  const cacheKey = `${authBase}|${normalized}`;
2296
2270
  const now = Date.now();
2297
2271
  const cached = _TopoloClient.serviceIdCache.get(cacheKey);
@@ -2345,7 +2319,7 @@ var TopoloClient = class _TopoloClient {
2345
2319
  if (this.serviceCatalogCache && this.serviceCatalogCache.expiresAt > now) {
2346
2320
  return this.serviceCatalogCache.entries;
2347
2321
  }
2348
- const res = await this.request({ service: "auth", method: "GET", path: "/api/services/catalog" });
2322
+ const res = await this.request({ service: "developers", method: "GET", path: "/api/services/catalog" });
2349
2323
  const raw = res.data?.services ?? res.services;
2350
2324
  if (!Array.isArray(raw)) {
2351
2325
  throw new TopoloSdkError(
@@ -2359,6 +2333,11 @@ var TopoloClient = class _TopoloClient {
2359
2333
  const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
2360
2334
  if (!serviceId) continue;
2361
2335
  const permissions = Array.isArray(s.granted_permissions) ? s.granted_permissions.filter((permission) => typeof permission === "string" && permission.trim().length > 0) : [];
2336
+ const catalogApiKeyResources = normalizeApiKeyResourceMetadataList(
2337
+ s.api_key_resources ?? s.apiKeyResources
2338
+ );
2339
+ const apiKeyResource = normalizeApiKeyResourceMetadata(s.api_key_resource ?? s.apiKeyResource) ?? catalogApiKeyResources[0] ?? null;
2340
+ const apiKeyResources = catalogApiKeyResources.length > 0 ? catalogApiKeyResources : apiKeyResource ? [apiKeyResource] : [];
2362
2341
  entries.push({
2363
2342
  serviceId,
2364
2343
  slug: typeof s.app_slug === "string" ? s.app_slug : null,
@@ -2369,7 +2348,9 @@ var TopoloClient = class _TopoloClient {
2369
2348
  status: typeof s.status === "string" ? s.status : null,
2370
2349
  surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2371
2350
  launchable: s.launchable === true,
2372
- permissions
2351
+ permissions,
2352
+ apiKeyResource,
2353
+ apiKeyResources
2373
2354
  });
2374
2355
  }
2375
2356
  this.serviceCatalogCache = {
@@ -2455,7 +2436,9 @@ var TopoloClient = class _TopoloClient {
2455
2436
  status: "bundled",
2456
2437
  surfaceKind: "application",
2457
2438
  launchable: Boolean(app.productionUrl),
2458
- permissions: []
2439
+ permissions: [],
2440
+ apiKeyResource: null,
2441
+ apiKeyResources: []
2459
2442
  }));
2460
2443
  }
2461
2444
  /**
@@ -2504,6 +2487,46 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2504
2487
  }
2505
2488
  return null;
2506
2489
  }
2490
+ function normalizeApiKeyResourceMetadata(input) {
2491
+ if (!isRecord(input)) return null;
2492
+ const resourceType = readString(input.resourceType ?? input.resource_type);
2493
+ const label = readString(input.label);
2494
+ const emptyLabel = readString(input.emptyLabel ?? input.empty_label);
2495
+ const resourceConcept = readString(input.resourceConcept ?? input.resource_concept);
2496
+ const canonicalResourceType = readString(input.canonicalResourceType ?? input.canonical_resource_type);
2497
+ const defaultEntitlement = readString(input.defaultEntitlement ?? input.default_entitlement);
2498
+ const exceptionPolicy = readString(input.exceptionPolicy ?? input.exception_policy);
2499
+ if (!resourceType || !label || !emptyLabel || resourceConcept !== "workspace" || canonicalResourceType !== "workspace" || defaultEntitlement !== "enterprise" || exceptionPolicy !== "app_or_organization") {
2500
+ return null;
2501
+ }
2502
+ return {
2503
+ resourceType,
2504
+ label,
2505
+ emptyLabel,
2506
+ resourceConcept,
2507
+ canonicalResourceType,
2508
+ defaultEntitlement,
2509
+ exceptionPolicy
2510
+ };
2511
+ }
2512
+ function normalizeApiKeyResourceMetadataList(input) {
2513
+ if (!Array.isArray(input)) return [];
2514
+ const resources = [];
2515
+ const seenTypes = /* @__PURE__ */ new Set();
2516
+ for (const item of input) {
2517
+ const resource = normalizeApiKeyResourceMetadata(item);
2518
+ if (!resource || seenTypes.has(resource.resourceType)) continue;
2519
+ seenTypes.add(resource.resourceType);
2520
+ resources.push(resource);
2521
+ }
2522
+ return resources;
2523
+ }
2524
+ function readString(value) {
2525
+ return typeof value === "string" ? value.trim() : "";
2526
+ }
2527
+ function isRecord(value) {
2528
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2529
+ }
2507
2530
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
2508
2531
  for (const serviceKey of serviceKeyCandidates2(slug)) {
2509
2532
  const configured = serviceUrls?.[serviceKey];
@@ -2527,6 +2550,17 @@ function serviceKeyCandidates2(slug) {
2527
2550
  function normalizeLookupKey(value) {
2528
2551
  return String(value || "").trim().toLowerCase().replace(/\s+/g, "-");
2529
2552
  }
2553
+ function deriveDevelopersFromAuth(authUrl) {
2554
+ if (!authUrl) return null;
2555
+ try {
2556
+ const url = new URL(authUrl);
2557
+ if (!/^auth\./i.test(url.hostname)) return null;
2558
+ url.hostname = url.hostname.replace(/^auth\./i, "developers.");
2559
+ return url.origin;
2560
+ } catch {
2561
+ return null;
2562
+ }
2563
+ }
2530
2564
  function catalogEntryMatches(entry, needle) {
2531
2565
  return catalogEntryLookupKeys(entry).includes(needle);
2532
2566
  }
@@ -2838,6 +2872,8 @@ var TOOLS = [
2838
2872
  effectiveUrl: resolveCatalogServiceUrl(entry, { serviceKey: id }),
2839
2873
  surfaceKind: entry.surfaceKind,
2840
2874
  launchable: entry.launchable,
2875
+ apiKeyResource: entry.apiKeyResource,
2876
+ apiKeyResources: [...entry.apiKeyResources],
2841
2877
  permissions: [...entry.permissions]
2842
2878
  };
2843
2879
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3031,6 +3067,14 @@ var TOOLS = [
3031
3067
  additionalProperties: { type: "string" },
3032
3068
  description: "Query string parameters as string values."
3033
3069
  },
3070
+ resourceType: {
3071
+ type: "string",
3072
+ description: "Optional workspace resource alias from topolo_list_services apiKeyResources. Aliases are app-defined, e.g. brand, mailbox, venue, merchant, or any other cataloged alias."
3073
+ },
3074
+ resourceId: {
3075
+ type: "string",
3076
+ description: "Resource id for resourceType. Required when resourceType is provided."
3077
+ },
3034
3078
  confirm: {
3035
3079
  type: "boolean",
3036
3080
  description: "Required for non-GET methods. Explicit human-in-the-loop acknowledgement for writes."
@@ -3057,12 +3101,19 @@ var TOOLS = [
3057
3101
  const normalisedQuery = query && typeof query === "object" && !Array.isArray(query) ? Object.fromEntries(
3058
3102
  Object.entries(query).map(([k, v]) => [k, String(v)])
3059
3103
  ) : void 0;
3104
+ const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
3105
+ const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
3106
+ if (resourceType && !resourceId || !resourceType && resourceId) {
3107
+ throw new Error("resourceType and resourceId must be provided together.");
3108
+ }
3109
+ const resource = resourceType && resourceId ? { resourceType, resourceId } : void 0;
3060
3110
  return topolo.client.request({
3061
3111
  service: service.trim(),
3062
3112
  method,
3063
3113
  path,
3064
3114
  body: args["body"],
3065
3115
  ...normalisedQuery ? { query: normalisedQuery } : {},
3116
+ ...resource ? { resource } : {},
3066
3117
  confirm: Boolean(args["confirm"])
3067
3118
  });
3068
3119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,27 +15,27 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
- "scripts": {
19
- "build": "tsup",
20
- "dev": "tsup --watch",
21
- "typecheck": "tsc --noEmit",
22
- "start": "node dist/index.js",
23
- "test": "vitest run",
24
- "test:watch": "vitest",
25
- "coverage": "vitest run --coverage"
26
- },
27
18
  "dependencies": {
28
19
  "@modelcontextprotocol/sdk": "^1.0.0"
29
20
  },
30
21
  "devDependencies": {
31
- "@topolo/sdk": "workspace:*",
32
22
  "@types/node": "^20.12.0",
33
23
  "@vitest/coverage-v8": "^2.1.0",
34
24
  "tsup": "^8.0.0",
35
25
  "typescript": "^5.4.0",
36
- "vitest": "^2.1.0"
26
+ "vitest": "^2.1.0",
27
+ "@topolo/sdk": "0.4.9"
37
28
  },
38
29
  "engines": {
39
30
  "node": ">=20"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup",
34
+ "dev": "tsup --watch",
35
+ "typecheck": "tsc --noEmit",
36
+ "start": "node dist/index.js",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "coverage": "vitest run --coverage"
40
40
  }
41
- }
41
+ }