@sonenta/mcp 0.17.0 → 0.19.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.19.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 = "verbumia-mcp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.19.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"
|
|
@@ -9,9 +9,9 @@ Tools (V1 MCP surface; glossary_* added 2026-05-24; reports + trash added 2026-0
|
|
|
9
9
|
- missing_keys_stats — count-only summary of missing events (drives pagination)
|
|
10
10
|
- acknowledge_missing_keys— batch-resolve missing events (cleanup the dashboard)
|
|
11
11
|
- create_namespace — idempotent namespace creation (slug validated, slug_invalid on bad input)
|
|
12
|
-
- create_key — idempotent key creation
|
|
12
|
+
- create_key — idempotent key creation; optionally seeds source value + sets semantic `type`
|
|
13
13
|
- create_keys_bulk — batch create_key (up to 500/call, per-item results)
|
|
14
|
-
- update_key — edit a key's SOURCE value in place (PATCH); demotes reviewed/approved targets to needs-review
|
|
14
|
+
- update_key — edit a key's SOURCE value / `type` / description in place (PATCH); demotes reviewed/approved targets to needs-review
|
|
15
15
|
- update_keys_bulk — batch update_key (up to 500/call, per-item results)
|
|
16
16
|
- propose_translation — submit a translation value (auto-creates the key if missing)
|
|
17
17
|
- propose_translations_bulk— batch propose_translation (up to 500/call, per-item results)
|
|
@@ -35,15 +35,24 @@ Tools (V1 MCP surface; glossary_* added 2026-05-24; reports + trash added 2026-0
|
|
|
35
35
|
- list_a11y_gaps — actionable a11y gaps (filtered items[] of a11y_report)
|
|
36
36
|
- set_a11y_variant — upsert one a11y variant override (key, language, surface)
|
|
37
37
|
- delete_a11y_variant — delete one a11y variant override (cell re-inherits base value)
|
|
38
|
+
- list_surfaces — list the project's configurable surfaces (device + a11y; #928)
|
|
39
|
+
- create_surface — create a custom DEVICE surface (a11y is a fixed set)
|
|
40
|
+
- update_surface — rename / toggle-active a surface (deactivate is soft)
|
|
41
|
+
- delete_surface — delete a custom surface (builtins: deactivate only)
|
|
42
|
+
- get_variants — full variant matrix for one key (every active surface x language; #928)
|
|
43
|
+
- set_variant — upsert one variant override for ANY active surface (device or a11y)
|
|
44
|
+
- delete_variant — delete one variant override for any surface (cell re-inherits base)
|
|
38
45
|
|
|
39
46
|
A11y (accessibility) tools (a11y V1) extend the variant engine with four SEMANTIC
|
|
40
47
|
surfaces — aria_label / alt_text / screen_reader / plain_language — orthogonal to
|
|
41
|
-
the visible text. The report hits `/v1/mcp/projects/{id}/a11y-report
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
the visible text. The report hits `/v1/mcp/projects/{id}/a11y-report`. Variant
|
|
49
|
+
writes (set_variant / delete_variant, and the a11y-named set_a11y_variant /
|
|
50
|
+
delete_a11y_variant) use the mcp:* code-addressed GENERIC route
|
|
51
|
+
`/v1/mcp/projects/{id}/keys/{key_uuid}/variants/{language_code}/{surface}`, which
|
|
52
|
+
accepts any surface ACTIVE for the project (device or a11y; #928) — locale
|
|
53
|
+
reinjected straight from a report/matrix item. estimate/generate/translate are
|
|
54
|
+
billed in credits. All are thin HTTP wrappers surfaced verbatim. Every tool is
|
|
55
|
+
reachable with the same `mcp:*` key as the rest of the MCP surface.
|
|
47
56
|
|
|
48
57
|
list_keys and list_untranslated_keys accept `include_glossary_hints=true` to
|
|
49
58
|
attach backend-matched `glossary_hints[]` per key. All glossary matching is
|
|
@@ -75,6 +84,30 @@ from .client import VerbumiaApiError, VerbumiaClient
|
|
|
75
84
|
# visible text. `surfaceKinds` in the variants matrix tags each as a11y.
|
|
76
85
|
A11Y_SURFACES = ("aria_label", "alt_text", "screen_reader", "plain_language")
|
|
77
86
|
|
|
87
|
+
# Key semantic types (a11y key.type pivot). Default "text". The type decides
|
|
88
|
+
# which a11y treatments a key offers (e.g. an `image` key's base value IS its
|
|
89
|
+
# alt text; a `button` offers aria_label) — the engine gates variant/a11y tools
|
|
90
|
+
# by type server-side, so listing these here is just for create/update_key.
|
|
91
|
+
KEY_TYPES = (
|
|
92
|
+
"text",
|
|
93
|
+
"heading",
|
|
94
|
+
"label",
|
|
95
|
+
"caption",
|
|
96
|
+
"badge",
|
|
97
|
+
"tooltip",
|
|
98
|
+
"error_message",
|
|
99
|
+
"toast",
|
|
100
|
+
"button",
|
|
101
|
+
"link",
|
|
102
|
+
"menu_item",
|
|
103
|
+
"input_placeholder",
|
|
104
|
+
"input_label",
|
|
105
|
+
"image",
|
|
106
|
+
"icon",
|
|
107
|
+
"meta_title",
|
|
108
|
+
"meta_description",
|
|
109
|
+
)
|
|
110
|
+
|
|
78
111
|
|
|
79
112
|
def _project_uuid(arguments: dict | None, client: VerbumiaClient) -> str:
|
|
80
113
|
"""Resolve project_uuid from per-call args or the configured env scope.
|
|
@@ -362,7 +395,8 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
362
395
|
"Returns created=false if the key already exists. When "
|
|
363
396
|
"source_value is supplied AND the project has a source "
|
|
364
397
|
"language, the value is also written as a source-language "
|
|
365
|
-
"translation in the same call."
|
|
398
|
+
"translation in the same call. Pass `type` (default 'text') to "
|
|
399
|
+
"set the key's semantic type, which decides its a11y treatments."
|
|
366
400
|
),
|
|
367
401
|
inputSchema={
|
|
368
402
|
"type": "object",
|
|
@@ -386,6 +420,12 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
386
420
|
"description": {"type": "string"},
|
|
387
421
|
"plurals": {"type": "boolean", "default": False},
|
|
388
422
|
"max_length": {"type": "integer", "minimum": 1},
|
|
423
|
+
"type": {
|
|
424
|
+
"type": "string",
|
|
425
|
+
"enum": list(KEY_TYPES),
|
|
426
|
+
"default": "text",
|
|
427
|
+
"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).",
|
|
428
|
+
},
|
|
389
429
|
},
|
|
390
430
|
"required": ["namespace", "name"],
|
|
391
431
|
"additionalProperties": False,
|
|
@@ -456,6 +496,7 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
456
496
|
"description": {"type": "string"},
|
|
457
497
|
"plurals": {"type": "boolean", "default": False},
|
|
458
498
|
"max_length": {"type": "integer", "minimum": 1},
|
|
499
|
+
"type": {"type": "string", "enum": list(KEY_TYPES), "default": "text", "description": "Semantic key type (default 'text'); see create_key."},
|
|
459
500
|
},
|
|
460
501
|
"required": ["namespace", "name"],
|
|
461
502
|
"additionalProperties": False,
|
|
@@ -469,7 +510,8 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
469
510
|
Tool(
|
|
470
511
|
name="update_key",
|
|
471
512
|
description=(
|
|
472
|
-
"Edit a key
|
|
513
|
+
"Edit a key in place — its SOURCE-language value, its `type`, "
|
|
514
|
+
"and/or its description (pass at least one). Preserves the "
|
|
473
515
|
"key and its history (version bump + history revision + audit). "
|
|
474
516
|
"Address the key by EITHER key_uuid (preferred; from list_keys) "
|
|
475
517
|
"OR namespace + name — exactly one of the two. When the source "
|
|
@@ -507,8 +549,13 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
507
549
|
"type": "string",
|
|
508
550
|
"description": "Optional: also update the key's description.",
|
|
509
551
|
},
|
|
552
|
+
"type": {
|
|
553
|
+
"type": "string",
|
|
554
|
+
"enum": list(KEY_TYPES),
|
|
555
|
+
"description": "Optional: reclassify the key's semantic type (see create_key). Changing the type re-gates which a11y treatments the key offers.",
|
|
556
|
+
},
|
|
510
557
|
},
|
|
511
|
-
"required": [
|
|
558
|
+
"required": [],
|
|
512
559
|
"additionalProperties": False,
|
|
513
560
|
},
|
|
514
561
|
),
|
|
@@ -543,8 +590,9 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
543
590
|
"name": {"type": "string", "description": "Dot-notation key name (use with namespace)."},
|
|
544
591
|
"source_value": {"type": "string", "description": "New source-language value to write."},
|
|
545
592
|
"description": {"type": "string", "description": "Optional: also update the key's description."},
|
|
593
|
+
"type": {"type": "string", "enum": list(KEY_TYPES), "description": "Optional: reclassify the key's semantic type (see create_key)."},
|
|
546
594
|
},
|
|
547
|
-
"required": [
|
|
595
|
+
"required": [],
|
|
548
596
|
"additionalProperties": False,
|
|
549
597
|
},
|
|
550
598
|
},
|
|
@@ -1126,6 +1174,9 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1126
1174
|
"locale lacks it), alt_missing (image key with no source alt_text), "
|
|
1127
1175
|
"reading_level_high (hard source text + no plain_language), "
|
|
1128
1176
|
"a11y_variant_absent (a require_surface is missing in the source). "
|
|
1177
|
+
"Gaps are TYPE-AWARE: only treatments relevant to each key's type are "
|
|
1178
|
+
"reported (e.g. alt_missing only on image keys, aria gaps only on "
|
|
1179
|
+
"buttons/links/inputs), so a text key is never flagged for aria/alt. "
|
|
1129
1180
|
"Defaults to the production version. Surfaced verbatim — use "
|
|
1130
1181
|
"list_a11y_gaps for just the actionable item list."
|
|
1131
1182
|
),
|
|
@@ -1287,6 +1338,221 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1287
1338
|
"additionalProperties": False,
|
|
1288
1339
|
},
|
|
1289
1340
|
),
|
|
1341
|
+
Tool(
|
|
1342
|
+
name="list_surfaces",
|
|
1343
|
+
description=(
|
|
1344
|
+
"List the project's configurable SURFACES (task 928): the device "
|
|
1345
|
+
"and a11y surfaces variants can be authored for. Returns "
|
|
1346
|
+
"{surfaces:[{slug, label, kind, builtin, active}]} where kind is "
|
|
1347
|
+
"'device' or 'a11y'. An un-customized project lazily defaults to the "
|
|
1348
|
+
"builtins — device: desktop/mobile/tablet; a11y: aria_label/alt_text/"
|
|
1349
|
+
"screen_reader/plain_language — all active. Only ACTIVE surfaces accept "
|
|
1350
|
+
"variant writes and get published to the CDN."
|
|
1351
|
+
),
|
|
1352
|
+
inputSchema={
|
|
1353
|
+
"type": "object",
|
|
1354
|
+
"properties": {
|
|
1355
|
+
"project_uuid": {
|
|
1356
|
+
"type": "string",
|
|
1357
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1358
|
+
},
|
|
1359
|
+
},
|
|
1360
|
+
"additionalProperties": False,
|
|
1361
|
+
},
|
|
1362
|
+
),
|
|
1363
|
+
Tool(
|
|
1364
|
+
name="create_surface",
|
|
1365
|
+
description=(
|
|
1366
|
+
"Create a CUSTOM DEVICE surface for the project (task 928), e.g. "
|
|
1367
|
+
"'watch', 'tv', 'kiosk'. Device surfaces are extensible; a11y surfaces "
|
|
1368
|
+
"are a FIXED semantic set — creating an a11y slug (e.g. aria_label) is "
|
|
1369
|
+
"rejected with 422. Returns the updated surface catalog. Builtins need "
|
|
1370
|
+
"no creation (they exist lazily)."
|
|
1371
|
+
),
|
|
1372
|
+
inputSchema={
|
|
1373
|
+
"type": "object",
|
|
1374
|
+
"properties": {
|
|
1375
|
+
"project_uuid": {
|
|
1376
|
+
"type": "string",
|
|
1377
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1378
|
+
},
|
|
1379
|
+
"slug": {
|
|
1380
|
+
"type": "string",
|
|
1381
|
+
"pattern": "^[a-z0-9_-]{1,40}$",
|
|
1382
|
+
"description": "Surface slug: lowercase letters, digits, '_' or '-', 1-40 chars (e.g. 'watch').",
|
|
1383
|
+
},
|
|
1384
|
+
"label": {
|
|
1385
|
+
"type": "string",
|
|
1386
|
+
"description": "Optional human-readable label (defaults from the slug).",
|
|
1387
|
+
},
|
|
1388
|
+
},
|
|
1389
|
+
"required": ["slug"],
|
|
1390
|
+
"additionalProperties": False,
|
|
1391
|
+
},
|
|
1392
|
+
),
|
|
1393
|
+
Tool(
|
|
1394
|
+
name="update_surface",
|
|
1395
|
+
description=(
|
|
1396
|
+
"Rename and/or toggle a surface (task 928): set its `label` and/or "
|
|
1397
|
+
"`active`. Deactivating is SOFT — existing variants are RETAINED but "
|
|
1398
|
+
"hidden and dropped from the CDN until reactivated (which republishes "
|
|
1399
|
+
"them); the response `affected_variants` reports how many are impacted. "
|
|
1400
|
+
"Builtins can be toggled/renamed but not deleted. Returns "
|
|
1401
|
+
"{surfaces, affected_variants}."
|
|
1402
|
+
),
|
|
1403
|
+
inputSchema={
|
|
1404
|
+
"type": "object",
|
|
1405
|
+
"properties": {
|
|
1406
|
+
"project_uuid": {
|
|
1407
|
+
"type": "string",
|
|
1408
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1409
|
+
},
|
|
1410
|
+
"slug": {
|
|
1411
|
+
"type": "string",
|
|
1412
|
+
"description": "Slug of the surface to update.",
|
|
1413
|
+
},
|
|
1414
|
+
"label": {
|
|
1415
|
+
"type": "string",
|
|
1416
|
+
"description": "New human-readable label (rename).",
|
|
1417
|
+
},
|
|
1418
|
+
"active": {
|
|
1419
|
+
"type": "boolean",
|
|
1420
|
+
"description": "Activate (true) or soft-deactivate (false) the surface.",
|
|
1421
|
+
},
|
|
1422
|
+
},
|
|
1423
|
+
"required": ["slug"],
|
|
1424
|
+
"additionalProperties": False,
|
|
1425
|
+
},
|
|
1426
|
+
),
|
|
1427
|
+
Tool(
|
|
1428
|
+
name="delete_surface",
|
|
1429
|
+
description=(
|
|
1430
|
+
"Delete a CUSTOM surface (task 928). Builtins cannot be deleted — "
|
|
1431
|
+
"deactivate them with update_surface(active=false) instead (a 422/409 "
|
|
1432
|
+
"is returned if you try to delete a builtin). Deleting a custom surface "
|
|
1433
|
+
"removes it from the catalog."
|
|
1434
|
+
),
|
|
1435
|
+
inputSchema={
|
|
1436
|
+
"type": "object",
|
|
1437
|
+
"properties": {
|
|
1438
|
+
"project_uuid": {
|
|
1439
|
+
"type": "string",
|
|
1440
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1441
|
+
},
|
|
1442
|
+
"slug": {
|
|
1443
|
+
"type": "string",
|
|
1444
|
+
"description": "Slug of the custom surface to delete.",
|
|
1445
|
+
},
|
|
1446
|
+
},
|
|
1447
|
+
"required": ["slug"],
|
|
1448
|
+
"additionalProperties": False,
|
|
1449
|
+
},
|
|
1450
|
+
),
|
|
1451
|
+
Tool(
|
|
1452
|
+
name="get_variants",
|
|
1453
|
+
description=(
|
|
1454
|
+
"Read the full VARIANT MATRIX for one key (task 928): every surface "
|
|
1455
|
+
"OFFERED for the key x every project language, with each cell's "
|
|
1456
|
+
"{override, value, resolvedValue, status, drift, ...}. The response "
|
|
1457
|
+
"lists `surfaces` and `surfaceKinds` ({surface: 'device'|'a11y'}) — "
|
|
1458
|
+
"these are the key's offered surfaces only: the project's active "
|
|
1459
|
+
"device surfaces plus the a11y treatments relevant to the key's TYPE "
|
|
1460
|
+
"(e.g. a text key shows no aria/alt). This is the source of truth for "
|
|
1461
|
+
"which surfaces set_variant will accept. Address by key_uuid (from "
|
|
1462
|
+
"list_keys). Surfaced verbatim."
|
|
1463
|
+
),
|
|
1464
|
+
inputSchema={
|
|
1465
|
+
"type": "object",
|
|
1466
|
+
"properties": {
|
|
1467
|
+
"project_uuid": {
|
|
1468
|
+
"type": "string",
|
|
1469
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1470
|
+
},
|
|
1471
|
+
"key_uuid": {
|
|
1472
|
+
"type": "string",
|
|
1473
|
+
"description": "Key UUID (from list_keys).",
|
|
1474
|
+
},
|
|
1475
|
+
},
|
|
1476
|
+
"required": ["key_uuid"],
|
|
1477
|
+
"additionalProperties": False,
|
|
1478
|
+
},
|
|
1479
|
+
),
|
|
1480
|
+
Tool(
|
|
1481
|
+
name="set_variant",
|
|
1482
|
+
description=(
|
|
1483
|
+
"Upsert ONE variant override for (key, language, surface) for ANY "
|
|
1484
|
+
"surface active on the project (task 928) — device (desktop / mobile / "
|
|
1485
|
+
"tablet / custom like watch / tv) OR a11y (aria_label / alt_text / "
|
|
1486
|
+
"screen_reader / plain_language). Address by key_uuid + locale CODE + "
|
|
1487
|
+
"surface slug (the fields get_variants / list_keys already give you). "
|
|
1488
|
+
"Stored as a draft. 422 unless the surface is OFFERED for this key — "
|
|
1489
|
+
"an active device surface (see list_surfaces) OR an a11y treatment "
|
|
1490
|
+
"relevant to the key's TYPE (e.g. no aria_label on a text key; see "
|
|
1491
|
+
"get_variants for the key's offered surfaces); 409 if the cell is "
|
|
1492
|
+
"already reviewed/approved (never overwritten). Returns {key_uuid, "
|
|
1493
|
+
"surface, language_code, value, status}. (For a11y specifically you "
|
|
1494
|
+
"can also use set_a11y_variant.)"
|
|
1495
|
+
),
|
|
1496
|
+
inputSchema={
|
|
1497
|
+
"type": "object",
|
|
1498
|
+
"properties": {
|
|
1499
|
+
"project_uuid": {
|
|
1500
|
+
"type": "string",
|
|
1501
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1502
|
+
},
|
|
1503
|
+
"key_uuid": {
|
|
1504
|
+
"type": "string",
|
|
1505
|
+
"description": "Key UUID (from list_keys / get_variants).",
|
|
1506
|
+
},
|
|
1507
|
+
"language_code": {
|
|
1508
|
+
"type": "string",
|
|
1509
|
+
"description": "Target locale CODE, e.g. 'fr'. Backend resolves it; 404 if unknown.",
|
|
1510
|
+
},
|
|
1511
|
+
"surface": {
|
|
1512
|
+
"type": "string",
|
|
1513
|
+
"description": "Surface slug — any surface ACTIVE for the project (device or a11y). See list_surfaces.",
|
|
1514
|
+
},
|
|
1515
|
+
"value": {
|
|
1516
|
+
"type": "string",
|
|
1517
|
+
"description": "The variant text value for this (key, language, surface).",
|
|
1518
|
+
},
|
|
1519
|
+
},
|
|
1520
|
+
"required": ["key_uuid", "language_code", "surface", "value"],
|
|
1521
|
+
"additionalProperties": False,
|
|
1522
|
+
},
|
|
1523
|
+
),
|
|
1524
|
+
Tool(
|
|
1525
|
+
name="delete_variant",
|
|
1526
|
+
description=(
|
|
1527
|
+
"Delete ONE variant override for (key, language, surface), for ANY "
|
|
1528
|
+
"surface (device or a11y) — task 928. The cell re-inherits the base "
|
|
1529
|
+
"value; a 404 means there was no override. Address by key_uuid + "
|
|
1530
|
+
"language_code + surface (same as set_variant)."
|
|
1531
|
+
),
|
|
1532
|
+
inputSchema={
|
|
1533
|
+
"type": "object",
|
|
1534
|
+
"properties": {
|
|
1535
|
+
"project_uuid": {
|
|
1536
|
+
"type": "string",
|
|
1537
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1538
|
+
},
|
|
1539
|
+
"key_uuid": {
|
|
1540
|
+
"type": "string",
|
|
1541
|
+
"description": "Key UUID (from list_keys / get_variants).",
|
|
1542
|
+
},
|
|
1543
|
+
"language_code": {
|
|
1544
|
+
"type": "string",
|
|
1545
|
+
"description": "Target locale CODE, e.g. 'fr'.",
|
|
1546
|
+
},
|
|
1547
|
+
"surface": {
|
|
1548
|
+
"type": "string",
|
|
1549
|
+
"description": "Surface slug (device or a11y) to clear.",
|
|
1550
|
+
},
|
|
1551
|
+
},
|
|
1552
|
+
"required": ["key_uuid", "language_code", "surface"],
|
|
1553
|
+
"additionalProperties": False,
|
|
1554
|
+
},
|
|
1555
|
+
),
|
|
1290
1556
|
]
|
|
1291
1557
|
|
|
1292
1558
|
@server.call_tool()
|
|
@@ -1408,7 +1674,7 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1408
1674
|
"namespace": args["namespace"],
|
|
1409
1675
|
"name": args["name"],
|
|
1410
1676
|
}
|
|
1411
|
-
for opt in ("source_value", "description", "plurals", "max_length"):
|
|
1677
|
+
for opt in ("source_value", "description", "plurals", "max_length", "type"):
|
|
1412
1678
|
if opt in args:
|
|
1413
1679
|
body[opt] = args[opt]
|
|
1414
1680
|
data = await client.post(
|
|
@@ -1440,16 +1706,18 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1440
1706
|
return _text(data)
|
|
1441
1707
|
if name == "update_key":
|
|
1442
1708
|
project_uuid = _project_uuid(args, client)
|
|
1443
|
-
if "source_value"
|
|
1444
|
-
return _err(
|
|
1709
|
+
if not any(k in args for k in ("source_value", "type", "description")):
|
|
1710
|
+
return _err(
|
|
1711
|
+
"nothing to update: pass at least one of source_value, type, description"
|
|
1712
|
+
)
|
|
1445
1713
|
has_uuid = bool(args.get("key_uuid"))
|
|
1446
1714
|
has_name = bool(args.get("namespace")) and bool(args.get("name"))
|
|
1447
1715
|
if has_uuid == has_name:
|
|
1448
1716
|
return _err(
|
|
1449
1717
|
"address the key by exactly one of key_uuid OR (namespace + name)"
|
|
1450
1718
|
)
|
|
1451
|
-
body: dict[str, Any] = {
|
|
1452
|
-
for opt in ("key_uuid", "namespace", "name", "description"):
|
|
1719
|
+
body: dict[str, Any] = {}
|
|
1720
|
+
for opt in ("source_value", "key_uuid", "namespace", "name", "description", "type"):
|
|
1453
1721
|
if opt in args:
|
|
1454
1722
|
body[opt] = args[opt]
|
|
1455
1723
|
data = await client.patch(
|
|
@@ -1726,11 +1994,13 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1726
1994
|
return _err("key_uuid, language_code and surface are required")
|
|
1727
1995
|
if surface not in A11Y_SURFACES:
|
|
1728
1996
|
return _err(f"surface must be one of {', '.join(A11Y_SURFACES)}")
|
|
1729
|
-
# mcp:* code-addressed
|
|
1730
|
-
#
|
|
1997
|
+
# mcp:* code-addressed GENERIC variant route (covers a11y + device;
|
|
1998
|
+
# task 928). Backend resolves the locale code -> uuid and validates the
|
|
1999
|
+
# surface is active server-side. (The legacy /a11y-variants/ path still
|
|
2000
|
+
# works for back-compat, but /variants/ is canonical.)
|
|
1731
2001
|
path = (
|
|
1732
2002
|
f"/v1/mcp/projects/{project_uuid}/keys/{key_uuid}"
|
|
1733
|
-
f"/
|
|
2003
|
+
f"/variants/{language_code}/{surface}"
|
|
1734
2004
|
)
|
|
1735
2005
|
if name == "delete_a11y_variant":
|
|
1736
2006
|
await client.delete(path)
|
|
@@ -1746,6 +2016,79 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1746
2016
|
return _err("value is required")
|
|
1747
2017
|
data = await client.put(path, json={"value": args["value"]})
|
|
1748
2018
|
return _text(data)
|
|
2019
|
+
if name == "list_surfaces":
|
|
2020
|
+
project_uuid = _project_uuid(args, client)
|
|
2021
|
+
data = await client.get(f"/v1/mcp/projects/{project_uuid}/surfaces")
|
|
2022
|
+
return _text(data)
|
|
2023
|
+
if name == "create_surface":
|
|
2024
|
+
project_uuid = _project_uuid(args, client)
|
|
2025
|
+
if not args.get("slug"):
|
|
2026
|
+
return _err("slug is required")
|
|
2027
|
+
body: dict[str, Any] = {"slug": args["slug"]}
|
|
2028
|
+
if "label" in args:
|
|
2029
|
+
body["label"] = args["label"]
|
|
2030
|
+
data = await client.post(
|
|
2031
|
+
f"/v1/mcp/projects/{project_uuid}/surfaces", json=body
|
|
2032
|
+
)
|
|
2033
|
+
return _text(data)
|
|
2034
|
+
if name == "update_surface":
|
|
2035
|
+
project_uuid = _project_uuid(args, client)
|
|
2036
|
+
slug = args.get("slug")
|
|
2037
|
+
if not slug:
|
|
2038
|
+
return _err("slug is required")
|
|
2039
|
+
body: dict[str, Any] = {}
|
|
2040
|
+
for opt in ("label", "active"):
|
|
2041
|
+
if opt in args:
|
|
2042
|
+
body[opt] = args[opt]
|
|
2043
|
+
if not body:
|
|
2044
|
+
return _err("nothing to update: pass label and/or active")
|
|
2045
|
+
data = await client.put(
|
|
2046
|
+
f"/v1/mcp/projects/{project_uuid}/surfaces/{slug}", json=body
|
|
2047
|
+
)
|
|
2048
|
+
return _text(data)
|
|
2049
|
+
if name == "delete_surface":
|
|
2050
|
+
project_uuid = _project_uuid(args, client)
|
|
2051
|
+
slug = args.get("slug")
|
|
2052
|
+
if not slug:
|
|
2053
|
+
return _err("slug is required")
|
|
2054
|
+
await client.delete(f"/v1/mcp/projects/{project_uuid}/surfaces/{slug}")
|
|
2055
|
+
return _text({"deleted": True, "slug": slug})
|
|
2056
|
+
if name == "get_variants":
|
|
2057
|
+
project_uuid = _project_uuid(args, client)
|
|
2058
|
+
key_uuid = args.get("key_uuid")
|
|
2059
|
+
if not key_uuid:
|
|
2060
|
+
return _err("key_uuid is required")
|
|
2061
|
+
data = await client.get(
|
|
2062
|
+
f"/v1/mcp/projects/{project_uuid}/keys/{key_uuid}/variants"
|
|
2063
|
+
)
|
|
2064
|
+
return _text(data)
|
|
2065
|
+
if name in ("set_variant", "delete_variant"):
|
|
2066
|
+
project_uuid = _project_uuid(args, client)
|
|
2067
|
+
key_uuid = args.get("key_uuid")
|
|
2068
|
+
language_code = args.get("language_code")
|
|
2069
|
+
surface = args.get("surface")
|
|
2070
|
+
if not key_uuid or not language_code or not surface:
|
|
2071
|
+
return _err("key_uuid, language_code and surface are required")
|
|
2072
|
+
# generic variant route: backend validates the surface is active (422)
|
|
2073
|
+
# and guards reviewed/approved cells (409).
|
|
2074
|
+
path = (
|
|
2075
|
+
f"/v1/mcp/projects/{project_uuid}/keys/{key_uuid}"
|
|
2076
|
+
f"/variants/{language_code}/{surface}"
|
|
2077
|
+
)
|
|
2078
|
+
if name == "delete_variant":
|
|
2079
|
+
await client.delete(path)
|
|
2080
|
+
return _text(
|
|
2081
|
+
{
|
|
2082
|
+
"deleted": True,
|
|
2083
|
+
"key_uuid": key_uuid,
|
|
2084
|
+
"language_code": language_code,
|
|
2085
|
+
"surface": surface,
|
|
2086
|
+
}
|
|
2087
|
+
)
|
|
2088
|
+
if "value" not in args:
|
|
2089
|
+
return _err("value is required")
|
|
2090
|
+
data = await client.put(path, json={"value": args["value"]})
|
|
2091
|
+
return _text(data)
|
|
1749
2092
|
return _err(f"unknown tool: {name}")
|
|
1750
2093
|
except VerbumiaApiError as exc:
|
|
1751
2094
|
return _err(f"API {exc.status}: {exc.body}")
|
|
Binary file
|