@sonenta/mcp 0.20.0 → 0.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonenta/mcp",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "MCP server for Sonenta translation management \u2014 wires Claude Desktop and other MCP clients into your project's keys, missing-key feed, and translation drafts.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sonenta.com",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "verbumia-mcp"
3
- version = "0.20.0"
3
+ version = "0.20.1"
4
4
  description = "Model Context Protocol server for Verbumia — list projects, missing keys, propose translations from Claude Desktop and other MCP clients."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12,<3.14"
@@ -1,3 +1,3 @@
1
1
  """Verbumia MCP server (MIT)."""
2
2
 
3
- __version__ = "0.20.0"
3
+ __version__ = "0.20.1"
@@ -111,6 +111,38 @@ KEY_TYPES = (
111
111
  "meta_description",
112
112
  )
113
113
 
114
+ # Human/LLM guidance for choosing a key's `type`. Embedded in the create_key /
115
+ # update_key tool descriptions so an agent types keys correctly instead of
116
+ # leaving everything as the "text" default. The type DRIVES which a11y
117
+ # treatments + cognitive (plain-language) scoring a key gets, so it is the input
118
+ # that makes the a11y tooling work.
119
+ KEY_TYPE_GUIDANCE = (
120
+ "Set this at CREATION — do NOT leave everything as 'text'. The type drives "
121
+ "which a11y treatments the key offers (and whether it gets cognitive / "
122
+ "plain-language scoring), so correct typing is what lets the a11y tooling "
123
+ "work. Pick by the string's UI role:\n"
124
+ "- text — body / paragraph copy (offers plain_language, screen_reader)\n"
125
+ "- heading — a title or section heading (plain_language, screen_reader)\n"
126
+ "- label — a standalone short label (plain_language, screen_reader)\n"
127
+ "- caption — image caption / helper / secondary text (plain_language, screen_reader)\n"
128
+ "- button — a button's label (offers aria_label, screen_reader)\n"
129
+ "- link — a hyperlink / anchor label (aria_label, screen_reader)\n"
130
+ "- menu_item — a navigation / menu entry (aria_label, screen_reader)\n"
131
+ "- input_placeholder — a form input placeholder (aria_label)\n"
132
+ "- input_label — a form field's label (aria_label)\n"
133
+ "- tooltip — tooltip text (screen_reader)\n"
134
+ "- badge — a small status / count badge (screen_reader)\n"
135
+ "- error_message — a validation / error message (screen_reader)\n"
136
+ "- toast — a transient toast / snackbar notification (screen_reader)\n"
137
+ "- image — an image; THIS key's value IS the alt text\n"
138
+ "- icon — an icon; THIS key's value IS the accessible name (aria)\n"
139
+ "- meta_title — an SEO page <title> (no a11y treatment)\n"
140
+ "- meta_description — an SEO meta description (no a11y treatment)\n"
141
+ "Default 'text'. Reclassify existing keys later with update_key / "
142
+ "update_keys_bulk (type-only, no source_value needed). The current type is "
143
+ "returned by list_keys so you can audit and fix mis-typed keys."
144
+ )
145
+
114
146
 
115
147
  def _project_uuid(arguments: dict | None, client: VerbumiaClient) -> str:
