@topolo/mcp 0.11.9 → 0.11.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.
Files changed (2) hide show
  1. package/dist/index.js +87 -46
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1540,7 +1540,9 @@ var TopoloClient = class _TopoloClient {
1540
1540
  };
1541
1541
  }
1542
1542
  planResolvedAction(entry, input, options) {
1543
- const validation = validateTopoloActionInput(entry.inputSchema, input);
1543
+ const inputValidation = validateTopoloActionInput(entry.inputSchema, input);
1544
+ const resourceIssue = validateActionResource(entry, options.resource);
1545
+ const validation = resourceIssue ? { valid: false, errors: [...inputValidation.errors, resourceIssue] } : inputValidation;
1544
1546
  const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
1545
1547
  const confirmationRequired = agentAccess === "confirm" || entry.requiresConfirmation || entry.destructive;
1546
1548
  let request = null;
@@ -1599,6 +1601,13 @@ var TopoloClient = class _TopoloClient {
1599
1601
  `Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((issue) => `${issue.path} ${issue.message}`).join("; ")}`
1600
1602
  );
1601
1603
  }
1604
+ const resourceIssue = validateActionResource(entry, options.resource);
1605
+ if (resourceIssue) {
1606
+ throw new TopoloSdkError(
1607
+ "invalid_action_resource",
1608
+ `Topolo action "${entry.actionId}" resource context is invalid: ${resourceIssue.message}`
1609
+ );
1610
+ }
1602
1611
  const mapped = mapActionInput(entry, input);
1603
1612
  const service = entry.appSlug || entry.appId;
1604
1613
  const result = await this.request({
@@ -1661,6 +1670,32 @@ var TopoloClient = class _TopoloClient {
1661
1670
  };
1662
1671
  }
1663
1672
  };
