@topolo/mcp 0.4.6 → 0.4.8

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 +191 -138
  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
89
  nexus: "https://nexus-api.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
  },
@@ -644,12 +635,10 @@ var APPLICATIONS = {
644
635
  {
645
636
  "name": "topolo-feed-api",
646
637
  "kind": "worker",
647
- "apiUrl": "https://feed-api.topolo.app",
648
- "domains": [
649
- "feed-api.topolo.app"
650
- ],
638
+ "apiUrl": null,
639
+ "domains": [],
651
640
  "routes": [],
652
- "serviceId": "feed"
641
+ "serviceId": null
653
642
  },
654
643
  {
655
644
  "name": "topolo-feed-web",
@@ -659,7 +648,7 @@ var APPLICATIONS = {
659
648
  "feed.topolo.app"
660
649
  ],
661
650
  "routes": [],
662
- "serviceId": null
651
+ "serviceId": "feed"
663
652
  }
664
653
  ]
665
654
  },
@@ -711,17 +700,15 @@ var APPLICATIONS = {
711
700
  "forecast.topolo.app"
712
701
  ],
713
702
  "routes": [],
714
- "serviceId": null
703
+ "serviceId": "forecast"
715
704
  },
716
705
  {
717
706
  "name": "topolo-forecast-api",
718
707
  "kind": "worker",
719
- "apiUrl": "https://forecast-api.topolo.app",
720
- "domains": [
721
- "forecast-api.topolo.app"
722
- ],
708
+ "apiUrl": null,
709
+ "domains": [],
723
710
  "routes": [],
724
- "serviceId": "forecast"
711
+ "serviceId": null
725
712
  }
726
713
  ]
727
714
  },
@@ -863,17 +850,15 @@ var APPLICATIONS = {
863
850
  "learn.topolo.app"
864
851
  ],
865
852
  "routes": [],
866
- "serviceId": null
853
+ "serviceId": "learn"
867
854
  },
868
855
  {
869
856
  "name": "topolo-learn-api",
870
857
  "kind": "worker",
871
- "apiUrl": "https://learn-api.topolo.app",
872
- "domains": [
873
- "learn-api.topolo.app"
874
- ],
858
+ "apiUrl": null,
859
+ "domains": [],
875
860
  "routes": [],
876
- "serviceId": "learn"
861
+ "serviceId": null
877
862
  }
878
863
  ]
879
864
  },
@@ -919,17 +904,15 @@ var APPLICATIONS = {
919
904
  "mail.topolo.app"
920
905
  ],
921
906
  "routes": [],
922
- "serviceId": null
907
+ "serviceId": "mail"
923
908
  },
924
909
  {
925
910
  "name": "topolo-mail-api",
926
911
  "kind": "worker",
927
- "apiUrl": "https://mail-api.topolo.app",
928
- "domains": [
929
- "mail-api.topolo.app"
930
- ],
912
+ "apiUrl": null,
913
+ "domains": [],
931
914
  "routes": [],
932
- "serviceId": "mail"
915
+ "serviceId": null
933
916
  }
934
917
  ]
935
918
  },
@@ -946,12 +929,10 @@ var APPLICATIONS = {
946
929
  {
947
930
  "name": "topolo-mdm-api",
948
931
  "kind": "worker",
949
- "apiUrl": "https://mdm-api.topolo.app",
950
- "domains": [
951
- "mdm-api.topolo.app"
952
- ],
932
+ "apiUrl": null,
933
+ "domains": [],
953
934
  "routes": [],
954
- "serviceId": "mdm"
935
+ "serviceId": null
955
936
  },
956
937
  {
957
938
  "name": "topolo-mdm-console",
@@ -961,7 +942,7 @@ var APPLICATIONS = {
961
942
  "mdm.topolo.app"
962
943
  ],
963
944
  "routes": [],
964
- "serviceId": null
945
+ "serviceId": "mdm"
965
946
  }
966
947
  ]
967
948
  },
