@sonenta/mcp 0.19.0 → 0.20.0

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.19.0",
3
+ "version": "0.20.0",
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.19.0"
3
+ version = "0.20.0"
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.19.0"
3
+ __version__ = "0.20.0"
@@ -42,6 +42,9 @@ Tools (V1 MCP surface; glossary_* added 2026-05-24; reports + trash added 2026-0
42
42
  - get_variants — full variant matrix for one key (every active surface x language; #928)
43
43
  - set_variant — upsert one variant override for ANY active surface (device or a11y)
44
44
  - delete_variant — delete one variant override for any surface (cell re-inherits base)
45
+ - list_cognitive_candidates — text keys eligible for plain-language (cognitive) scoring
46
+ - set_cognitive_score — record a key's cognitive score (0-100) + plain-language suggestion (0 credit)
47
+ - analyze_cognitive — server AI cognitive analysis (billed; opt-in fallback to local scoring)
45
48
 
46
49
  A11y (accessibility) tools (a11y V1) extend the variant engine with four SEMANTIC
47
50
  surfaces — aria_label / alt_text / screen_reader / plain_language — orthogonal to
@@ -1553,6 +1556,116 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
1553
1556
  "additionalProperties": False,
1554
1557
  },
1555
1558
  ),
1559
+ Tool(
1560
+ name="list_cognitive_candidates",
1561
+ description=(
1562
+ "List keys that are CANDIDATES for plain-language (cognitive) scoring: "
1563
+ "text-relevant keys (a type that offers plain_language — text / heading "
1564
+ "/ label / caption) whose source value clears a small word floor. This "
1565
+ "is a scope filter, NOT a difficulty judgement — score them to find the "
1566
+ "hard ones. Pass only_unanalyzed=true to skip keys already scored. Each "
1567
+ "item carries key_uuid + source_value so you can score it locally with "
1568
+ "set_cognitive_score."
1569
+ ),
1570
+ inputSchema={
1571
+ "type": "object",
1572
+ "properties": {
1573
+ "project_uuid": {
1574
+ "type": "string",
1575
+ "description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
1576
+ },
1577
+ "only_unanalyzed": {
1578
+ "type": "boolean",
1579
+ "description": "Only return keys with no cognitive_score yet (not analyzed).",
1580
+ },
1581
+ "namespace_uuid": {
1582
+ "type": "string",
1583
+ "description": "Restrict to one namespace by its UUID.",
1584
+ },
1585
+ "limit": {"type": "integer", "minimum": 1, "maximum": 500, "default": 100},
1586
+ "offset": {"type": "integer", "minimum": 0, "default": 0},
1587
+ },
1588
+ "additionalProperties": False,
1589
+ },
1590
+ ),
1591
+ Tool(
1592
+ name="set_cognitive_score",
1593
+ description=(
1594
+ "Record a key's cognitive-difficulty SCORE (0-100, higher = harder to "
1595
+ "read) and an optional plain-language SUGGESTION — the local-first, "
1596
+ "**0-credit** path. Use this after scoring a candidate YOURSELF: you "
1597
+ "(the agent) judge the difficulty and write a clearer rewrite, then "
1598
+ "persist both here (marked by_bot). A key only enters the review queue "
1599
+ "(reading_level_high) once its score is >= the project threshold "
1600
+ "(default 60). The suggestion is a draft for a human to apply/approve."
1601
+ ),
1602
+ inputSchema={
1603
+ "type": "object",
1604
+ "properties": {
1605
+ "project_uuid": {
1606
+ "type": "string",
1607
+ "description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
1608
+ },
1609
+ "key_uuid": {
1610
+ "type": "string",
1611
+ "description": "Key UUID (from list_cognitive_candidates / list_keys).",
1612
+ },
1613
+ "score": {
1614
+ "type": "number",
1615
+ "minimum": 0,
1616
+ "maximum": 100,
1617
+ "description": "Cognitive difficulty score, 0 (easy) to 100 (hard).",
1618
+ },
1619
+ "suggestion": {
1620
+ "type": "string",
1621
+ "description": "Optional plain-language rewrite of the source value (draft).",
1622
+ },
1623
+ },
1624
+ "required": ["key_uuid", "score"],
1625
+ "additionalProperties": False,
1626
+ },
1627
+ ),
1628
+ Tool(
1629
+ name="analyze_cognitive",
1630
+ description=(
1631
+ "Server-side AI cognitive analysis — the BILLED, opt-in FALLBACK to "
1632
+ "local scoring. Rates the cognitive difficulty of candidate keys and "
1633
+ "suggests plain-language rewrites server-side. Prefer scoring locally "
1634
+ "with set_cognitive_score (0 credits); use this only for very large "
1635
+ "volumes or on explicit request. Bills credits (standard=1, premium=4 "
1636
+ "per key; 402 if insufficient, 503 if AI unavailable). Returns "
1637
+ "{requested, analyzed, credits_charged, credits_refunded}."
1638
+ ),
1639
+ inputSchema={
1640
+ "type": "object",
1641
+ "properties": {
1642
+ "project_uuid": {
1643
+ "type": "string",
1644
+ "description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
1645
+ },
1646
+ "tier": {
1647
+ "type": "string",
1648
+ "enum": ["standard", "premium"],
1649
+ "default": "standard",
1650
+ "description": "Quality tier (credit_cost per key: standard=1, premium=4).",
1651
+ },
1652
+ "key_uuids": {
1653
+ "type": "array",
1654
+ "items": {"type": "string"},
1655
+ "description": "Restrict to these keys (from list_cognitive_candidates). Omit to scope the whole project (or a namespace).",
1656
+ },
1657
+ "namespace_uuid": {
1658
+ "type": "string",
1659
+ "description": "Restrict to one namespace by its UUID.",
1660
+ },
1661
+ "idempotency_key": {
1662
+ "type": "string",
1663
+ "description": "Stable token making this billable call safe to retry: reuse the SAME value to avoid a double-charge. A random one is generated if omitted.",
1664
+ },
1665
+ },
1666
+ "additionalProperties": False,
1667
+ },
1668
+ ),
1556
1669
  ]
