@thalovant/mcp-server 0.6.2 → 0.6.4

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.6.4 — 2026-10-19
4
+
5
+ - **Descriptions that say what a runtime tool changes.** `thalovant_send_action`, `thalovant_send_code` and `thalovant_wait_for_event` had one-line descriptions that didn't say whether the tool changes anything. A scanner that reads only names and descriptions rated all three as high-risk tools that execute, `thalovant_wait_for_event` included, though it was already annotated `readOnlyHint: true`. `thalovant_send_action` now says the hub routes its payload like a spoken request, so a skill may act on it. `thalovant_send_code` says the hub receives its value as an exact utterance, and both say they return without waiting for a reply and that `THALOVANT_MCP_READONLY` leaves them out. `thalovant_wait_for_event` says it only listens and sends the hub nothing, and that `sessionId`, `requestId` and `context` filter which events count. `payload`, `title`, `value`, `kind`, `label` and `eventName` now have their own descriptions. Schemas, annotations and behaviour are unchanged.
6
+ - The Claude Desktop and Codex examples start the published package with `npx -y @thalovant/mcp-server`. They used to point at a path on one developer's machine.
7
+ - `package.json`, `server.json`, the OCI label, the README and this file move to 0.6.4 together.
8
+
9
+ ## 0.6.3 — 2026-10-11
10
+
11
+ - **Sentences in the language the hub answers in.** `thalovant_intent_inventory` takes `@thalovant/sdk` 0.9.9, which lists a language the hub never loaded in one of the hub's own languages: the same language in another region first, then the hub's own language, then the one with the most sentences. A hub loads its `lang` and `secondary_langs` and lists nothing under any other tag, so on 2026-10-11 a ja-JP, es-ES or de-DE request came back empty on every public hub, even though the hub answers those people. The output's `listed_in` names the language each requested language was listed in. `examples` are now rendered from that language (they used to be rendered from the requested tag and came back empty). `presentation.notes` says when the sentences are in another language, so an agent can tell the user.
12
+ - **Fallback skills that exist.** The SDK reads fallbacks from `ovos.skills.list` and no longer asks `ovos.skills.fallback.list`, which ovos-core reverted the day it landed. Skills with no intents, such as Daily Desk's memory skill, now appear. `priority` is `null`: the hub does not say in which order it tries fallbacks, and the list is in skill id order. The tool description and README say so. A connection needs `ovos.skills.list` on its allow-list; until then fallbacks stay unknown, as before.
13
+ - Takes `@thalovant/sdk` ^0.9.9, and the lock resolves the published 0.9.9. The parity contract acknowledges reference digest `5b973a65ab58` (see thalovant/thalovant-python-sdk#105).
14
+ - `package.json`, `server.json`, the OCI label, the README and this file move to 0.6.3 together.
15
+
3
16
  ## 0.6.2 — 2026-10-02
4
17
 
5
18
  - Takes `@thalovant/sdk` ^0.9.3, which the lock resolves. Its `reply.claimed` now recognizes a skill's own confident fallback-tier answer; the earlier range silently swallowed a genuinely successful Home Assistant reply (a `claimed` answer from the home skill's fallback tier read as "nothing answered"). No code change in this package: the logic is the SDK's `replyClaimMetadata`, which `src/reply-output.ts` delegates to.
package/README.md CHANGED
@@ -24,6 +24,8 @@ Thalovant publishes SDKs for Python, Node.js, Go, and Rust. This server uses Nod
24
24
 
25
25
  ## Install
26
26
 
