@sonenta/mcp 0.29.0 → 0.30.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.29.0",
3
+ "version": "0.30.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 = "sonenta-mcp"
3
- version = "0.29.0"
3
+ version = "0.30.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"
@@ -1,3 +1,3 @@
1
1
  """Sonenta MCP server (MIT)."""
2
2
 
3
- __version__ = "0.29.0"
3
+ __version__ = "0.30.0"
@@ -2225,13 +2225,26 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
2225
2225
  "items": {
2226
2226
  "type": "object",
2227
2227
  "properties": {
2228
- "canonical_key_uuid": {"type": "string"},
2228
+ "canonical_key_uuid": {
2229
+ "type": "string",
2230
+ "description": "An EXISTING key kept as the canonical survivor. Mutually exclusive with new_key.",
2231
+ },
2232
+ "new_key": {
2233
+ "type": "object",
2234
+ "description": "Create a BRAND-NEW canonical key instead of keeping an existing one (NK1). Mutually exclusive with canonical_key_uuid. For a new_key cluster, redundant_key_uuids = ALL the group's keys (none survives). After authoring, call apply_new_key_cluster — the backend creates the key + copies translations + trashes the old keys.",
2235
+ "properties": {
2236
+ "name": {"type": "string", "description": "New key name (must not collide with an active key)."},
2237
+ "namespace": {"type": "string", "description": "Namespace slug the new key lives in (must exist)."},
2238
+ },
2239
+ "required": ["name", "namespace"],
2240
+ },
2229
2241
  "redundant_key_uuids": {
2230
2242
  "type": "array",
2231
2243
  "items": {"type": "string"},
2244
+ "description": "Keys folded into the canonical. For a new_key cluster, this is ALL the group's keys.",
2232
2245
  },
2233
2246
  },
2234
- "required": ["canonical_key_uuid", "redundant_key_uuids"],
2247
+ "required": ["redundant_key_uuids"],
2235
2248
  },
2236
2249
  },
2237
2250
  "survivor_outcome": {
@@ -2283,6 +2296,48 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
2283
2296
  "additionalProperties": False,
2284
2297
  },
2285
2298
  ),
2299
+ Tool(
2300
+ name="apply_new_key_cluster",
2301
+ description=(
2302
+ "Consolidate a duplicate group onto a BRAND-NEW canonical key (NK1) — "
2303
+ "the BACKEND does the atomic data transaction so no translation is "
2304
+ "lost: it creates the new key (type/description/context inherited from "
2305
+ "the group's most-complete key), COPIES every language value from that "
2306
+ "most-complete key, and TRASHES all the old keys. Address it by "
2307
+ "source_value + new_key {name, namespace} (the namespace must exist and "
2308
+ "the name must be free). Returns {new_key_uuid, name, namespace, "
2309
+ "copied_from_key_uuid, copied_languages, trashed_key_uuids}. YOUR job "
2310
+ "after this is the CODE only: repoint every t() usage of the trashed "
2311
+ "keys onto new_key_uuid, then record it via set_source_duplicate_merge_"
2312
+ "plan / set_duplicate_status files_modified. You do NOT create_key or "
2313
+ "delete_keys_bulk yourself — the backend owns that. Author the "
2314
+ "new_key cluster in the merge plan first (on the dev's acceptance)."
2315
+ ),
2316
+ inputSchema={
2317
+ "type": "object",
2318
+ "properties": {
2319
+ "project_uuid": {
2320
+ "type": "string",
2321
+ "description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
2322
+ },
2323
+ "source_value": {
2324
+ "type": "string",
2325
+ "description": "The shared source-language value identifying the duplicate group.",
2326
+ },
2327
+ "new_key": {
2328
+ "type": "object",
2329
+ "description": "The new canonical key to create and consolidate the group onto.",
2330
+ "properties": {
2331
+ "name": {"type": "string", "description": "New key name (must not collide with an active key)."},
2332
+ "namespace": {"type": "string", "description": "Namespace slug the new key lives in (must exist)."},
2333
+ },
2334
+ "required": ["name", "namespace"],
2335
+ },
2336
+ },
2337
+ "required": ["source_value", "new_key"],
2338
+ "additionalProperties": False,
2339
+ },
2340
+ ),
2286
2341
  ]
2287
2342
 
2288
2343
  @server.call_tool()
@@ -3038,6 +3093,18 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
3038
3093
  json=body,
3039
3094
  )
3040
3095
  return _text(data)
3096
+ if name == "apply_new_key_cluster":
3097
+ project_uuid = _project_uuid(args, client)
3098
+ new_key = args.get("new_key")
3099
+ if not args.get("source_value") or not isinstance(new_key, dict):
3100
+ return _err("source_value and new_key {name, namespace} are required")
3101
+ if not new_key.get("name") or not new_key.get("namespace"):
3102
+ return _err("new_key requires name and namespace")
3103
+ data = await client.post(
3104
+ f"/v1/mcp/projects/{project_uuid}/source-duplicates/apply-cluster",
3105
+ json={"source_value": args["source_value"], "new_key": new_key},
3106
+ )
3107
+ return _text(data)
3041
3108
  return _err(f"unknown tool: {name}")
3042
3109
  except VerbumiaApiError as exc:
3043
3110
  return _err(f"API {exc.status}: {exc.body}")