1557
1670
 
1558
1671
  @server.call_tool()
@@ -2089,6 +2202,50 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
2089
2202
  return _err("value is required")
2090
2203
  data = await client.put(path, json={"value": args["value"]})
2091
2204
  return _text(data)
2205
+ if name == "list_cognitive_candidates":
2206
+ project_uuid = _project_uuid(args, client)
2207
+ params = {
2208
+ k: v
2209
+ for k, v in {
2210
+ "only_unanalyzed": ("true" if args.get("only_unanalyzed") else None),
2211
+ "namespace_uuid": args.get("namespace_uuid"),
2212
+ "limit": args.get("limit", 100),
2213
+ "offset": args.get("offset", 0),
2214
+ }.items()
2215
+ if v is not None
2216
+ }
2217
+ data = await client.get(
2218
+ f"/v1/mcp/projects/{project_uuid}/cognitive/candidates", params=params
2219
+ )
2220
+ return _text(data)
2221
+ if name == "set_cognitive_score":
2222
+ project_uuid = _project_uuid(args, client)
2223
+ key_uuid = args.get("key_uuid")
2224
+ if not key_uuid:
2225
+ return _err("key_uuid is required")
2226
+ if "score" not in args:
2227
+ return _err("score is required")
2228
+ body: dict[str, Any] = {"score": args["score"]}
2229
+ if "suggestion" in args:
2230
+ body["suggestion"] = args["suggestion"]
2231
+ data = await client.put(
2232
+ f"/v1/mcp/projects/{project_uuid}/keys/{key_uuid}/cognitive-score",
2233
+ json=body,
2234
+ )
2235
+ return _text(data)
2236
+ if name == "analyze_cognitive":
2237
+ project_uuid = _project_uuid(args, client)
2238
+ body: dict[str, Any] = {
2239
+ "tier": args.get("tier", "standard"),
2240
+ "idempotency_key": args.get("idempotency_key") or str(uuid.uuid4()),
2241
+ }
2242
+ for opt in ("key_uuids", "namespace_uuid"):
2243
+ if args.get(opt):
2244
+ body[opt] = args[opt]
2245
+ data = await client.post(
2246
+ f"/v1/mcp/projects/{project_uuid}/cognitive/analyze", json=body
2247
+ )
2248
+ return _text(data)
2092
2249
  return _err(f"unknown tool: {name}")
2093
2250
  except VerbumiaApiError as exc:
2094
2251
  return _err(f"API {exc.status}: {exc.body}")