@topolo/mcp 0.4.4 → 0.4.5

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/dist/dispatch.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  auditAllApplicationRequirements,
11
11
  applicationRequirementScopes,
12
12
  requirementsForApplication,
13
+ resolveCatalogServiceUrl,
13
14
  resolveServiceUrl
14
15
  } from "@topolo/sdk";
15
16
 
@@ -87,7 +88,9 @@ var TOOLS = [
87
88
  slug: entry.slug,
88
89
  name: entry.name,
89
90
  baseUrl: entry.baseUrl,
90
- effectiveUrl: entry.baseUrl || resolveServiceUrl(id),
91
+ apiBaseUrl: entry.apiBaseUrl,
92
+ launchUrl: entry.launchUrl,
93
+ effectiveUrl: resolveCatalogServiceUrl(entry, { serviceKey: id }) || resolveServiceUrl(id),
91
94
  surfaceKind: entry.surfaceKind,
92
95
  launchable: entry.launchable,
93
96
  permissions: [...entry.permissions]
package/dist/index.js CHANGED
@@ -2040,15 +2040,88 @@ function statusRank(status) {
2040
2040
  return 2;
2041
2041
  }
2042
2042
  var DEFAULT_SERVICE_URLS = SERVICES;
2043
+ function resolveCatalogServiceUrl(entry, options = {}) {
2044
+ const keys = catalogServiceKeyCandidates(entry, options.serviceKey);
2045
+ const override = resolveServiceUrlOverride(keys, options.overrides);
2046
+ if (override) return override;
2047
+ const apiBaseUrl = firstString(entry.apiBaseUrl, entry.api_base_url);
2048
+ if (apiBaseUrl) return apiBaseUrl;
2049
+ const bundledUrl = firstBundledServiceUrl(keys);
2050
+ const liveUrl = firstString(entry.baseUrl);
2051
+ if (bundledUrl && (!liveUrl || sameUrlEnvironment(bundledUrl, liveUrl))) return bundledUrl;
2052
+ return liveUrl ?? bundledUrl ?? null;
2053
+ }
2043
2054
  function resolveServiceUrl(service, overrides) {
2044
- const override = overrides?.[service];
2055
+ const override = resolveServiceUrlOverride([service], overrides);
2045
2056
  if (override) return override;
2046
- const envKey = `TOPOLO_SERVICE_URL_${service.replace(/\./g, "_").toUpperCase()}`;
2047
- const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
2048
- if (envValue) return envValue;
2049
2057
  return DEFAULT_SERVICE_URLS[service];
2050
2058
  }
2059
+ function catalogServiceKeyCandidates(entry, preferred) {
2060
+ const values = [preferred, entry.slug, entry.name, entry.serviceId].filter(
2061
+ (value) => typeof value === "string" && value.trim().length > 0
2062
+ );
2063
+ const keys = /* @__PURE__ */ new Set();
2064
+ for (const value of values) {
2065
+ for (const candidate of serviceKeyCandidates(value)) keys.add(candidate);
2066
+ }
2067
+ return [...keys];
2068
+ }
2069
+ function serviceKeyCandidates(value) {
2070
+ const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
2071
+ const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
2072
+ return [.../* @__PURE__ */ new Set([
2073
+ normalized,
2074
+ normalized.replace(/-/g, "_"),
2075
+ withoutTopolo,
2076
+ withoutTopolo.replace(/-/g, "_")
2077
+ ])].filter(Boolean);
2078
+ }
2079
+ function resolveServiceUrlOverride(keys, overrides) {
2080
+ for (const key of keys) {
2081
+ const direct = overrides?.[key];
2082
+ if (direct) return direct;
2083
+ const envStyleKey = key.replace(/\./g, "_");
2084
+ const envStyle = overrides?.[envStyleKey];
2085
+ if (envStyle) return envStyle;
2086
+ }
2087
+ for (const key of keys) {
2088
+ const envKey = `TOPOLO_SERVICE_URL_${key.replace(/\./g, "_").toUpperCase()}`;
2089
+ const envValue = typeof process !== "undefined" ? process.env?.[envKey] : void 0;
2090
+ if (envValue) return envValue;
2091
+ }
2092
+ return null;
2093
+ }
2094
+ function firstBundledServiceUrl(keys) {
2095
+ for (const key of keys) {
2096
+ const url = DEFAULT_SERVICE_URLS[key];
2097
+ if (url) return url;
2098
+ }
2099
+ return null;
2100
+ }
2101
+ function firstString(...values) {
2102
+ for (const value of values) {
2103
+ if (typeof value === "string" && value.trim()) return value.trim();
2104
+ }
2105
+ return null;
2106
+ }
2107
+ function sameUrlEnvironment(left, right) {
2108
+ return urlEnvironment(left) === urlEnvironment(right);
2109
+ }
2110
+ function urlEnvironment(value) {
2111
+ try {
2112
+ const url = new URL(value);
2113
+ const host = url.hostname.toLowerCase();
2114
+ if (host === "localhost" || host === "127.0.0.1") return "local";
2115
+ if (host.endsWith(".stg.topolo.us") || host.includes("topolo-staging")) return "staging";
2116
+ if (host.endsWith(".topolo.app") || host.endsWith(".topolo.us")) return "production";
2117
+ return "external";
2118
+ } catch {
2119
+ return "external";
2120
+ }
2121
+ }
2051
2122
  var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
2123
+ var TOPOLO_ACTION_ID_HEADER = "X-Topolo-Action-Id";
2124
+ var TOPOLO_ACTION_NAME_HEADER = "X-Topolo-Action-Name";
2052
2125
  var POSITIVE_CACHE_TTL_MS = 5 * 60 * 1e3;
2053
2126
  var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
2054
2127
  var CATALOG_CACHE_TTL_MS = 60 * 1e3;
@@ -2216,8 +2289,12 @@ var TopoloClient = class _TopoloClient {
2216
2289
  async resolveLiveCallableService(serviceKey) {
2217
2290
  const catalog = await this.listServices();
2218
2291
  const match = catalog.find((entry) => catalogEntryMatches(entry, serviceKey));
2219
- if (!match?.baseUrl || !isCallableCatalogEntry(match)) return null;
2220
- return { serviceKey, baseUrl: match.baseUrl };
2292
+ if (!match || !isCallableCatalogEntry(match)) return null;
2293
+ const baseUrl = resolveCatalogServiceUrl(match, {
2294
+ serviceKey,
2295
+ overrides: this.serviceUrls
2296
+ });
2297
+ return baseUrl ? { serviceKey, baseUrl } : null;
2221
2298
  }
2222
2299
  /**
2223
2300
  * Resolve a service-slug → opaque service-id via caller config or TopoloAuth.
@@ -2311,6 +2388,8 @@ var TopoloClient = class _TopoloClient {
2311
2388
  name: typeof s.name === "string" ? s.name : null,
2312
2389
  description: typeof s.description === "string" ? s.description : null,
2313
2390
  baseUrl: typeof s.base_url === "string" ? s.base_url : null,
2391
+ apiBaseUrl: typeof s.api_base_url === "string" ? s.api_base_url : typeof s.apiBaseUrl === "string" ? s.apiBaseUrl : null,
2392
+ launchUrl: typeof s.launch_url === "string" ? s.launch_url : typeof s.launchUrl === "string" ? s.launchUrl : null,
2314
2393
  status: typeof s.status === "string" ? s.status : null,
2315
2394
  surfaceKind: typeof s.surface_kind === "string" ? s.surface_kind : null,
2316
2395
  launchable: s.launchable === true,
@@ -2377,6 +2456,10 @@ var TopoloClient = class _TopoloClient {
2377
2456
  path: mapped.path,
2378
2457
  ...mapped.query ? { query: mapped.query } : {},
2379
2458
  ...mapped.body !== void 0 ? { body: mapped.body } : {},
2459
+ headers: {
2460
+ [TOPOLO_ACTION_ID_HEADER]: entry.actionId,
2461
+ [TOPOLO_ACTION_NAME_HEADER]: entry.name
2462
+ },
2380
2463
  confirm: options.confirm === true
2381
2464
  });
2382
2465
  }
@@ -2392,6 +2475,8 @@ var TopoloClient = class _TopoloClient {
2392
2475
  name: app.name ?? app.id,
2393
2476
  description: null,
2394
2477
  baseUrl: app.productionUrl ?? null,
2478
+ apiBaseUrl: null,
2479
+ launchUrl: app.productionUrl ?? null,
2395
2480
  status: "bundled",
2396
2481
  surfaceKind: "application",
2397
2482
  launchable: Boolean(app.productionUrl),
@@ -2435,7 +2520,7 @@ var TopoloClient = class _TopoloClient {
2435
2520
  }
2436
2521
  };
2437
2522
  function resolveConfiguredServiceId(slug, serviceIds) {
2438
- for (const serviceKey of serviceKeyCandidates(slug)) {
2523
+ for (const serviceKey of serviceKeyCandidates2(slug)) {
2439
2524
  const configured = serviceIds?.[serviceKey];
2440
2525
  if (typeof configured === "string" && configured.trim()) return configured.trim();
2441
2526
  const envKey = `TOPOLO_SERVICE_ID_${serviceKey.replace(/\./g, "_").toUpperCase()}`;
@@ -2445,7 +2530,7 @@ function resolveConfiguredServiceId(slug, serviceIds) {
2445
2530
  return null;
2446
2531
  }
2447
2532
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
2448
- for (const serviceKey of serviceKeyCandidates(slug)) {
2533
+ for (const serviceKey of serviceKeyCandidates2(slug)) {
2449
2534
  const configured = serviceUrls?.[serviceKey];
2450
2535
  if (typeof configured === "string" && configured.trim()) return configured.trim();
2451
2536
  const envKey = `TOPOLO_SERVICE_URL_${serviceKey.replace(/\./g, "_").toUpperCase()}`;
@@ -2454,7 +2539,7 @@ function resolveConfiguredServiceUrl(slug, serviceUrls) {
2454
2539
  }
2455
2540
  return null;
2456
2541
  }
2457
- function serviceKeyCandidates(slug) {
2542
+ function serviceKeyCandidates2(slug) {
2458
2543
  const normalized = normalizeLookupKey(slug);
2459
2544
  const withoutTopolo = normalized.startsWith("topolo-") ? normalized.slice("topolo-".length) : normalized;
2460
2545
  return [.../* @__PURE__ */ new Set([
@@ -2476,7 +2561,7 @@ function catalogEntryLookupKeys(entry) {
2476
2561
  for (const value of values) {
2477
2562
  const normalized = normalizeLookupKey(value);
2478
2563
  if (!normalized) continue;
2479
- for (const candidate of serviceKeyCandidates(normalized)) {
2564
+ for (const candidate of serviceKeyCandidates2(normalized)) {
2480
2565
  keys.add(candidate);
2481
2566
  }
2482
2567
  }
@@ -2486,7 +2571,7 @@ function isCallableCatalogEntry(entry) {
2486
2571
  const kind = normalizeLookupKey(entry.surfaceKind || "");
2487
2572
  if (kind === "api" || kind === "technical_service" || kind === "service") return true;
2488
2573
  return Boolean(
2489
- entry.baseUrl && catalogEntryLookupKeys(entry).some((key) => Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, key))
2574
+ (entry.baseUrl || entry.apiBaseUrl) && catalogEntryLookupKeys(entry).some((key) => Object.prototype.hasOwnProperty.call(DEFAULT_SERVICE_URLS, key))
2490
2575
  );
2491
2576
  }
2492
2577
  function normalizeActionCatalogEntry(item) {
@@ -2781,7 +2866,9 @@ var TOOLS = [
2781
2866
  slug: entry.slug,
2782
2867
  name: entry.name,
2783
2868
  baseUrl: entry.baseUrl,
2784
- effectiveUrl: entry.baseUrl || resolveServiceUrl(id),
2869
+ apiBaseUrl: entry.apiBaseUrl,
2870
+ launchUrl: entry.launchUrl,
2871
+ effectiveUrl: resolveCatalogServiceUrl(entry, { serviceKey: id }) || resolveServiceUrl(id),
2785
2872
  surfaceKind: entry.surfaceKind,
2786
2873
  launchable: entry.launchable,
2787
2874
  permissions: [...entry.permissions]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
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": {