116
148
  """Resolve project_uuid from per-call args or the configured env scope.
@@ -208,13 +240,16 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
208
240
  Tool(
209
241
  name="list_keys",
210
242
  description=(
211
- "List translation keys with their source-language value and "
212
- "per-language translations. Designed for bulk translation: "
213
- "Claude can scan the result and propose translations for "
214
- "every key that's still missing in a target language. "
215
- "Filters: namespace slug, status_filter (only keys with at "
216
- "least one translation in that status), language_code "
217
- "(restrict translations[] to a single language)."
243
+ "List translation keys with their source-language value, their "
244
+ "semantic `type`, and per-language translations. Designed for "
245
+ "bulk translation: Claude can scan the result and propose "
246
+ "translations for every key that's still missing in a target "
247
+ "language. Each key's `type` lets you AUDIT typing and reclassify "
248
+ "mis-typed keys (e.g. a button left as 'text') via update_key / "
249
+ "update_keys_bulk so the a11y treatments apply. Filters: "
250
+ "namespace slug, status_filter (only keys with at least one "
251
+ "translation in that status), language_code (restrict "
252
+ "translations[] to a single language)."
218
253
  ),
219
254
  inputSchema={
220
255
  "type": "object",
@@ -398,8 +433,11 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
398
433
  "Returns created=false if the key already exists. When "
399
434
  "source_value is supplied AND the project has a source "
400
435
  "language, the value is also written as a source-language "
401
- "translation in the same call. Pass `type` (default 'text') to "
402
- "set the key's semantic type, which decides its a11y treatments."
436
+ "translation in the same call. ALWAYS set `type` to the key's "
437
+ "semantic role (button / link / heading / image / icon / "
438
+ "input_label / …) — do NOT leave it as the default 'text'; the "
439
+ "type drives the key's a11y treatments. See the `type` field for "
440
+ "the full 17-type taxonomy and when to use each."
403
441
  ),
404
442
  inputSchema={
405
443
  "type": "object",
@@ -427,7 +465,7 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
427
465
  "type": "string",
428
466
  "enum": list(KEY_TYPES),
429
467
  "default": "text",
430
- "description": "Semantic key type (default 'text'). Decides which a11y treatments apply — e.g. 'image' (base value is the alt text), 'icon' (base is the accessible name), 'button'/'link' (offer aria_label), 'heading'/'label' (plain_language/screen_reader).",
468
+ "description": "Semantic key type. " + KEY_TYPE_GUIDANCE,
431
469
  },
432
470
  },
433
471
  "required": ["namespace", "name"],
@@ -441,7 +479,11 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
441
479
  "doesn't exist (write-through). Status is set to draft (or "
442
480
  "translated if explicitly requested); reviewer/approved "
443
481
  "promotion requires a human reviewer. Identifies the key "
444
- "by namespace + key name + language_code — no uuids needed."
482
+ "by namespace + key name + language_code — no uuids needed. "
483
+ "The response carries `variable_warnings: {missing, extra}` "
484
+ "(null when interpolation variables match the source) — a "
485
+ "non-blocking flag for placeholder drift; check it and fix the "
486
+ "value if set."
445
487
  ),
446
488
  inputSchema={
447
489
  "type": "object",
@@ -499,7 +541,7 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
499
541
  "description": {"type": "string"},
500
542
  "plurals": {"type": "boolean", "default": False},
501
543
  "max_length": {"type": "integer", "minimum": 1},
502
- "type": {"type": "string", "enum": list(KEY_TYPES), "default": "text", "description": "Semantic key type (default 'text'); see create_key."},
544
+ "type": {"type": "string", "enum": list(KEY_TYPES), "default": "text", "description": "Semantic key type — set per item by the string's UI role (button / image / heading / …); do NOT default everything to 'text'. See create_key's `type` field for the full taxonomy and why it matters for a11y."},
503
545
  },
504
546
  "required": ["namespace", "name"],
505
547
  "additionalProperties": False,
@@ -615,7 +657,10 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
615
657
  "(same namespace+key+language twice) error per item; status is "
616
658
  "draft (or translated if requested), reviewed/approved require "
617
659
  "a human reviewer. NO server-side translation — values come "
618
- "from you. Use this instead of N propose_translation calls."
660
+ "from you. Use this instead of N propose_translation calls. "
661
+ "Each result carries `variable_warnings: {missing, extra}` "
662
+ "(null when interpolation variables match the source) flagging "
663
+ "placeholder drift, non-blocking."
619
664
  ),
620
665
  inputSchema={
621
666
  "type": "object",
@@ -685,7 +730,10 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
685
730
  description=(
686
731
  "Lint a JSON i18next blob against the project: checks for "
687
732
  "missing keys, extra keys, and placeholder parity ({var}, "
688
- "{{var}}, %{var}) vs the source language."
733
+ "{{var}}, %{var}) vs the source language. Each "
734
+ "`placeholder_mismatches[]` item carries `expected` / `got` plus "
735
+ "`missing` (source variables absent from the translation) and "
736
+ "`extra` (variables in the translation unknown to the source)."
689
737
  ),
690
738
  inputSchema={
691
739
  "type": "object",