1673
+ function validateActionResource(entry, resource) {
1674
+ const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
1675
+ if (!permissionName.startsWith("workspace:")) return null;
1676
+ if (!resource) {
1677
+ return {
1678
+ path: "$.resource",
1679
+ code: "required",
1680
+ message: "Select a workspace with the application resource catalog before planning or calling this action."
1681
+ };
1682
+ }
1683
+ if (resource.resourceType !== "workspace") {
1684
+ return {
1685
+ path: "$.resource.resourceType",
1686
+ code: "const",
1687
+ message: 'This action requires resourceType "workspace".'
1688
+ };
1689
+ }
1690
+ if (!resource.resourceId.trim()) {
1691
+ return {
1692
+ path: "$.resource.resourceId",
1693
+ code: "required",
1694
+ message: "This action requires a workspace resourceId."
1695
+ };
1696
+ }
1697
+ return null;
1698
+ }
1664
1699
  function resolveConfiguredAppId(slug, appIds) {
1665
1700
  for (const serviceKey of serviceKeyCandidates2(slug)) {
1666
1701
  const configured = appIds?.[serviceKey];
@@ -2818,52 +2853,58 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
2818
2853
  if (process.env[`${prefix}_API_KEY`] || process.env[`${prefix}_ACCESS_TOKEN`] || environment === "production" && process.env.TOPOLO_API_KEY || environment === "production" && process.env.TOPOLO_ACCESS_TOKEN) {
2819
2854
  return resolveCredential(environment);
2820
2855
  }
2821
- const config = await readConfig();
2822
- const profile = profileForEnvironment(config, environment);
2823
- const cred = profile.credential;
2824
- if (!cred || cred.kind !== "access_token") return cred ?? null;
2825
- if (!cred.refreshToken || !profile.oauthClientId) return cred;
2826
- const now = Math.floor(Date.now() / 1e3);
2827
- if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
2828
- const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
2829
- if (environment === "local" && !serviceUrls?.auth) {
2830
- throw new TopoloSdkError2(
2831
- "environment_service_url_required",
2832
- "Local Topolo credential refresh requires an explicit local Auth service origin."
2833
- );
2834
- }
2835
- try {
2836
- const oauth = new TopoloOAuth({
2837
- serviceUrls
2838
- });
2839
- const tokens = await oauth.refreshToken({
2840
- clientId: profile.oauthClientId,
2841
- refreshToken: cred.refreshToken
2842
- });
2843
- const refreshed = {
2844
- kind: "access_token",
2845
- accessToken: tokens.access_token,
2846
- ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: cred.refreshToken },
2847
- expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
2848
- };
2849
- const activeContext = profile.activeContext;
2850
- const activeEntry = activeContext ? profile.contexts?.[activeContext] : void 0;
2851
- const updated = activeContext && activeEntry ? writeProfileConfig(config, environment, {
2852
- credential: refreshed,
2853
- contexts: {
2854
- ...profile.contexts ?? {},
2855
- [activeContext]: {
2856
- ...activeEntry,
2857
- credential: refreshed,
2858
- oauthClientId: activeEntry.oauthClientId ?? profile.oauthClientId
2856
+ const observed = profileForEnvironment(await readConfig(), environment).credential;
2857
+ return withConfigWriteLock(async () => {
2858
+ const config = await readConfig();
2859
+ const profile = profileForEnvironment(config, environment);
2860
+ const cred = profile.credential;
2861
+ if (!cred || cred.kind !== "access_token") return cred ?? null;
2862
+ if (!cred.refreshToken || !profile.oauthClientId) return cred;
2863
+ if (options.force && observed?.kind === "access_token" && observed.accessToken !== cred.accessToken) {
2864
+ return cred;
2865
+ }
2866
+ const now = Math.floor(Date.now() / 1e3);
2867
+ if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
2868
+ const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
2869
+ if (environment === "local" && !serviceUrls?.auth) {
2870
+ throw new TopoloSdkError2(
2871
+ "environment_service_url_required",
2872
+ "Local Topolo credential refresh requires an explicit local Auth service origin."
2873
+ );
2874
+ }
2875
+ try {
2876
+ const oauth = new TopoloOAuth({
2877
+ serviceUrls
2878
+ });
2879
+ const tokens = await oauth.refreshToken({
2880
+ clientId: profile.oauthClientId,
2881
+ refreshToken: cred.refreshToken
2882
+ });
2883
+ const refreshed = {
2884
+ kind: "access_token",
2885
+ accessToken: tokens.access_token,
2886
+ ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: cred.refreshToken },
2887
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
2888
+ };
2889
+ const activeContext = profile.activeContext;
2890
+ const activeEntry = activeContext ? profile.contexts?.[activeContext] : void 0;
2891
+ const updated = activeContext && activeEntry ? writeProfileConfig(config, environment, {
2892
+ credential: refreshed,
2893
+ contexts: {
2894
+ ...profile.contexts ?? {},
2895
+ [activeContext]: {
2896
+ ...activeEntry,
2897
+ credential: refreshed,
2898
+ oauthClientId: activeEntry.oauthClientId ?? profile.oauthClientId
2899
+ }
2859
2900
  }
2860
- }
2861
- }) : environment === "production" && !config.profiles?.production ? { ...config, credential: refreshed } : writeProfileConfig(config, environment, { credential: refreshed });
2862
- await writeConfig(updated);
2863
- return refreshed;
2864
- } catch {
2865
- return cred;
2866
- }
2901
+ }) : environment === "production" && !config.profiles?.production ? { ...config, credential: refreshed } : writeProfileConfig(config, environment, { credential: refreshed });
2902
+ await writeConfig(updated);
2903
+ return refreshed;
2904
+ } catch {
2905
+ return cred;
2906
+ }
2907
+ });
2867
2908
  }
2868
2909
  async function refreshContextCredentialIfNeeded(environment, key, options = {}) {
2869
2910
  return withConfigWriteLock(async () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.11.9",
4
- "topoloPublishedVersion": "0.11.9",
3
+ "version": "0.11.11",
4
+ "topoloPublishedVersion": "0.11.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
  "license": "UNLICENSED",
7
7
  "author": "Topolo",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@modelcontextprotocol/sdk": "^1.0.0",
38
- "@topolo/sdk": "0.11.9"
38
+ "@topolo/sdk": "0.11.11"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^20.12.0",