@thalovant/mcp-server 0.7.0 → 0.7.1

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,8 +1,13 @@
1
- Last Edit: Claude Opus 5.5 - 2026-11-03 - Motive: 0.7.0 chat apps tools; date the published 0.6.6.
1
+ Last Edit: Claude Fable 5.1 - 2026-11-06 - Motive: 0.7.1 hints for the store's licensing refusal and the memory limit; date the published 0.7.0.
2
2
 
3
3
  # Changelog
4
4
 
5
- ## 0.7.0 - Unreleased
5
+ ## 0.7.1 - Unreleased
6
+
7
+ - **Two refusals explained by their code.** A skill the store does not install for licensing reasons (YouTube, YouTube Music, Spotify, SoundCloud and Bandcamp; the API's 409 `skill_unavailable_licensing` with `reason: licensing`, declared with the launch data-protection change) got the generic 409 hint, which talks about a version already installed. It now says the service's terms do not allow a hosted assistant to play its content, that no plan, scope, version or retry changes that, that radio and aggregator skills install as usual, and that `thalovant_list_marketplace_skills` reports it per skill as `unavailable_reason` and `installable`. A memory create or update refused with 409 `memory_limit` (the plan's remembered items are used up; a Free plan keeps one) carried no hint at all, because the memory tools never went through the hint path; `thalovant_create_memory_item` and `thalovant_update_memory_item` now do, under a profile of their own that adds a hint for that code alone (the hub-centric scope and plan hints would misdescribe the memory routes), naming the `limit` and `upgrade_plan` fields, the delete tool that frees a slot, the update tool that changes the item that exists, and that granting a scope will not help. Two tests drive both through the fake control plane.
8
+ - `package.json`, `package-lock.json`, `server.json`, the OCI label, the README and this file move to 0.7.1 together.
9
+
10
+ ## 0.7.0 - 2026-11-03
6
11
 
7
12
  A hub can answer in a chat network through a chat app: Telegram and Discord, and Matrix, Mattermost and Twitch as beta once the API lists them. These tools let an agent read and look after the bridges a person connected in the console. Existing tool schemas are unchanged.
8
13
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Last Edit: Claude Opus 5.5 - 2026-11-03 - Motive: Chat apps tools (connectors, bridges, pause/resume, chats, test message, allowance) for 0.7.0.
1
+ Last Edit: Claude Fable 5.1 - 2026-11-06 - Motive: Say what the 409 skill_unavailable_licensing and memory_limit refusals get as hints (0.7.1).
2
2
 
3
3
  # Thalovant MCP Server
4
4
 
@@ -103,7 +103,7 @@ Grant fewer scopes for narrower deployments: a read-only assistant needs only `h
103
103
 
104
104
  `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`.
105
105
 
106
- 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`, which appends the root `code` in parentheses, for example `Thalovant API request failed with HTTP 409: Skill version already installed. (skill_version_already_installed)`, and then the code and the body's other fields (see [Tool Errors](#tool-errors)).
106
+ 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`, which appends the root `code` in parentheses, for example `Thalovant API request failed with HTTP 409: Skill version already installed. (skill_version_already_installed)`, and then the code and the body's other fields (see [Tool Errors](#tool-errors)). A skill the store does not install for licensing reasons (YouTube, YouTube Music, Spotify, SoundCloud and Bandcamp: the service's terms do not allow a hosted assistant to play its content) fails `409` with code `skill_unavailable_licensing` and `reason: licensing`, which `thalovant_list_marketplace_skills` announces per skill as `unavailable_reason` and `installable`; no plan, scope or version changes that.
107
107
 
108
108
  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.
109
109
 
@@ -517,6 +517,13 @@ fields: {"allowed_images":{"bus":[...],"core":[...]},"allowed_repositories":{"co
517
517
 
518
518
  A hint may follow for statuses that need one. A `platform_image_required` or
519
519
  `plan_limit` 403 gets its own hint rather than the general scope hint.
520
+ A 409 `skill_unavailable_licensing` says the store refuses the skill for its
521
+ licensing and where the catalog announces it, instead of the version hint. A
522
+ memory create or update refused with 409 `memory_limit` (the plan's remembered
523
+ items are used up; a Free plan keeps one, `limit` and `upgrade_plan` are in the
524
+ fields) names the delete tool that frees a slot, the update tool that changes
525
+ the item that exists, and the plan to move to; other memory refusals carry the
526
+ SDK's message alone.
520
527
 
521
528
  ## Request hints, embedded audio, and configuration merging
522
529
 
@@ -589,7 +596,7 @@ Runtime calls sharing the same hub client identity run sequentially within one
589
596
  MCP process. Use a distinct client identity for each independently running MCP
590
597
  server so the hub can keep their sessions separate.
591
598
 
592
- Version 0.7.0 requires `@thalovant/sdk` `^0.10.0` (0.10.0 through versions below 0.11.0), which carries the chat app methods and `ThalovantApiError.fields`. Since 0.5.1 the server enforces
599
+ Version 0.7.1 requires `@thalovant/sdk` `^0.10.0` (0.10.0 through versions below 0.11.0), which carries the chat app methods and `ThalovantApiError.fields`. Since 0.5.1 the server enforces
593
600
  secure effective MQTT URLs and carries a single connection deadline through
594
601
  MQTT setup and HTTP failure cleanup. Runtime tools support
595
602
  HiveMind v3 Noise over WSS, HTTPS and MQTT over TLS. `thalovant_healthcheck`
package/dist/index.js CHANGED
@@ -259,11 +259,18 @@ function releaseOptionsFrom(args) {
259
259
  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.';
260
260
  const PLATFORM_IMAGE_HINT_403 = 'HTTP 403 platform_image_required — only a platform administrator can run an image the platform does not release. This is not a scope problem: pick, for each key in refused_images, one of that key\'s allowed_images, or any tag or digest of its allowed_repositories entry (both are in the fields above), and retry.';
261
261
  const PLAN_LIMIT_HINT_403 = 'HTTP 403 plan_limit — the plan\'s allowance for the named resource is used up (see resource, used and limit in the fields above). Granting a scope will not help: free one (delete or reuse an existing one) or move the workspace to a larger plan.';
262
+ const LICENSING_HINT_409 = "HTTP 409 skill_unavailable_licensing — the store does not install this skill on Thalovant: the service's terms do not allow a hosted assistant to play its content (YouTube, YouTube Music, Spotify, SoundCloud and Bandcamp; the problem's reason says licensing). No plan, scope or version changes that, and retrying will not either; radio and aggregator skills such as TuneIn, SomaFM and News install as usual. thalovant_list_marketplace_skills reports this per skill as unavailable_reason and installable (false), so check them before installing.";
263
+ const MEMORY_LIMIT_HINT_409 = "HTTP 409 memory_limit — the plan's remembered items are used up (see limit in the fields above; a Free plan keeps one). Free a slot with thalovant_delete_memory_item, change the item that exists with thalovant_update_memory_item, or move the workspace to the plan upgrade_plan names (Home); granting a scope will not help.";
262
264
  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.';
263
265
  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.';
264
266
  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.';
265
267
  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.';
266
268
  function controlPlaneErrorHint(profile, status, body, code) {
269
+ if (profile === "memoryWrite") {
270
+ // The memory routes have their own scopes and refusals, which the hub-centric
271
+ // hints below would misdescribe; the one worth a hint is the plan's limit.
272
+ return status === 409 && code === "memory_limit" ? MEMORY_LIMIT_HINT_409 : undefined;
273
+ }
267
274
  switch (status) {
268
275
  case 402:
269
276
  return (profile === "skillInstall" || profile === "hubSkillInstall") && /marketplace/i.test(body) ? MARKETPLACE_HINT_402 : PLAN_HINT_402;
@@ -280,6 +287,8 @@ function controlPlaneErrorHint(profile, status, body, code) {
280
287
  ? "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."
281
288
  : undefined;
282
289
  case 409:
290
+ if (code === "skill_unavailable_licensing")
291
+ return LICENSING_HINT_409;
283
292
  if (profile === "hubWrite") {
284
293
  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.';
285
294
  }
@@ -1990,7 +1999,7 @@ export function createServer() {
1990
1999
  const { loginScope, apiUrl, ...payload } = args;
1991
2000
  const api = await createControlPlane({ loginScope, apiUrl });
1992
2001
  ensureAuthenticated(api);
1993
- return jsonContent(redactSecrets(await api.createMemoryItem(payload)));
2002
+ return jsonContent(redactSecrets(await callControlPlane("memoryWrite", () => api.createMemoryItem(payload))));
1994
2003
  });
1995
2004
  registerThalovantTool(server, "thalovant_update_memory_item", {
1996
2005
  title: "Update Memory Item",
@@ -2019,7 +2028,7 @@ export function createServer() {
2019
2028
  const { loginScope, apiUrl, memoryId, ...payload } = args;
2020
2029
  const api = await createControlPlane({ loginScope, apiUrl });
2021
2030
  ensureAuthenticated(api);
2022
- return jsonContent(redactSecrets(await api.updateMemoryItem(memoryId, payload)));
2031
+ return jsonContent(redactSecrets(await callControlPlane("memoryWrite", () => api.updateMemoryItem(memoryId, payload))));
2023
2032
  });
2024
2033
  registerThalovantTool(server, "thalovant_delete_memory_item", {
2025
2034
  title: "Delete Memory Item",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thalovant/mcp-server",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
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",
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.7.0",
10
+ "version": "0.7.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@thalovant/mcp-server",
15
- "version": "0.7.0",
15
+ "version": "0.7.1",
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.7.0",
22
+ "identifier": "ghcr.io/thalovant/thalovant-mcp:0.7.1",
23
23
  "transport": {
24
24
  "type": "streamable-http",
25
25
  "url": "http://127.0.0.1:3000/mcp"