@thalovant/mcp-server 0.1.22 → 0.1.24

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.24
4
+
5
+ - Require Node SDK 0.3.16 and use its public hub-skill methods instead of the temporary duplicate HTTP and polling implementation. Preserve tool schemas, accepted-operation output, API guidance and MCP cancellation checks.
6
+ - Stop starting status reads once the polling budget expires. Keep the accepted operation ID visible when polling fails or the operation reports failure, so callers can inspect the existing operation without repeating the write. The polling deadline does not cancel an HTTP request already in flight.
7
+ - Add real stdio regressions for failed status reads, deadline boundaries and cancellation; retain the 45-tool default catalog across stdio and Streamable HTTP.
8
+
9
+ ## 0.1.23
10
+
11
+ - Add hub-scoped skill management: `thalovant_list_hub_skills` (read-only, `hubs:inspect`), `thalovant_install_hub_skill`, `thalovant_update_hub_skill` and `thalovant_remove_hub_skill` (`hubs:write`, paid plan) act on one hub rather than a runtime group's skill set; changes apply live on the hub in about 15 seconds. The list tool returns the whole `GET /v1/hubs/{hub_id}/skills` envelope (`hub_id`, `runtime_group_id`, `observed_at`, `source`, the runtime's phase and message, and `data` rows with a `pending|installed|failed|removing|drifted|quarantined|unmanaged` state). Writes answer with the accepted operation (`operation_id`, `hub_id`, `runtime_group_id`, `skill`, `version`, `previous_version`, `state`) and take `wait` to poll it every 2 s until it converges (`installed`/`removed`, or an error carrying the operation's `error_message`), honoring MCP request cancellation and a 120 s default `timeoutMs`. Installing an already-installed skill at another version performs an update.
12
+ - Surface the API's RFC 7807 problem codes: the tool error keeps the short message and appends the root `code`, e.g. `HTTP 409: Skill version already installed. (skill_version_already_installed)`, and the guidance explains `409 skill_version_already_installed`, `404 hub_without_runtime_group`, and a `422` for an unresolvable `latest` or invalid version on install.
13
+ - Call `GET/POST /v1/hubs/{hub_id}/skills` and `PATCH/DELETE /v1/hubs/{hub_id}/skills/{skill}` directly, with the SDK's header, HTTPS-or-loopback and redirect conventions, and poll with the SDK's `getOperation`, until the Node SDK ships these methods (`@thalovant/sdk` >= 0.3.15). Every route and response type (the final contract of thalovant-api #253) lives in one marked block in `src/index.ts`.
14
+ - The default catalogue now contains 45 tools (22 in read-only mode, 47 with destructive tools enabled). Existing tool schemas are unchanged.
15
+
3
16
  ## 0.1.22
4
17
 
5
18
  - Require Node SDK 0.3.14 for fail-closed persistent Noise trust, complete filesystem state transactions across Node processes, active reply-ID guards, and intent-description timeouts that only recover when usable definitions were received. Preserve process-local runtime identity leases and require distinct identities across independently running MCP processes.
package/README.md CHANGED
@@ -57,8 +57,8 @@ Minimum scopes for the full control-plane tool surface:
57
57
  | Scope | Used by |
58
58
  |-------|---------|
59
59
  | `hubs:read` | `thalovant_list_hubs`, `thalovant_get_hub`, `thalovant_get_analytics_overview`, `thalovant_list_marketplace_skills`, `thalovant_list_runtime_groups`, `thalovant_get_runtime_group`, `thalovant_get_runtime_group_config`, and the hub lookup inside `thalovant_create_client_identity` |
60
- | `hubs:inspect` | `thalovant_get_hub_runtime_capabilities`, `thalovant_list_runtime_group_marketplace`, `thalovant_list_runtime_group_inventory` |
61
- | `hubs:write` | All hub and runtime-group provisioning: `thalovant_create_hub`, `thalovant_update_hub`, `thalovant_release_hub`, `thalovant_create_runtime_group`, `thalovant_update_runtime_group`, `thalovant_update_runtime_group_config`, `thalovant_release_runtime_group`, `thalovant_install_runtime_group_skill`, `thalovant_uninstall_runtime_group_skill`, the hub rating tools, and the opt-in delete tools |
60
+ | `hubs:inspect` | `thalovant_get_hub_runtime_capabilities`, `thalovant_list_runtime_group_marketplace`, `thalovant_list_runtime_group_inventory`, `thalovant_list_hub_skills` |
61
+ | `hubs:write` | All hub and runtime-group provisioning: `thalovant_create_hub`, `thalovant_update_hub`, `thalovant_release_hub`, `thalovant_create_runtime_group`, `thalovant_update_runtime_group`, `thalovant_update_runtime_group_config`, `thalovant_release_runtime_group`, `thalovant_install_runtime_group_skill`, `thalovant_uninstall_runtime_group_skill`, the per-hub `thalovant_install_hub_skill`, `thalovant_update_hub_skill`, `thalovant_remove_hub_skill`, the hub rating tools, and the opt-in delete tools |
62
62
  | `clients:write` | `thalovant_create_client_identity` (`POST /v1/clients`) |
63
63
  | `memory:read` | `thalovant_list_memory_items`, `thalovant_get_memory_summary`, `thalovant_get_memory_item` |
64
64
  | `memory:write` | `thalovant_create_memory_item`, `thalovant_update_memory_item`, `thalovant_delete_memory_item` |
@@ -79,6 +79,16 @@ Grant fewer scopes for narrower deployments: a read-only assistant needs only `h
79
79
 
80
80
  `thalovant_update_hub` and `thalovant_delete_hub` use optimistic locking and require the hub's current etag, sent as `If-Match`. The etag is only available in the **body** of the hub resource — the API sends no `ETag` response header — so an agent must call `thalovant_get_hub` first and pass the `etag` field from that response. A missing or stale value fails `412 ETag mismatch` and changes nothing; re-fetch and retry. `name`, `namespace`, and `domain` are immutable after creation, so `thalovant_update_hub` does not accept them at all; send only the fields you are changing rather than round-tripping a whole hub resource. Runtime-group writes do not use etags.
81
81
 
82
+ ### Hub Skills
83
+
84
+ `thalovant_list_hub_skills`, `thalovant_install_hub_skill`, `thalovant_update_hub_skill`, and `thalovant_remove_hub_skill` act on **one hub**, not on a runtime group's skill set. A hub can start with no skills at all and gain them one at a time; a change applies live on the hub in about 15 seconds with no restart. `hubId` must be the hub UUID — the authenticated hub routes reject slugs.
85
+
86
+ `thalovant_list_hub_skills` returns the whole `GET /v1/hubs/{hub_id}/skills` envelope: `hub_id`, `runtime_group_id`, `observed_at`, `source`, the runtime's phase and message, and `data`, one row per skill (possibly empty) with `skill`, `title`, `marketplace_skill_id`, `package_name`, `source_type`, `install_source`, `version`, `version_pin`, `installed_version`, `observed_version`, `previous_version`, `latest_version`, `available_version`, `update_available`, `changelog`, `active`, `state`, the runtime's phase, message and last error, and `last_transition_at`. `state` is one of `pending`, `installed`, `failed`, `removing`, `drifted`, `quarantined`, or `unmanaged`; a change in progress shows as `pending`.
87
+
88
+ Each write answers `202` with `operation_id`, `hub_id`, `runtime_group_id`, `skill`, `version` (`null` for a removal), `previous_version`, and `state` (`installing`, `updating`, or `removing`); pass `wait: true` to poll that operation every 2 s until it converges (`installed`, or `removed` for a removal; a `failed` or `timed_out` operation raises an error carrying its `error_message`), with a 120 s default `timeoutMs`, or follow it yourself with `thalovant_get_operation`. Installing a skill that is already installed at another version performs an update; the same version fails `409` with code `skill_version_already_installed`. A hub with no runtime group fails `404` with code `hub_without_runtime_group` (a plain `404` means an unknown hub or a skill that is not installed), and an unresolvable `latest` or an invalid version fails `422`. Errors are RFC 7807 problem bodies; the tool error keeps the short `message` and appends the root `code` in parentheses, for example `Thalovant API request failed with HTTP 409: Skill version already installed. (skill_version_already_installed)`.
89
+
90
+ Listing needs `hubs:inspect` (implied by `hubs:read`); the writes need `hubs:write` and a paid plan, and because scope is checked before plan a free-plan token sees `403`, never `402`. Hub-restricted tokens (a `hub_ids` allowlist) are honoured on all four routes. The server uses the published Node SDK hub-skill methods and preserves MCP cancellation checks during polling. A failed status read retains the accepted operation ID; use `thalovant_get_operation` with that ID instead of submitting the write again. No new poll starts at or after the polling deadline. That deadline does not cancel an HTTP request already in flight.
91
+
82
92
  ### Login Fallback
83
93
 
84
94
  ```bash
@@ -358,6 +368,13 @@ Hub and runtime-group provisioning:
358
368
  - `thalovant_install_runtime_group_skill`
359
369
  - `thalovant_uninstall_runtime_group_skill`
360
370
 
371
+ Hub skills, acting on one hub (see [Hub Skills](#hub-skills); the list tool is read-only):
372
+
373
+ - `thalovant_list_hub_skills`
374
+ - `thalovant_install_hub_skill`
375
+ - `thalovant_update_hub_skill`
376
+ - `thalovant_remove_hub_skill`
377
+
361
378
  Destructive, **not registered unless explicitly enabled** (see [Destructive Tools](#destructive-tools)):
362
379
 
363
380
  - `thalovant_delete_hub`
@@ -419,7 +436,7 @@ Runtime calls sharing the same hub client identity run sequentially within one
419
436
  MCP process. Use a distinct client identity for each independently running MCP
420
437
  server so the hub can keep their sessions separate.
421
438
 
422
- Version 0.1.22 uses `@thalovant/sdk` `^0.3.14` (0.3.14 through versions below 0.4.0). This release enforces
439
+ Version 0.1.24 uses `@thalovant/sdk` `^0.3.16` (0.3.16 through versions below 0.4.0). This release enforces
423
440
  secure effective MQTT URLs and carries a single connection deadline through
424
441
  MQTT setup and HTTP failure cleanup. Runtime tools support
425
442
  HiveMind v3 Noise over WSS, HTTPS and MQTT over TLS. `thalovant_healthcheck`
package/dist/index.js CHANGED
@@ -211,20 +211,27 @@ function releaseOptionsFrom(args) {
211
211
  const SCOPE_HINT_403 = 'HTTP 403 "Insufficient scopes" — the token lacks the scope this route needs: hubs:write for provisioning, hubs:read for the marketplace catalog, hubs:inspect for runtime-group and hub runtime views (hubs:write implies hubs:read, which implies hubs:inspect and hubs:preview). The API checks scope BEFORE the paid-plan gate, so a 403 can mask a plan problem and granting the scope may surface a 402 next. Free-plan API tokens are capped at hubs:read, clients:read, and clients:write, so a free-tier API token fails provisioning with this 403 and never reaches the 402. A 403 here can also mean the caller does not own the resource ("Ownership required"), or a plan restriction such as HUB_AUTOSCALING_NOT_INCLUDED or a custom hub domain the plan does not allow.';
212
212
  const PLAN_HINT_402 = 'HTTP 402 "API access requires a paid plan." — the token is valid and correctly scoped but the tenant is on the free plan. Free-tier callers can browse the catalog (thalovant_list_marketplace_skills) and set hub ratings, but cannot create, update, release, or delete hubs and runtime groups, and cannot install skills.';
213
213
  const MARKETPLACE_HINT_402 = 'HTTP 402 "This skill requires paid marketplace access for the tenant plan." — a DIFFERENT 402 from the plan gate: the plan is paid enough to provision, but this catalog entry has access_tier "paid" and the plan does not include paid marketplace skills. thalovant_list_runtime_group_marketplace reports this per skill as purchase_required, installable, and access_message; check it before installing.';
214
+ const HUB_SKILL_HINT_422 = 'HTTP 422 — the requested hub skill version is unusable: "latest" could not be resolved to a published version of that skill, or the version string is not a valid version. Check the skill name and its published versions with thalovant_list_marketplace_skills, or pass an explicit x.y.z.';
214
215
  const ETAG_HINT_412 = 'HTTP 412 "ETag mismatch" — the If-Match etag was missing or stale and nothing changed. Re-fetch the hub with thalovant_get_hub, read the `etag` field from the response BODY (there is no ETag response header), and retry with that exact value. The comparison is exact string equality, so do not rewrite or weaken the value.';
215
216
  function controlPlaneErrorHint(profile, status, body) {
216
217
  switch (status) {
217
218
  case 402:
218
- return profile === "skillInstall" && /marketplace/i.test(body) ? MARKETPLACE_HINT_402 : PLAN_HINT_402;
219
+ return (profile === "skillInstall" || profile === "hubSkillInstall") && /marketplace/i.test(body) ? MARKETPLACE_HINT_402 : PLAN_HINT_402;
219
220
  case 403:
220
221
  return SCOPE_HINT_403;
222
+ case 404:
223
+ return /hub_without_runtime_group/.test(body)
224
+ ? "HTTP 404 (hub_without_runtime_group) — the hub exists but has no runtime group attached yet, so it cannot hold skills; attach it to a runtime group (thalovant_update_hub) before managing its skills. A 404 without that code means the hub is unknown, or the named skill is not installed on it."
225
+ : undefined;
221
226
  case 409:
222
227
  if (profile === "hubWrite") {
223
228
  return 'HTTP 409 — for a hub create this is either a duplicate ("Hub with name ... already exists for this owner" / "Hub slug ... already exists"), an autoscaling slot limit (AUTOSCALING_HUB_LIMIT_REACHED), or "Idempotency key re-used with different payload". Only hub create honors Idempotency-Key: retrying an identical create is safe and returns the original hub, but reusing a key with a changed body conflicts — use a new idempotencyKey for a genuinely different hub.';
224
229
  }
225
- return "HTTP 409 — the resource is not in a state that allows this call: a hub with no connected client cannot report runtime capabilities, a runtime group cannot be deleted while it is the workspace default or still has hubs attached, and a deactivated marketplace skill cannot be installed.";
230
+ return "HTTP 409 — the resource is not in a state that allows this call: a hub with no connected client cannot report runtime capabilities, a runtime group cannot be deleted while it is the workspace default or still has hubs attached, a deactivated marketplace skill cannot be installed, and a hub skill install answers 409 with code skill_version_already_installed when the skill is already installed at exactly that version (POST with a DIFFERENT version performs an update, as does thalovant_update_hub_skill).";
226
231
  case 412:
227
232
  return ETAG_HINT_412;
233
+ case 422:
234
+ return profile === "hubSkillInstall" ? HUB_SKILL_HINT_422 : undefined;
228
235
  default:
229
236
  return undefined;
230
237
  }
@@ -249,6 +256,39 @@ async function callControlPlane(profile, run) {
249
256
  throw new Error(`${message}\n\n${hint}`);
250
257
  }
251
258
  }
259
+ const HUB_SKILL_DEFAULT_TIMEOUT_MS = 120_000;
260
+ const HUB_SKILL_MAX_TIMEOUT_MS = 600_000;
261
+ function sleepWithSignal(ms, signal) {
262
+ return new Promise((resolve, reject) => {
263
+ if (signal?.aborted) {
264
+ reject(new Error("The request was cancelled."));
265
+ return;
266
+ }
267
+ const timer = setTimeout(() => {
268
+ signal?.removeEventListener("abort", onAbort);
269
+ resolve();
270
+ }, ms);
271
+ function onAbort() {
272
+ clearTimeout(timer);
273
+ reject(new Error("The request was cancelled."));
274
+ }
275
+ signal?.addEventListener("abort", onAbort, { once: true });
276
+ });
277
+ }
278
+ /** Keep MCP cancellation attached to the SDK-owned operation polling. */
279
+ function hubSkillWaitOptions(wait, timeoutMs) {
280
+ const signal = requestContext.getStore()?.signal;
281
+ throwIfRuntimeCancelled(signal);
282
+ return {
283
+ wait: wait ?? false,
284
+ timeoutMs: timeoutMs ?? HUB_SKILL_DEFAULT_TIMEOUT_MS,
285
+ sleep: (ms) => sleepWithSignal(ms, signal),
286
+ now: () => {
287
+ throwIfRuntimeCancelled(signal);
288
+ return Date.now();
289
+ },
290
+ };
291
+ }
252
292
  class HttpError extends Error {
253
293
  status;
254
294
  constructor(status, message) {
@@ -2112,6 +2152,93 @@ export function createServer() {
2112
2152
  await callControlPlane("write", () => api.uninstallRuntimeGroupSkill(runtimeGroupId, skillId));
2113
2153
  return textContent("Runtime group skill uninstalled.");
2114
2154
  });
2155
+ registerThalovantTool(server, "thalovant_list_hub_skills", {
2156
+ title: "List Hub Skills",
2157
+ description: "List the skills installed on ONE Thalovant hub. Returns the whole envelope: hub_id, runtime_group_id, observed_at, source, the runtime's phase and message, and data — one row per skill with version, version_pin, installed_version, observed_version, previous_version, latest_version, available_version, update_available, changelog, active, state (pending, installed, failed, removing, drifted, quarantined, unmanaged; a change in progress shows as pending), the runtime's last error and last_transition_at. This is per-hub, not the runtime group's skill set: a hub can start with an empty data array and gain skills one at a time with thalovant_install_hub_skill. hubId must be the hub UUID — the authenticated hub routes reject slugs; a hub with no runtime group attached fails 404 with code hub_without_runtime_group. Requires the hubs:inspect scope (hubs:read implies it); hub-restricted tokens only see the hubs in their allowlist.",
2158
+ inputSchema: {
2159
+ ...controlPlaneSchema,
2160
+ hubId: z.string().min(1).describe("Hub UUID (not the slug)."),
2161
+ },
2162
+ annotations: { readOnlyHint: true, openWorldHint: true },
2163
+ }, async ({ hubId, ...auth }) => {
2164
+ const api = await createControlPlane(auth);
2165
+ ensureAuthenticated(api);
2166
+ throwIfRuntimeCancelled(requestContext.getStore()?.signal);
2167
+ return jsonContent(redactSecrets(await callControlPlane("read", () => api.listHubSkills(hubId))));
2168
+ });
2169
+ registerThalovantTool(server, "thalovant_install_hub_skill", {
2170
+ title: "Install Hub Skill",
2171
+ description: "Install a skill on ONE Thalovant hub (not a runtime group or skill set). The change applies live on the hub in about 15 seconds, with no hub restart; a hub may start with no skills and gain them one at a time. Discover the skill name with thalovant_list_marketplace_skills. The API answers 202 with an operation_id and state \"installing\": pass wait: true to poll the operation until it converges (state \"installed\", or an error carrying the operation's failure message), with a 120 s default timeout; otherwise follow it with thalovant_get_operation. The 202 body also carries hub_id, runtime_group_id and previous_version. Installing a skill that is already installed at ANOTHER version performs an update; the SAME version fails 409 with code skill_version_already_installed. Fails 404 with code hub_without_runtime_group when the hub has no runtime group attached yet, and 422 when \"latest\" cannot be resolved or the version string is invalid. hubId must be the hub UUID — the authenticated hub routes reject slugs. Requires the hubs:write scope and a paid plan; scope is checked before plan, so a free-plan token sees 403, never 402. Hub-restricted tokens may only act on the hubs in their allowlist.",
2172
+ inputSchema: {
2173
+ ...controlPlaneSchema,
2174
+ hubId: z.string().min(1).describe("Hub UUID (not the slug)."),
2175
+ skill: z.string().min(1).max(191).describe("Skill name, 1-191 characters."),
2176
+ version: z.string().min(1).max(64).optional().describe('Version to install, 1-64 characters. Defaults to "latest".'),
2177
+ wait: z.boolean().optional().describe("Poll the operation until the install converges (installed or failed). Defaults to false."),
2178
+ timeoutMs: z.number().int().min(1_000).max(HUB_SKILL_MAX_TIMEOUT_MS).optional().describe("How long to wait when wait is true, 1000-600000 ms. Defaults to 120000."),
2179
+ },
2180
+ annotations: {
2181
+ readOnlyHint: false,
2182
+ destructiveHint: false,
2183
+ idempotentHint: false,
2184
+ openWorldHint: true,
2185
+ },
2186
+ }, async ({ hubId, skill, version, wait, timeoutMs, ...auth }) => {
2187
+ const api = await createControlPlane(auth);
2188
+ ensureAuthenticated(api);
2189
+ const options = hubSkillWaitOptions(wait, timeoutMs);
2190
+ const result = await callControlPlane("hubSkillInstall", () => api.installHubSkill(hubId, skill, {
2191
+ ...options, version: version ?? "latest",
2192
+ }));
2193
+ return jsonContent(redactSecrets({ ...result, operation: result.operation ?? null }));
2194
+ });
2195
+ registerThalovantTool(server, "thalovant_update_hub_skill", {
2196
+ title: "Update Hub Skill",
2197
+ description: "Move a skill already installed on ONE Thalovant hub to a specific version (not a runtime group or skill set). The change applies live on the hub in about 15 seconds, with no hub restart. version is required — read the current version and latest_version from thalovant_list_hub_skills. The API answers 202 with an operation_id, hub_id, runtime_group_id, previous_version and state \"updating\": pass wait: true to poll the operation until it converges (state \"installed\", or an error carrying the operation's failure message), with a 120 s default timeout; otherwise follow it with thalovant_get_operation. Fails 404 when the skill is not installed on the hub (code hub_without_runtime_group when the hub has no runtime group), 409 with code skill_version_already_installed when it is already at that version, and 422 for an invalid version. hubId must be the hub UUID — the authenticated hub routes reject slugs. Requires the hubs:write scope and a paid plan; scope is checked before plan, so a free-plan token sees 403, never 402. Hub-restricted tokens may only act on the hubs in their allowlist.",
2198
+ inputSchema: {
2199
+ ...controlPlaneSchema,
2200
+ hubId: z.string().min(1).describe("Hub UUID (not the slug)."),
2201
+ skill: z.string().min(1).max(191).describe("Skill name, 1-191 characters."),
2202
+ version: z.string().min(1).max(64).describe("Required target version, 1-64 characters."),
2203
+ wait: z.boolean().optional().describe("Poll the operation until the update converges (installed or failed). Defaults to false."),
2204
+ timeoutMs: z.number().int().min(1_000).max(HUB_SKILL_MAX_TIMEOUT_MS).optional().describe("How long to wait when wait is true, 1000-600000 ms. Defaults to 120000."),
2205
+ },
2206
+ annotations: {
2207
+ readOnlyHint: false,
2208
+ destructiveHint: false,
2209
+ idempotentHint: true,
2210
+ openWorldHint: true,
2211
+ },
2212
+ }, async ({ hubId, skill, version, wait, timeoutMs, ...auth }) => {
2213
+ const api = await createControlPlane(auth);
2214
+ ensureAuthenticated(api);
2215
+ const options = hubSkillWaitOptions(wait, timeoutMs);
2216
+ const result = await callControlPlane("write", () => api.updateHubSkill(hubId, skill, { ...options, version }));
2217
+ return jsonContent(redactSecrets({ ...result, operation: result.operation ?? null }));
2218
+ });
2219
+ registerThalovantTool(server, "thalovant_remove_hub_skill", {
2220
+ title: "Remove Hub Skill",
2221
+ description: "Remove one skill from ONE Thalovant hub (not a runtime group or skill set). Only the named skill is removed; the hub and its other skills are untouched, and the change applies live on the hub in about 15 seconds with no hub restart. The API answers 202 with an operation_id, hub_id, runtime_group_id, previous_version (version is null) and state \"removing\": pass wait: true to poll the operation until it converges (state \"removed\", or an error carrying the operation's failure message), with a 120 s default timeout; otherwise follow it with thalovant_get_operation. Fails 404 when the skill is not installed on the hub (code hub_without_runtime_group when the hub has no runtime group). hubId must be the hub UUID — the authenticated hub routes reject slugs. Requires the hubs:write scope and a paid plan; scope is checked before plan, so a free-plan token sees 403, never 402. Hub-restricted tokens may only act on the hubs in their allowlist.",
2222
+ inputSchema: {
2223
+ ...controlPlaneSchema,
2224
+ hubId: z.string().min(1).describe("Hub UUID (not the slug)."),
2225
+ skill: z.string().min(1).max(191).describe("Skill name to remove, 1-191 characters."),
2226
+ wait: z.boolean().optional().describe("Poll the operation until the removal converges (removed or failed). Defaults to false."),
2227
+ timeoutMs: z.number().int().min(1_000).max(HUB_SKILL_MAX_TIMEOUT_MS).optional().describe("How long to wait when wait is true, 1000-600000 ms. Defaults to 120000."),
2228
+ },
2229
+ annotations: {
2230
+ readOnlyHint: false,
2231
+ destructiveHint: true,
2232
+ idempotentHint: true,
2233
+ openWorldHint: true,
2234
+ },
2235
+ }, async ({ hubId, skill, wait, timeoutMs, ...auth }) => {
2236
+ const api = await createControlPlane(auth);
2237
+ ensureAuthenticated(api);
2238
+ const options = hubSkillWaitOptions(wait, timeoutMs);
2239
+ const result = await callControlPlane("write", () => api.removeHubSkill(hubId, skill, options));
2240
+ return jsonContent(redactSecrets({ ...result, operation: result.operation ?? null }));
2241
+ });
2115
2242
  if (destructiveToolsEnabled()) {
2116
2243
  registerThalovantTool(server, "thalovant_delete_hub", {
2117
2244
  title: "Delete Hub",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thalovant/mcp-server",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "Stdio and Streamable HTTP MCP server for Thalovant control-plane and hub runtime APIs.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -64,7 +64,7 @@
64
64
  },
65
65
  "dependencies": {
66
66
  "@modelcontextprotocol/sdk": "^1.30.0",
67
- "@thalovant/sdk": "^0.3.14",
67
+ "@thalovant/sdk": "^0.3.16",
68
68
  "jose": "^6.2.8",
69
69
  "zod": "^3.25.76"
70
70
  },
package/server.json CHANGED
@@ -7,19 +7,19 @@
7
7
  "url": "https://github.com/thalovant/thalovant-mcp",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.1.22",
10
+ "version": "0.1.24",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@thalovant/mcp-server",
15
- "version": "0.1.22",
15
+ "version": "0.1.24",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }
19
19
  },
20
20
  {
21
21
  "registryType": "oci",
22
- "identifier": "ghcr.io/thalovant/thalovant-mcp:0.1.22",
22
+ "identifier": "ghcr.io/thalovant/thalovant-mcp:0.1.24",
23
23
  "transport": {
24
24
  "type": "streamable-http",
25
25
  "url": "http://127.0.0.1:3000/mcp"