@@ -1094,20 +1075,23 @@ var APPLICATIONS = {
1094
1075
  {
1095
1076
  "name": "topolo-one",
1096
1077
  "kind": "worker",
1097
- "apiUrl": null,
1098
- "domains": [],
1099
- "routes": [],
1078
+ "apiUrl": "https://one.topolo.app",
1079
+ "domains": [
1080
+ "one.topolo.app"
1081
+ ],
1082
+ "routes": [
1083
+ "one.topolo.app/*"
1084
+ ],
1100
1085
  "serviceId": null
1101
1086
  },
1102
1087
  {
1103
1088
  "name": "topolo-one-api",
1104
1089
  "kind": "worker",
1105
- "apiUrl": "https://one.topolo.app",
1090
+ "apiUrl": "https://www.topolo.app",
1106
1091
  "domains": [
1107
- "one.topolo.app"
1092
+ "www.topolo.app"
1108
1093
  ],
1109
1094
  "routes": [
1110
- "one.topolo.app/*",
1111
1095
  "www.topolo.app/*"
1112
1096
  ],
1113
1097
  "serviceId": "one"
@@ -1217,36 +1201,6 @@ var APPLICATIONS = {
1217
1201
  }
1218
1202
  ]
1219
1203
  },
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
1204
  "quro": {
1251
1205
  "id": "quro",
1252
1206
  "appDir": "TopoloQuro",
@@ -1312,17 +1266,15 @@ var APPLICATIONS = {
1312
1266
  "roadmapper.topolo.app"
1313
1267
  ],
1314
1268
  "routes": [],
1315
- "serviceId": null
1269
+ "serviceId": "roadmapper"
1316
1270
  },
1317
1271
  {
1318
1272
  "name": "topolo-roadmapper-api",
1319
1273
  "kind": "worker",
1320
- "apiUrl": "https://roadmapper-api.topolo.app",
1321
- "domains": [
1322
- "roadmapper-api.topolo.app"
1323
- ],
1274
+ "apiUrl": null,
1275
+ "domains": [],
1324
1276
  "routes": [],
1325
- "serviceId": "roadmapper"
1277
+ "serviceId": null
1326
1278
  }
1327
1279
  ]
1328
1280
  },
@@ -1374,17 +1326,15 @@ var APPLICATIONS = {
1374
1326
  "studio.topolo.app"
1375
1327
  ],
1376
1328
  "routes": [],
1377
- "serviceId": null
1329
+ "serviceId": "social_studio"
1378
1330
  },
1379
1331
  {
1380
1332
  "name": "topolo-social-studio-api",
1381
1333
  "kind": "worker",
1382
- "apiUrl": "https://studio-api.topolo.app",
1383
- "domains": [
1384
- "studio-api.topolo.app"
1385
- ],
1334
+ "apiUrl": null,
1335
+ "domains": [],
1386
1336
  "routes": [],
1387
- "serviceId": "social_studio"
1337
+ "serviceId": null
1388
1338
  },
1389
1339
  {
1390
1340
  "name": "topolo-social-studio-queue",
@@ -1414,17 +1364,15 @@ var APPLICATIONS = {
1414
1364
  "socialize.topolo.app"
1415
1365
  ],
1416
1366
  "routes": [],
1417
- "serviceId": null
1367
+ "serviceId": "socialize"
1418
1368
  },
1419
1369
  {
1420
1370
  "name": "topolo-socialize-api",
1421
1371
  "kind": "worker",
1422
- "apiUrl": "https://socialize-api.topolo.app",
1423
- "domains": [
1424
- "socialize-api.topolo.app"
1425
- ],
1372
+ "apiUrl": null,
1373
+ "domains": [],
1426
1374
  "routes": [],
1427
- "serviceId": "socialize"
1375
+ "serviceId": null
1428
1376
  },
1429
1377
  {
1430
1378
  "name": "topolo-socialize-scheduler",
@@ -1625,12 +1573,10 @@ var APPLICATIONS = {
1625
1573
  {
1626
1574
  "name": "topolo-web-api",
1627
1575
  "kind": "worker",
1628
- "apiUrl": "https://web-api.topolo.app",
1629
- "domains": [
1630
- "web-api.topolo.app"
1631
- ],
1576
+ "apiUrl": null,
1577
+ "domains": [],
1632
1578
  "routes": [],
1633
- "serviceId": "web"
1579
+ "serviceId": null
1634
1580
  },
1635
1581
  {
1636
1582
  "name": "topolo-web-runtime",
@@ -1653,12 +1599,12 @@ var APPLICATIONS = {
1653
1599
  "web.topolo.app"
1654
1600
  ],
1655
1601
  "routes": [],
1656
- "serviceId": null
1602
+ "serviceId": "web"
1657
1603
  }
1658
1604
  ]
1659
1605
  }