27
+ Run the published package with `npx -y @thalovant/mcp-server`, or build it from a checkout:
28
+
27
29
  ```bash
28
30
  npm install
29
31
  npm run build
@@ -308,8 +310,8 @@ Recommended: authenticate with a scoped API token so the MCP config never contai
308
310
  {
309
311
  "mcpServers": {
310
312
  "thalovant": {
311
- "command": "node",
312
- "args": ["/home/goldyfruit/Development/Thalovant/mcp/dist/index.js"],
313
+ "command": "npx",
314
+ "args": ["-y", "@thalovant/mcp-server"],
313
315
  "env": {
314
316
  "THALOVANT_API_TOKEN": "tvpat_...",
315
317
  "THALOVANT_PROFILE": "prod"
@@ -327,8 +329,8 @@ Use the same stdio command in your MCP client config:
327
329
  {
328
330
  "mcpServers": {
329
331
  "thalovant": {
330
- "command": "node",
331
- "args": ["/home/goldyfruit/Development/Thalovant/mcp/dist/index.js"],
332
+ "command": "npx",
333
+ "args": ["-y", "@thalovant/mcp-server"],
332
334
  "env": {
333
335
  "THALOVANT_API_TOKEN": "tvpat_...",
334
336
  "THALOVANT_PROFILE": "prod"
@@ -418,10 +420,17 @@ Tool outputs redact credential-shaped fields. `thalovant_create_client_identity`
418
420
 
419
421
  `thalovant_intent_inventory` uses the runtime identity and accepts `languages`,
420
422
  `describe`, `fallback`, `speakable`, `sentence`, `slots`, `exampleLimit`, and a per-query/batch `timeoutMs`. It returns registered
421
- intents and examples, fallback skills, `fallbacks_known`, and `may_answer` by
422
- requested language. A missing or denied optional fallback-skill query remains
423
- unknown, rather than being reported as a known empty list. The optional probe
424
- adds at most 1500ms. A silent listing can use engine manifests; disable this with
423
+ intents and examples, fallback skills, `fallbacks_known`, `listed_in`, and
424
+ `may_answer` by requested language. `listed_in` names the hub language each
425
+ requested language was listed in. A hub lists nothing under a language it did
426
+ not load, so a language with no sentences is listed in its usual form (`en-CA`
427
+ in `en-US`) or in the closest language the hub loaded, and `examples` are
428
+ rendered in that language. When `listed_in` differs from what was asked, the
429
+ sentences are in another language, and `presentation.notes` says so. Fallback
430
+ skills come from `ovos.skills.list`, in skill id order, with `priority: null`:
431
+ the hub does not say in which order it tries them. A missing or denied
432
+ fallback-skill query remains unknown, rather than being reported as a known
433
+ empty list. The optional probe adds at most 1500ms. A silent listing can use engine manifests; disable this with
425
434
  `fallback: false`. Both new tools are available in read-only mode and respect
426
435
  per-principal policy. `thalovant_get_operation` reads an operation ID returned by
427
436
  provisioning without replaying the write.
@@ -531,7 +540,7 @@ Runtime calls sharing the same hub client identity run sequentially within one
531
540
  MCP process. Use a distinct client identity for each independently running MCP
532
541
  server so the hub can keep their sessions separate.
533
542
 
534
- Version 0.6.2 requires `@thalovant/sdk` `^0.9.3` (0.9.3 through versions below 0.10.0). Since 0.5.1 the server enforces
543
+ Version 0.6.4 requires `@thalovant/sdk` `^0.9.9` (0.9.9 through versions below 0.10.0). Since 0.5.1 the server enforces
535
544
  secure effective MQTT URLs and carries a single connection deadline through
536
545
  MQTT setup and HTTP failure cleanup. Runtime tools support
537
546
  HiveMind v3 Noise over WSS, HTTPS and MQTT over TLS. `thalovant_healthcheck`
package/dist/index.js CHANGED
@@ -1614,7 +1614,7 @@ export function createServer() {
1614
1614
  });
1615
1615
  registerThalovantTool(server, "thalovant_intent_inventory", {
1616
1616
  title: "Runtime Intent Inventory",
1617
- description: "Discover a hub's registered intents, language examples and fallback skills using its runtime identity. Unknown fallback support remains distinct from a known empty list; answerability is a conservative hint.",
1617
+ description: "Discover a hub's registered intents, language examples and fallback skills using its runtime identity. listed_in names, per requested language, the hub language the sentences were listed in: a hub lists nothing under a language it did not load, so a language with no sentences is listed in its usual form or the closest language the hub loaded. When listed_in differs from the language asked, tell the user which language the sentences are in. Fallback skills come from ovos.skills.list in skill id order; the hub does not say in which order they are tried, so priority is null. Unknown fallback support remains distinct from a known empty list; answerability is a conservative hint.",
1618
1618
  inputSchema: {
1619
1619
  ...runtimeAuthSchema,
1620
1620
  languages: z.array(z.string().trim().min(1).max(100)).min(1).max(20).default(["en-us"]),
@@ -1634,8 +1634,12 @@ export function createServer() {
1634
1634
  return jsonContent(redactSecrets({
1635
1635
  ...inventory.asObject(),
1636
1636
  presentation: inventoryPresentation(inventory),
1637
+ // Keyed by the language asked, rendered from the tag that answered:
1638
+ // a Spanish phone on an English hub gets the English sentences, and
1639
+ // listed_in says so.
1637
1640
  examples: inventory.intents.map(intent => ({ id: intent.id,
1638
- languages: Object.fromEntries(inventory.languages.map(lang => [lang, intent.examples(lang, exampleLimit, { speakable, sentence, slots })])),
1641
+ languages: Object.fromEntries(inventory.languages.map((lang, index) => [lang,
1642
+ intent.examples(inventory.listedIn[index] ?? lang, exampleLimit, { speakable, sentence, slots })])),
1639
1643
  })),
1640
1644
  may_answer: Object.fromEntries(inventory.languages.map(lang => [lang, inventory.mayAnswer(lang)])),
1641
1645
  }));
@@ -1713,11 +1717,11 @@ export function createServer() {
1713
1717
  });
1714
1718
  registerThalovantTool(server, "thalovant_send_action", {
1715
1719
  title: "Send Action",
1716
- description: "Send a structured action payload to a Thalovant hub.",
1720
+ description: "Send one action to a Thalovant hub using a saved identity, the way a button or a shortcut would. The hub routes payload like a spoken request, so the skill that takes it can change things: switch a light, start a timer, play music. Returns once the action is sent, without waiting for the reply; use thalovant_ask to read the answer. THALOVANT_MCP_READONLY leaves this tool out.",
1717
1721
  inputSchema: {
1718
1722
  ...runtimeAuthSchema,
1719
- payload: z.string().min(1).max(20_000),
1720
- title: z.string().min(1).max(512).optional(),
1723
+ payload: z.string().min(1).max(20_000).describe("The request the hub routes, in words, such as \"turn on the kitchen light\"."),
1724
+ title: z.string().min(1).max(512).optional().describe("A label carried with the action in its context (input.title)."),
1721
1725
  lang: z.string().min(1).optional(),
1722
1726
  sessionId: z.string().min(1).optional(),
1723
1727
  requestId: z.string().min(1).optional(),
@@ -1745,12 +1749,12 @@ export function createServer() {
1745
1749
  });
1746
1750
  registerThalovantTool(server, "thalovant_send_code", {
1747
1751
  title: "Send Code",
1748
- description: "Send an exact typed, scanned, barcode, QR, or serial value to a Thalovant hub.",
1752
+ description: "Send one exact value to a Thalovant hub using a saved identity: something typed, scanned, a barcode, a QR code or a serial number. The hub receives it as an utterance whose input is marked exact, and a skill that recognizes the value may act on it. Returns once the value is sent, without waiting for a reply. THALOVANT_MCP_READONLY leaves this tool out.",
1749
1753
  inputSchema: {
1750
1754
  ...runtimeAuthSchema,
1751
- value: z.string().min(1).max(20_000),
1752
- kind: z.string().min(1).max(64).optional(),
1753
- label: z.string().min(1).max(128).optional(),
1755
+ value: z.string().min(1).max(20_000).describe("The value exactly as read; only leading and trailing spaces are trimmed."),
1756
+ kind: z.string().min(1).max(64).optional().describe("What the value is, such as barcode, qr or serial. Defaults to code."),
1757
+ label: z.string().min(1).max(128).optional().describe("A label carried with the value in its context (input.label)."),
1754
1758
  lang: z.string().min(1).optional(),
1755
1759
  sessionId: z.string().min(1).optional(),
1756
1760
  requestId: z.string().min(1).optional(),
@@ -1802,10 +1806,10 @@ export function createServer() {
1802
1806
  });
1803
1807
  registerThalovantTool(server, "thalovant_wait_for_event", {
1804
1808
  title: "Wait For Event",
1805
- description: "Wait for one named hub event using a saved identity.",
1809
+ description: "Wait for one named hub event using a saved identity and return it: the first eventName the hub sends once the connection is made, or a timeout error after timeoutMs. sessionId, requestId and context narrow it to events carrying the same values. Listens only: it opens the connection and sends the hub no message, so it changes nothing.",
1806
1810
  inputSchema: {
1807
1811
  ...runtimeAuthSchema,
1808
- eventName: z.string().min(1).max(256),
1812
+ eventName: z.string().min(1).max(256).describe("The bus message type to wait for, such as speak or recognizer_loop:utterance."),
1809
1813
  timeoutMs: z.number().int().min(1_000).max(MAX_TIMEOUT_MS).default(DEFAULT_TIMEOUT_MS),
1810
1814
  sessionId: z.string().min(1).optional(),
1811
1815
  requestId: z.string().min(1).optional(),
@@ -6,6 +6,12 @@ export function inventoryPresentation(runtime, generatedAt = new Date().toISOStr
6
6
  `Runtime discovery source: ${runtime.source}.`,
7
7
  ...runtime.denied.map(query => `Query refused or unanswered: ${query}.`),
8
8
  ...(!runtime.fallbacksKnown ? ["Fallback skill support is unknown."] : []),
9
+ ...(runtime.fallbacks.length > 0 ? ["Fallback skills are in skill id order; the hub does not say in which order it tries them."] : []),
10
+ ...runtime.languages.flatMap((lang, index) => {
11
+ const listed = runtime.listedIn[index];
12
+ return listed && listed.toLowerCase().replaceAll("_", "-") !== lang.toLowerCase().replaceAll("_", "-")
13
+ ? [`Sentences for ${lang} are listed in ${listed}: the hub has none under ${lang}.`] : [];
14
+ }),
9
15
  ];
10
16
  const inventory = new Inventory("", "", "hub", generatedAt, runtime.skills.map(skill => new Skill(skill.skillId, friendlyTitle(skill.skillId), [], skill.intents.map(intent => new Intent(intent.id, intent.name, intent.skillId, intent.engine, intent.phrases, intent.languages)))), notes);
11
17
  return { ...inventory.asObject(), languages_present: languagesPresent(inventory) };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thalovant/mcp-server",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
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.9.3",
67
+ "@thalovant/sdk": "^0.9.9",
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.6.2",
10
+ "version": "0.6.4",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@thalovant/mcp-server",
15
- "version": "0.6.2",
15
+ "version": "0.6.4",
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.6.2",
22
+ "identifier": "ghcr.io/thalovant/thalovant-mcp:0.6.4",
23
23
  "transport": {
24
24
  "type": "streamable-http",
25
25
  "url": "http://127.0.0.1:3000/mcp"