@sonenta/mcp 0.32.0 → 0.34.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.
|
|
3
|
+
"version": "0.34.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",
|
|
Binary file
|
package/python/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sonenta-mcp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.34.0"
|
|
4
4
|
description = "Model Context Protocol server for Sonenta — 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"
|
|
@@ -1383,6 +1383,44 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1383
1383
|
"additionalProperties": False,
|
|
1384
1384
|
},
|
|
1385
1385
|
),
|
|
1386
|
+
Tool(
|
|
1387
|
+
name="list_untranslated_surfaces",
|
|
1388
|
+
description=(
|
|
1389
|
+
"The TRANSLATION MAP: every a11y surface cell that has an APPROVED "
|
|
1390
|
+
"SOURCE value but is missing the target-language translation, WITH that "
|
|
1391
|
+
"source value to translate from. Returns {cells: [{key_uuid, key_name, "
|
|
1392
|
+
"namespace_slug, key_type, surface, language_code, source_value}]}. Use "
|
|
1393
|
+
"this to fill a11y surfaces (aria_label / alt_text / screen_reader / "
|
|
1394
|
+
"plain_language) by FAITHFULLY translating each source_value into the "
|
|
1395
|
+
"missing language — do NOT invent new content — then write each with "
|
|
1396
|
+
"set_a11y_variant(key_uuid, language_code, surface, <translation>). "
|
|
1397
|
+
"Optionally filter by surface and/or language_code. (Local-first: you "
|
|
1398
|
+
"translate; 0 AI credits. The billed server path is translate_a11y_variants.)"
|
|
1399
|
+
),
|
|
1400
|
+
inputSchema={
|
|
1401
|
+
"type": "object",
|
|
1402
|
+
"properties": {
|
|
1403
|
+
"project_uuid": {
|
|
1404
|
+
"type": "string",
|
|
1405
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1406
|
+
},
|
|
1407
|
+
"surface": {
|
|
1408
|
+
"type": "string",
|
|
1409
|
+
"enum": list(A11Y_SURFACES),
|
|
1410
|
+
"description": "Optional: only this a11y surface.",
|
|
1411
|
+
},
|
|
1412
|
+
"language_code": {
|
|
1413
|
+
"type": "string",
|
|
1414
|
+
"description": "Optional: only this target language (BCP-47).",
|
|
1415
|
+
},
|
|
1416
|
+
"version_id": {
|
|
1417
|
+
"type": "string",
|
|
1418
|
+
"description": "Optional version UUID; defaults to the production version.",
|
|
1419
|
+
},
|
|
1420
|
+
},
|
|
1421
|
+
"additionalProperties": False,
|
|
1422
|
+
},
|
|
1423
|
+
),
|
|
1386
1424
|
Tool(
|
|
1387
1425
|
name="set_a11y_variant",
|
|
1388
1426
|
description=(
|
|
@@ -2350,6 +2388,79 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
2350
2388
|
"additionalProperties": False,
|
|
2351
2389
|
},
|
|
2352
2390
|
),
|
|
2391
|
+
Tool(
|
|
2392
|
+
name="set_duplicate_status_bulk",
|
|
2393
|
+
description=(
|
|
2394
|
+
"Set ONE triage status across MANY duplicate groups at once (coarse "
|
|
2395
|
+
"triage). Body {source_values[], status, note?}. Intentionally NO "
|
|
2396
|
+
"files_modified (a single status over many groups). Use it to bulk-mark "
|
|
2397
|
+
"a batch as allowed (sanctioned) or to_fix; use the per-group "
|
|
2398
|
+
"set_duplicate_status for anything that records code changes."
|
|
2399
|
+
),
|
|
2400
|
+
inputSchema={
|
|
2401
|
+
"type": "object",
|
|
2402
|
+
"properties": {
|
|
2403
|
+
"project_uuid": {
|
|
2404
|
+
"type": "string",
|
|
2405
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
2406
|
+
},
|
|
2407
|
+
"source_values": {
|
|
2408
|
+
"type": "array",
|
|
2409
|
+
"items": {"type": "string"},
|
|
2410
|
+
"description": "The shared source values identifying the duplicate groups.",
|
|
2411
|
+
},
|
|
2412
|
+
"status": {
|
|
2413
|
+
"type": "string",
|
|
2414
|
+
"enum": ["neutral", "allowed", "to_fix", "resolved"],
|
|
2415
|
+
"description": "New triage status applied to every listed group (neutral clears the override).",
|
|
2416
|
+
},
|
|
2417
|
+
"note": {"type": "string", "description": "Optional human/agent note (same on all)."},
|
|
2418
|
+
},
|
|
2419
|
+
"required": ["source_values", "status"],
|
|
2420
|
+
"additionalProperties": False,
|
|
2421
|
+
},
|
|
2422
|
+
),
|
|
2423
|
+
Tool(
|
|
2424
|
+
name="propose_duplicate_merge_plan",
|
|
2425
|
+
description=(
|
|
2426
|
+
"OPT-IN, METERED second opinion: a server-side LLM reads a duplicate "
|
|
2427
|
+
"group (keys + translations + glossary + context) and PROPOSES a plan — "
|
|
2428
|
+
"recommendation merge | split | keep_separate + a well-formed merge_plan "
|
|
2429
|
+
"(null when keep_separate) + a one-line reason. PROPOSE-ONLY: it does NOT "
|
|
2430
|
+
"change status; apply via set_source_duplicate_merge_plan + "
|
|
2431
|
+
"apply_new_key_cluster / mark_cluster_applied / set_duplicate_status. "
|
|
2432
|
+
"You are already an LLM and decide locally at 0 credit — use this only "
|
|
2433
|
+
"as a tie-breaker or when the dev wants the AI's take. Costs 1 credit per "
|
|
2434
|
+
"generation UNLESS a valid cached proposal exists (cached=true → free); "
|
|
2435
|
+
"regenerate=true forces a fresh one. 402 when credits are insufficient. "
|
|
2436
|
+
"The persisted proposal also surfaces on list_source_duplicates "
|
|
2437
|
+
"(ai_proposed_plan / ai_diagnosis, free to read)."
|
|
2438
|
+
),
|
|
2439
|
+
inputSchema={
|
|
2440
|
+
"type": "object",
|
|
2441
|
+
"properties": {
|
|
2442
|
+
"project_uuid": {
|
|
2443
|
+
"type": "string",
|
|
2444
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
2445
|
+
},
|
|
2446
|
+
"source_value": {
|
|
2447
|
+
"type": "string",
|
|
2448
|
+
"description": "The shared source value identifying the duplicate group.",
|
|
2449
|
+
},
|
|
2450
|
+
"namespace_slug": {
|
|
2451
|
+
"type": "string",
|
|
2452
|
+
"description": "Required only if the value spans namespaces (groups are intra-namespace).",
|
|
2453
|
+
},
|
|
2454
|
+
"regenerate": {
|
|
2455
|
+
"type": "boolean",
|
|
2456
|
+
"description": "Force a fresh diagnosis (1 credit) even if a valid cached one exists. Default false.",
|
|
2457
|
+
},
|
|
2458
|
+
"version_id": {"type": "string", "description": "Optional version UUID; defaults to the live version."},
|
|
2459
|
+
},
|
|
2460
|
+
"required": ["source_value"],
|
|
2461
|
+
"additionalProperties": False,
|
|
2462
|
+
},
|
|
2463
|
+
),
|
|
2353
2464
|
]
|
|
2354
2465
|
|
|
2355
2466
|
@server.call_tool()
|
|
@@ -2791,6 +2902,33 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
2791
2902
|
"gaps": gaps,
|
|
2792
2903
|
}
|
|
2793
2904
|
)
|
|
2905
|
+
if name == "list_untranslated_surfaces":
|
|
2906
|
+
project_uuid = _project_uuid(args, client)
|
|
2907
|
+
params: dict[str, Any] = {"include_untranslated": True}
|
|
2908
|
+
if args.get("version_id"):
|
|
2909
|
+
params["version_id"] = args["version_id"]
|
|
2910
|
+
report = await client.get(
|
|
2911
|
+
f"/v1/mcp/projects/{project_uuid}/a11y-report", params=params
|
|
2912
|
+
)
|
|
2913
|
+
items = report.get("items", []) if isinstance(report, dict) else []
|
|
2914
|
+
surf_f = args.get("surface")
|
|
2915
|
+
lang_f = args.get("language_code")
|
|
2916
|
+
cells = [
|
|
2917
|
+
{
|
|
2918
|
+
"key_uuid": it.get("key_uuid"),
|
|
2919
|
+
"key_name": it.get("key_name"),
|
|
2920
|
+
"namespace_slug": it.get("namespace_slug"),
|
|
2921
|
+
"key_type": it.get("key_type"),
|
|
2922
|
+
"surface": it.get("surface"),
|
|
2923
|
+
"language_code": it.get("locale"),
|
|
2924
|
+
"source_value": it.get("source_value"),
|
|
2925
|
+
}
|
|
2926
|
+
for it in items
|
|
2927
|
+
if it.get("gap") == "a11y_untranslated"
|
|
2928
|
+
and (surf_f is None or it.get("surface") == surf_f)
|
|
2929
|
+
and (lang_f is None or it.get("locale") == lang_f)
|
|
2930
|
+
]
|
|
2931
|
+
return _text({"project_uuid": project_uuid, "count": len(cells), "cells": cells})
|
|
2794
2932
|
if name in ("set_a11y_variant", "delete_a11y_variant"):
|
|
2795
2933
|
project_uuid = _project_uuid(args, client)
|
|
2796
2934
|
key_uuid = args.get("key_uuid")
|
|
@@ -3122,6 +3260,35 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
3122
3260
|
json=body,
|
|
3123
3261
|
)
|
|
3124
3262
|
return _text(data)
|
|
3263
|
+
if name == "set_duplicate_status_bulk":
|
|
3264
|
+
project_uuid = _project_uuid(args, client)
|
|
3265
|
+
source_values = args.get("source_values")
|
|
3266
|
+
if not isinstance(source_values, list) or not source_values:
|
|
3267
|
+
return _err("source_values must be a non-empty list")
|
|
3268
|
+
if not args.get("status"):
|
|
3269
|
+
return _err("status is required")
|
|
3270
|
+
body = {"source_values": source_values, "status": args["status"]}
|
|
3271
|
+
if args.get("note") is not None:
|
|
3272
|
+
body["note"] = args["note"]
|
|
3273
|
+
data = await client.put(
|
|
3274
|
+
f"/v1/mcp/projects/{project_uuid}/source-duplicates/status/bulk",
|
|
3275
|
+
json=body,
|
|
3276
|
+
)
|
|
3277
|
+
return _text(data)
|
|
3278
|
+
if name == "propose_duplicate_merge_plan":
|
|
3279
|
+
project_uuid = _project_uuid(args, client)
|
|
3280
|
+
if not args.get("source_value"):
|
|
3281
|
+
return _err("source_value is required")
|
|
3282
|
+
body = {"source_value": args["source_value"]}
|
|
3283
|
+
if args.get("namespace_slug"):
|
|
3284
|
+
body["namespace_slug"] = args["namespace_slug"]
|
|
3285
|
+
if args.get("regenerate"):
|
|
3286
|
+
body["regenerate"] = True
|
|
3287
|
+
url = f"/v1/mcp/projects/{project_uuid}/source-duplicates/propose-plan"
|
|
3288
|
+
if args.get("version_id"):
|
|
3289
|
+
url += f"?version_id={args['version_id']}"
|
|
3290
|
+
data = await client.post(url, json=body)
|
|
3291
|
+
return _text(data)
|
|
3125
3292
|
return _err(f"unknown tool: {name}")
|
|
3126
3293
|
except VerbumiaApiError as exc:
|
|
3127
3294
|
return _err(f"API {exc.status}: {exc.body}")
|
|
Binary file
|