1660
1606
  };
1661
- var APPLICATION_REQUIREMENTS_VERSION = "2026-05-01.1";
1607
+ var APPLICATION_REQUIREMENTS_VERSION = "2026-06-07.1";
1662
1608
  var APPLICATION_REQUIREMENTS = [
1663
1609
  {
1664
1610
  id: "platform-metadata",
@@ -1714,16 +1660,20 @@ var APPLICATION_REQUIREMENTS = [
1714
1660
  summary: "Browser applications should feel like one platform and should not create isolated navigation or account surfaces.",
1715
1661
  appliesTo: ["browser"],
1716
1662
  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",
1663
+ "@topolo-io/app-shell is used for the platform shell where the app has authenticated UI",
1664
+ "@topolo-io/app-launcher keeps the shared launcher/app switcher reachable from authenticated app chrome",
1665
+ "Account menu and sign-out behavior use TopoloAppShell or the ui-kit TopoloAccountMenu primitive",
1666
+ "Repeated app body surfaces use shared TopoloAppLayout primitives instead of local stat cards, empty states, or status badges",
1667
+ "Local preview supports deterministic visual QA fixtures for overview, list, detail, settings, and empty/loading states",
1720
1668
  "Authenticated app body content stays inside the viewport on mobile and desktop widths"
1721
1669
  ],
1722
1670
  implementation: [
1723
- "Use TopoloAppShell for authenticated app layouts",
1724
- "Use TopoloAccountMenu when a partial-shell surface still needs account chrome",
1671
+ "Use @topolo-io/app-shell TopoloAppShell for authenticated app layouts",
1672
+ "Use the @topolo-io/ui-kit TopoloAccountMenu primitive when a partial-shell surface still needs account chrome",
1725
1673
  "Pass app-specific account actions as additive menu items instead of forking dropdowns",
1726
1674
  "Use shared design tokens and platform UI primitives before creating local equivalents",
1675
+ "Use TopoloAppMetricCard, TopoloAppEmptyState, TopoloAppStatusBadge, TopoloAppGrid, and TopoloAppSection for repeated app body patterns",
1676
+ "Keep visual QA fixtures available through visualQa=1 or __devpreview=1 so screenshot checks do not depend on live API state",
1727
1677
  "Keep mobile and desktop shell behavior aligned with the platform UI kit",
1728
1678
  "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
1679
  ]
@@ -1802,11 +1752,13 @@ var APPLICATION_REQUIREMENTS = [
1802
1752
  evidence: [
1803
1753
  "The app-owned API exposes GET /api/widget",
1804
1754
  "The endpoint returns TopoloWidgetApiResponse from @topolo/sdk",
1755
+ "Every returned widget includes snapshot.summary with app-owned operational prose",
1805
1756
  "The endpoint derives organization/user context from the app auth boundary"
1806
1757
  ],
1807
1758
  implementation: [
1808
1759
  "Import createTopoloWidgetResponse from @topolo/sdk in the app backend",
1809
1760
  "Return native app metrics or action widgets instead of generic launch-card fallbacks",
1761
+ "Populate snapshot.summary in the producing app for every widget instead of synthesizing summaries in TopoloOne",
1810
1762
  "Add a route-level test that validates the response with validateTopoloWidgetResponse"
1811
1763
  ]
1812
1764
  },
@@ -2099,6 +2051,8 @@ function firstString(...values) {
2099
2051
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2100
2052
  var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
2101
2053
  var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
2054
+ var TOPOLO_RESOURCE_TYPE_HEADER = "X-Topolo-Resource-Type";
2055
+ var TOPOLO_RESOURCE_ID_HEADER = "X-Topolo-Resource-ID";
2102
2056
  var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
2103
2057
  var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
2104
2058
  var CATALOG_CACHE_TTL_MS = 60 * 1e3;
@@ -2177,6 +2131,18 @@ var TopoloClient = class _TopoloClient {
2177
2131
  if (opts.headers) {
2178
2132
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
2179
2133
  }
2134
+ if (opts.resource) {
2135
+ const resourceType = opts.resource.resourceType.trim();
2136
+ const resourceId = opts.resource.resourceId.trim();
2137
+ if (!resourceType || !resourceId) {
2138
+ throw new TopoloSdkError(
2139
+ "invalid_resource_context",
2140
+ "Both resource.resourceType and resource.resourceId are required when resource context is provided."
2141
+ );
2142
+ }
2143
+ headers.set(TOPOLO_RESOURCE_TYPE_HEADER, resourceType);
2144
+ headers.set(TOPOLO_RESOURCE_ID_HEADER, resourceId);
2145
+ }
2180
2146
  const controller = new AbortController();
2181
2147
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
2182
2148
  const signal = opts.signal ? mergeSignals(opts.signal, controller.signal) : controller.signal;
@@ -2359,6 +2325,11 @@ var TopoloClient = class _TopoloClient {
2359
2325
  const serviceId = typeof s.service_id === "string" ? s.service_id : typeof s.id === "string" ? s.id : null;
2360
2326
  if (!serviceId) continue;
2361
2327
  const permissions = Array.isArray(s.granted_permissions) ? s.granted_permissions.filter((permission) => typeof permission === "string" && permission.trim().length > 0) : [];
2328
+ const catalogApiKeyResources = normalizeApiKeyResourceMetadataList(
2329
+ s.api_key_resources ?? s.apiKeyResources
2330
+ );
2331
+ const apiKeyResource = normalizeApiKeyResourceMetadata(s.api_key_resource ?? s.apiKeyResource) ?? catalogApiKeyResources[0] ?? null;
2332
+ const apiKeyResources = catalogApiKeyResources.length > 0 ? catalogApiKeyResources : apiKeyResource ? [apiKeyResource] : [];
2362
2333
  entries.push({
2363
2334
  serviceId,
2364
2335
  slug: typeof s.app_slug === "string" ? s.app_slug : null,
@@ -2369,7 +2340,9 @@ var TopoloClient = class _TopoloClient {
2369
2340
  status: typeof s.status === "string" ? s.status : null,
2370
2341
  surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2371
2342
  launchable: s.launchable === true,
2372
- permissions
2343
+ permissions,
2344
+ apiKeyResource,
2345
+ apiKeyResources
2373
2346
  });
2374
2347
  }
2375
2348
  this.serviceCatalogCache = {
@@ -2455,7 +2428,9 @@ var TopoloClient = class _TopoloClient {
2455
2428
  status: "bundled",
2456
2429
  surfaceKind: "application",
2457
2430
  launchable: Boolean(app.productionUrl),
2458
- permissions: []
2431
+ permissions: [],
2432
+ apiKeyResource: null,
2433
+ apiKeyResources: []
2459
2434
  }));
2460
2435
  }
2461
2436
  /**
@@ -2504,6 +2479,46 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2504
2479
  }
2505
2480
  return null;
2506
2481
  }
2482
+ function normalizeApiKeyResourceMetadata(input) {
2483
+ if (!isRecord(input)) return null;
2484
+ const resourceType = readString(input.resourceType ?? input.resource_type);
2485
+ const label = readString(input.label);
2486
+ const emptyLabel = readString(input.emptyLabel ?? input.empty_label);
2487
+ const resourceConcept = readString(input.resourceConcept ?? input.resource_concept);
2488
+ const canonicalResourceType = readString(input.canonicalResourceType ?? input.canonical_resource_type);
2489
+ const defaultEntitlement = readString(input.defaultEntitlement ?? input.default_entitlement);
2490
+ const exceptionPolicy = readString(input.exceptionPolicy ?? input.exception_policy);
2491
+ if (!resourceType || !label || !emptyLabel || resourceConcept !== "workspace" || canonicalResourceType !== "workspace" || defaultEntitlement !== "enterprise" || exceptionPolicy !== "app_or_organization") {
2492
+ return null;
2493
+ }
2494
+ return {
2495
+ resourceType,
2496
+ label,
2497
+ emptyLabel,
2498
+ resourceConcept,
2499
+ canonicalResourceType,
2500
+ defaultEntitlement,
2501
+ exceptionPolicy
2502
+ };
2503
+ }
2504
+ function normalizeApiKeyResourceMetadataList(input) {
2505
+ if (!Array.isArray(input)) return [];
2506
+ const resources = [];
2507
+ const seenTypes = /* @__PURE__ */ new Set();
2508
+ for (const item of input) {
2509
+ const resource = normalizeApiKeyResourceMetadata(item);
2510
+ if (!resource || seenTypes.has(resource.resourceType)) continue;
2511
+ seenTypes.add(resource.resourceType);
2512
+ resources.push(resource);
2513
+ }
2514
+ return resources;
2515
+ }
2516
+ function readString(value) {
2517
+ return typeof value === "string" ? value.trim() : "";
2518
+ }
2519
+ function isRecord(value) {
2520
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2521
+ }
2507
2522
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
2508
2523
  for (const serviceKey of serviceKeyCandidates2(slug)) {
2509
2524
  const configured = serviceUrls?.[serviceKey];
@@ -2838,6 +2853,8 @@ var TOOLS = [
2838
2853
  effectiveUrl: resolveCatalogServiceUrl(entry, { serviceKey: id }),
2839
2854
  surfaceKind: entry.surfaceKind,
2840
2855
  launchable: entry.launchable,
2856
+ apiKeyResource: entry.apiKeyResource,
2857
+ apiKeyResources: [...entry.apiKeyResources],
2841
2858
  permissions: [...entry.permissions]
2842
2859
  };
2843
2860
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3031,6 +3048,14 @@ var TOOLS = [
3031
3048
  additionalProperties: { type: "string" },
3032
3049
  description: "Query string parameters as string values."
3033
3050
  },
3051
+ resourceType: {
3052
+ type: "string",
3053
+ 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."
3054
+ },
3055
+ resourceId: {
3056
+ type: "string",
3057
+ description: "Resource id for resourceType. Required when resourceType is provided."
3058
+ },
3034
3059
  confirm: {
3035
3060
  type: "boolean",
3036
3061
  description: "Required for non-GET methods. Explicit human-in-the-loop acknowledgement for writes."
@@ -3057,12 +3082,19 @@ var TOOLS = [
3057
3082
  const normalisedQuery = query && typeof query === "object" && !Array.isArray(query) ? Object.fromEntries(
3058
3083
  Object.entries(query).map(([k, v]) => [k, String(v)])
3059
3084
  ) : void 0;
3085
+ const resourceType = typeof args["resourceType"] === "string" ? args["resourceType"].trim() : "";
3086
+ const resourceId = typeof args["resourceId"] === "string" ? args["resourceId"].trim() : "";
3087
+ if (resourceType && !resourceId || !resourceType && resourceId) {
3088
+ throw new Error("resourceType and resourceId must be provided together.");
3089
+ }
3090
+ const resource = resourceType && resourceId ? { resourceType, resourceId } : void 0;
3060
3091
  return topolo.client.request({
3061
3092
  service: service.trim(),
3062
3093
  method,
3063
3094
  path,
3064
3095
  body: args["body"],
3065
3096
  ...normalisedQuery ? { query: normalisedQuery } : {},
3097
+ ...resource ? { resource } : {},
3066
3098
  confirm: Boolean(args["confirm"])
3067
3099
  });
3068
3100
  }
@@ -3184,7 +3216,28 @@ function formatKnownValues(values, label) {
3184
3216
  }
3185
3217
 
3186
3218
  // src/version.ts
3187
- var MCP_VERSION = "0.4.1";
3219
+ import { readFileSync } from "fs";
3220
+ import { dirname, join } from "path";
3221
+ import { fileURLToPath } from "url";
3222
+ function readMcpVersion() {
3223
+ try {
3224
+ const here = dirname(fileURLToPath(import.meta.url));
3225
+ const candidates = [
3226
+ join(here, "package.json"),
3227
+ join(here, "..", "package.json")
3228
+ ];
3229
+ for (const path of candidates) {
3230
+ try {
3231
+ const pkg = JSON.parse(readFileSync(path, "utf8"));
3232
+ if (typeof pkg.version === "string" && pkg.version) return pkg.version;
3233
+ } catch {
3234
+ }
3235
+ }
3236
+ } catch {
3237
+ }
3238
+ return "0.0.0";
3239
+ }
3240
+ var MCP_VERSION = readMcpVersion();
3188
3241
 
3189
3242
  // src/server.ts
3190
3243
  function buildServer(topolo, scopes, tools = TOOLS) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
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.8"
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
+ }