@sonenta/mcp 0.16.1 → 0.17.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.17.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.17.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"
|
|
@@ -28,6 +28,22 @@ Tools (V1 MCP surface; glossary_* added 2026-05-24; reports + trash added 2026-0
|
|
|
28
28
|
- delete_keys_bulk — soft-delete (trash) keys by key_uuid; restorable
|
|
29
29
|
- restore_keys_bulk — restore trashed keys by key_uuid
|
|
30
30
|
- list_trash — list trashed (soft-deleted) keys (cursor paginated)
|
|
31
|
+
- a11y_estimate — preview the credit cost of an a11y generate/translate run (#a11y V1)
|
|
32
|
+
- generate_a11y_variant — AI-generate source-lang a11y variants (aria/alt/screen_reader/plain_language)
|
|
33
|
+
- translate_a11y_variants — translate existing source a11y overrides into target locales
|
|
34
|
+
- a11y_report — read-only WCAG a11y gap report (per key/surface/locale)
|
|
35
|
+
- list_a11y_gaps — actionable a11y gaps (filtered items[] of a11y_report)
|
|
36
|
+
- set_a11y_variant — upsert one a11y variant override (key, language, surface)
|
|
37
|
+
- delete_a11y_variant — delete one a11y variant override (cell re-inherits base value)
|
|
38
|
+
|
|
39
|
+
A11y (accessibility) tools (a11y V1) extend the variant engine with four SEMANTIC
|
|
40
|
+
surfaces — aria_label / alt_text / screen_reader / plain_language — orthogonal to
|
|
41
|
+
the visible text. The report hits `/v1/mcp/projects/{id}/a11y-report`; set/delete
|
|
42
|
+
use the mcp:* code-addressed route
|
|
43
|
+
`/v1/mcp/projects/{id}/keys/{key_uuid}/a11y-variants/{language_code}/{surface}`
|
|
44
|
+
(locale reinjected straight from an a11y_report item). estimate/generate/translate
|
|
45
|
+
are billed in credits. All are thin HTTP wrappers surfaced verbatim. Every a11y
|
|
46
|
+
tool is reachable with the same `mcp:*` key as the rest of the MCP surface.
|
|
31
47
|
|
|
32
48
|
list_keys and list_untranslated_keys accept `include_glossary_hints=true` to
|
|
33
49
|
attach backend-matched `glossary_hints[]` per key. All glossary matching is
|
|
@@ -45,6 +61,7 @@ The tool surface mirrors the canonical contract published in
|
|
|
45
61
|
from __future__ import annotations
|
|
46
62
|
|
|
47
63
|
import json
|
|
64
|
+
import uuid
|
|
48
65
|
from typing import Any
|
|
49
66
|
|
|
50
67
|
from mcp.server import Server
|
|
@@ -52,6 +69,12 @@ from mcp.types import TextContent, Tool
|
|
|
52
69
|
|
|
53
70
|
from .client import VerbumiaApiError, VerbumiaClient
|
|
54
71
|
|
|
72
|
+
# The four accessibility (a11y) variant surfaces (task 989). Distinct from the
|
|
73
|
+
# device surfaces (desktop/mobile/tablet); these map to semantic attributes
|
|
74
|
+
# (aria-label, <img alt>, sr-only text, clear-language rendering), not the
|
|
75
|
+
# visible text. `surfaceKinds` in the variants matrix tags each as a11y.
|
|
76
|
+
A11Y_SURFACES = ("aria_label", "alt_text", "screen_reader", "plain_language")
|
|
77
|
+
|
|
55
78
|
|
|
56
79
|
def _project_uuid(arguments: dict | None, client: VerbumiaClient) -> str:
|
|
57
80
|
"""Resolve project_uuid from per-call args or the configured env scope.
|
|
@@ -939,6 +962,331 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
939
962
|
"additionalProperties": False,
|
|
940
963
|
},
|
|
941
964
|
),
|
|
965
|
+
Tool(
|
|
966
|
+
name="a11y_estimate",
|
|
967
|
+
description=(
|
|
968
|
+
"Preview the CREDIT cost of an a11y generate/translate run BEFORE "
|
|
969
|
+
"spending anything (task 990). Returns {units, credit_cost_per_unit, "
|
|
970
|
+
"credits_required, balance, sufficient}. ALWAYS call this before "
|
|
971
|
+
"generate_a11y_variant / translate_a11y_variants so you know the cost "
|
|
972
|
+
"and whether the balance covers it. mode=generate counts missing "
|
|
973
|
+
"(key, surface) source variants; mode=translate counts "
|
|
974
|
+
"(key, surface, target-language) cells lacking a translation."
|
|
975
|
+
),
|
|
976
|
+
inputSchema={
|
|
977
|
+
"type": "object",
|
|
978
|
+
"properties": {
|
|
979
|
+
"project_uuid": {
|
|
980
|
+
"type": "string",
|
|
981
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
982
|
+
},
|
|
983
|
+
"mode": {
|
|
984
|
+
"type": "string",
|
|
985
|
+
"enum": ["generate", "translate"],
|
|
986
|
+
"description": "generate = source-language a11y variants; translate = propagate existing a11y overrides into target locales.",
|
|
987
|
+
},
|
|
988
|
+
"tier": {
|
|
989
|
+
"type": "string",
|
|
990
|
+
"enum": ["standard", "premium"],
|
|
991
|
+
"default": "standard",
|
|
992
|
+
"description": "Quality tier (credit_cost per unit: standard=1, premium=4).",
|
|
993
|
+
},
|
|
994
|
+
"surfaces": {
|
|
995
|
+
"type": "array",
|
|
996
|
+
"items": {"type": "string", "enum": list(A11Y_SURFACES)},
|
|
997
|
+
"description": "a11y surfaces to scope to. Omit/empty = all four (aria_label, alt_text, screen_reader, plain_language).",
|
|
998
|
+
},
|
|
999
|
+
"language_codes": {
|
|
1000
|
+
"type": "array",
|
|
1001
|
+
"items": {"type": "string"},
|
|
1002
|
+
"description": "Target locales (translate mode only), e.g. ['fr','es'].",
|
|
1003
|
+
},
|
|
1004
|
+
"key_uuids": {
|
|
1005
|
+
"type": "array",
|
|
1006
|
+
"items": {"type": "string"},
|
|
1007
|
+
"description": "Restrict to these keys (get key_uuid from list_keys). Omit to scope the whole project (or a namespace).",
|
|
1008
|
+
},
|
|
1009
|
+
"namespace_uuid": {
|
|
1010
|
+
"type": "string",
|
|
1011
|
+
"description": "Restrict to one namespace by its UUID.",
|
|
1012
|
+
},
|
|
1013
|
+
},
|
|
1014
|
+
"required": ["mode"],
|
|
1015
|
+
"additionalProperties": False,
|
|
1016
|
+
},
|
|
1017
|
+
),
|
|
1018
|
+
Tool(
|
|
1019
|
+
name="generate_a11y_variant",
|
|
1020
|
+
description=(
|
|
1021
|
+
"AI-generate SOURCE-language a11y variants for keys that lack them "
|
|
1022
|
+
"(task 990). aria_label / screen_reader / plain_language are derived "
|
|
1023
|
+
"from the key's visible text; alt_text from the key's context/"
|
|
1024
|
+
"description. Only fills (key, surface) cells with NO existing override "
|
|
1025
|
+
"and an available source. BILLS CREDITS (standard=1, premium=4 per "
|
|
1026
|
+
"generated item, debit upfront + refund undelivered) — call a11y_estimate "
|
|
1027
|
+
"first. Results are draft (bot-authored) overrides that flow to the CDN "
|
|
1028
|
+
"a11y overlays. Returns {generated, requested, credits_charged, "
|
|
1029
|
+
"credits_refunded}."
|
|
1030
|
+
),
|
|
1031
|
+
inputSchema={
|
|
1032
|
+
"type": "object",
|
|
1033
|
+
"properties": {
|
|
1034
|
+
"project_uuid": {
|
|
1035
|
+
"type": "string",
|
|
1036
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1037
|
+
},
|
|
1038
|
+
"tier": {
|
|
1039
|
+
"type": "string",
|
|
1040
|
+
"enum": ["standard", "premium"],
|
|
1041
|
+
"default": "standard",
|
|
1042
|
+
"description": "Quality tier (credit_cost per unit: standard=1, premium=4).",
|
|
1043
|
+
},
|
|
1044
|
+
"surfaces": {
|
|
1045
|
+
"type": "array",
|
|
1046
|
+
"items": {"type": "string", "enum": list(A11Y_SURFACES)},
|
|
1047
|
+
"description": "a11y surfaces to generate. Omit/empty = all four.",
|
|
1048
|
+
},
|
|
1049
|
+
"key_uuids": {
|
|
1050
|
+
"type": "array",
|
|
1051
|
+
"items": {"type": "string"},
|
|
1052
|
+
"description": "Restrict to these keys (get key_uuid from list_keys). Omit to scope the whole project (or a namespace).",
|
|
1053
|
+
},
|
|
1054
|
+
"namespace_uuid": {
|
|
1055
|
+
"type": "string",
|
|
1056
|
+
"description": "Restrict to one namespace by its UUID.",
|
|
1057
|
+
},
|
|
1058
|
+
"idempotency_key": {
|
|
1059
|
+
"type": "string",
|
|
1060
|
+
"description": "Stable token making this billable call safe to retry: reuse the SAME value to avoid a double-charge on retry. A random one is generated if omitted (so a blind retry would re-bill).",
|
|
1061
|
+
},
|
|
1062
|
+
},
|
|
1063
|
+
"additionalProperties": False,
|
|
1064
|
+
},
|
|
1065
|
+
),
|
|
1066
|
+
Tool(
|
|
1067
|
+
name="translate_a11y_variants",
|
|
1068
|
+
description=(
|
|
1069
|
+
"Translate EXISTING source-language a11y overrides into target locales "
|
|
1070
|
+
"(task 990). Only translates (key, surface) that HAVE a source override, "
|
|
1071
|
+
"into the requested languages that lack one. BILLS CREDITS "
|
|
1072
|
+
"(standard=1, premium=4 per item, debit upfront + refund undelivered) — "
|
|
1073
|
+
"call a11y_estimate (mode=translate) first. Results are draft "
|
|
1074
|
+
"(bot-authored) overrides that flow to the CDN a11y overlays. Returns "
|
|
1075
|
+
"{translated, requested, credits_charged, credits_refunded}."
|
|
1076
|
+
),
|
|
1077
|
+
inputSchema={
|
|
1078
|
+
"type": "object",
|
|
1079
|
+
"properties": {
|
|
1080
|
+
"project_uuid": {
|
|
1081
|
+
"type": "string",
|
|
1082
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1083
|
+
},
|
|
1084
|
+
"tier": {
|
|
1085
|
+
"type": "string",
|
|
1086
|
+
"enum": ["standard", "premium"],
|
|
1087
|
+
"default": "standard",
|
|
1088
|
+
"description": "Quality tier (credit_cost per unit: standard=1, premium=4).",
|
|
1089
|
+
},
|
|
1090
|
+
"language_codes": {
|
|
1091
|
+
"type": "array",
|
|
1092
|
+
"items": {"type": "string"},
|
|
1093
|
+
"minItems": 1,
|
|
1094
|
+
"description": "Target locales to translate into, e.g. ['fr','es'].",
|
|
1095
|
+
},
|
|
1096
|
+
"surfaces": {
|
|
1097
|
+
"type": "array",
|
|
1098
|
+
"items": {"type": "string", "enum": list(A11Y_SURFACES)},
|
|
1099
|
+
"description": "a11y surfaces to translate. Omit/empty = all four.",
|
|
1100
|
+
},
|
|
1101
|
+
"key_uuids": {
|
|
1102
|
+
"type": "array",
|
|
1103
|
+
"items": {"type": "string"},
|
|
1104
|
+
"description": "Restrict to these keys (get key_uuid from list_keys). Omit to scope the whole project (or a namespace).",
|
|
1105
|
+
},
|
|
1106
|
+
"namespace_uuid": {
|
|
1107
|
+
"type": "string",
|
|
1108
|
+
"description": "Restrict to one namespace by its UUID.",
|
|
1109
|
+
},
|
|
1110
|
+
"idempotency_key": {
|
|
1111
|
+
"type": "string",
|
|
1112
|
+
"description": "Stable token making this billable call safe to retry: reuse the SAME value to avoid a double-charge on retry. A random one is generated if omitted (so a blind retry would re-bill).",
|
|
1113
|
+
},
|
|
1114
|
+
},
|
|
1115
|
+
"required": ["language_codes"],
|
|
1116
|
+
"additionalProperties": False,
|
|
1117
|
+
},
|
|
1118
|
+
),
|
|
1119
|
+
Tool(
|
|
1120
|
+
name="a11y_report",
|
|
1121
|
+
description=(
|
|
1122
|
+
"Read-only WCAG ACCESSIBILITY quality report for the project "
|
|
1123
|
+
"(task 991): per key/surface/locale a11y gaps with by_gap / "
|
|
1124
|
+
"by_severity / by_surface rollups + per-item details. Gap types: "
|
|
1125
|
+
"a11y_untranslated (a source a11y override exists but this target "
|
|
1126
|
+
"locale lacks it), alt_missing (image key with no source alt_text), "
|
|
1127
|
+
"reading_level_high (hard source text + no plain_language), "
|
|
1128
|
+
"a11y_variant_absent (a require_surface is missing in the source). "
|
|
1129
|
+
"Defaults to the production version. Surfaced verbatim — use "
|
|
1130
|
+
"list_a11y_gaps for just the actionable item list."
|
|
1131
|
+
),
|
|
1132
|
+
inputSchema={
|
|
1133
|
+
"type": "object",
|
|
1134
|
+
"properties": {
|
|
1135
|
+
"project_uuid": {
|
|
1136
|
+
"type": "string",
|
|
1137
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1138
|
+
},
|
|
1139
|
+
"version_id": {
|
|
1140
|
+
"type": "string",
|
|
1141
|
+
"description": "Optional version UUID; defaults to the production version.",
|
|
1142
|
+
},
|
|
1143
|
+
"require_surface": {
|
|
1144
|
+
"type": "array",
|
|
1145
|
+
"items": {"type": "string", "enum": list(A11Y_SURFACES)},
|
|
1146
|
+
"description": "a11y surfaces to additionally flag as ABSENT in the source (produces a11y_variant_absent gaps), e.g. ['aria_label','alt_text'].",
|
|
1147
|
+
},
|
|
1148
|
+
"reading_level_max_words": {
|
|
1149
|
+
"type": "integer",
|
|
1150
|
+
"minimum": 5,
|
|
1151
|
+
"maximum": 60,
|
|
1152
|
+
"description": "Readability heuristic threshold (sentence length) for reading_level_high gaps. Default 15 server-side.",
|
|
1153
|
+
},
|
|
1154
|
+
},
|
|
1155
|
+
"additionalProperties": False,
|
|
1156
|
+
},
|
|
1157
|
+
),
|
|
1158
|
+
Tool(
|
|
1159
|
+
name="list_a11y_gaps",
|
|
1160
|
+
description=(
|
|
1161
|
+
"List the actionable a11y gaps (the items[] of a11y_report, task "
|
|
1162
|
+
"991), optionally filtered client-side. Each gap = {key_uuid, "
|
|
1163
|
+
"key_name, namespace_slug, surface, locale, gap, severity, detail}. "
|
|
1164
|
+
"To find keys/surfaces with NO a11y variant in the source, pass "
|
|
1165
|
+
"require_surface=<surface> and filter gap='a11y_variant_absent'. "
|
|
1166
|
+
"Feed key_uuid + surface (+ locale) into set_a11y_variant to fix one, "
|
|
1167
|
+
"or generate_a11y_variant / translate_a11y_variants to fill them in "
|
|
1168
|
+
"bulk."
|
|
1169
|
+
),
|
|
1170
|
+
inputSchema={
|
|
1171
|
+
"type": "object",
|
|
1172
|
+
"properties": {
|
|
1173
|
+
"project_uuid": {
|
|
1174
|
+
"type": "string",
|
|
1175
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1176
|
+
},
|
|
1177
|
+
"gap": {
|
|
1178
|
+
"type": "string",
|
|
1179
|
+
"enum": [
|
|
1180
|
+
"a11y_untranslated",
|
|
1181
|
+
"alt_missing",
|
|
1182
|
+
"reading_level_high",
|
|
1183
|
+
"a11y_variant_absent",
|
|
1184
|
+
],
|
|
1185
|
+
"description": "Client-side filter: keep only gaps of this type.",
|
|
1186
|
+
},
|
|
1187
|
+
"surface": {
|
|
1188
|
+
"type": "string",
|
|
1189
|
+
"enum": list(A11Y_SURFACES),
|
|
1190
|
+
"description": "Client-side filter: keep only gaps on this a11y surface.",
|
|
1191
|
+
},
|
|
1192
|
+
"locale": {
|
|
1193
|
+
"type": "string",
|
|
1194
|
+
"description": "Client-side filter: keep only gaps for this target locale code (e.g. 'fr'). Source-level gaps have locale=null.",
|
|
1195
|
+
},
|
|
1196
|
+
"require_surface": {
|
|
1197
|
+
"type": "array",
|
|
1198
|
+
"items": {"type": "string", "enum": list(A11Y_SURFACES)},
|
|
1199
|
+
"description": "a11y surfaces to flag as ABSENT in the source (produces a11y_variant_absent gaps). Pair with gap='a11y_variant_absent' to list keys missing a surface.",
|
|
1200
|
+
},
|
|
1201
|
+
"version_id": {
|
|
1202
|
+
"type": "string",
|
|
1203
|
+
"description": "Optional version UUID; defaults to the production version.",
|
|
1204
|
+
},
|
|
1205
|
+
"reading_level_max_words": {
|
|
1206
|
+
"type": "integer",
|
|
1207
|
+
"minimum": 5,
|
|
1208
|
+
"maximum": 60,
|
|
1209
|
+
"description": "Readability heuristic threshold for reading_level_high gaps. Default 15 server-side.",
|
|
1210
|
+
},
|
|
1211
|
+
},
|
|
1212
|
+
"additionalProperties": False,
|
|
1213
|
+
},
|
|
1214
|
+
),
|
|
1215
|
+
Tool(
|
|
1216
|
+
name="set_a11y_variant",
|
|
1217
|
+
description=(
|
|
1218
|
+
"Upsert ONE a11y variant override for (key, language, surface) — the "
|
|
1219
|
+
"manual fix for a gap (a11y V1). surface ∈ aria_label | alt_text | "
|
|
1220
|
+
"screen_reader | plain_language. The value is a TEXT string (the "
|
|
1221
|
+
"semantic a11y text, e.g. the accessible name or the simplified "
|
|
1222
|
+
"wording), translatable later via translate_a11y_variants. Address "
|
|
1223
|
+
"the key by key_uuid and the language by its locale CODE — exactly "
|
|
1224
|
+
"the (key_uuid, surface, locale) a gap carries in a11y_report, so you "
|
|
1225
|
+
"can fix it without resolving any UUID. Stored as a draft (bot) "
|
|
1226
|
+
"override. Returns {key_uuid, surface, language_code, value, status}."
|
|
1227
|
+
),
|
|
1228
|
+
inputSchema={
|
|
1229
|
+
"type": "object",
|
|
1230
|
+
"properties": {
|
|
1231
|
+
"project_uuid": {
|
|
1232
|
+
"type": "string",
|
|
1233
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1234
|
+
},
|
|
1235
|
+
"key_uuid": {
|
|
1236
|
+
"type": "string",
|
|
1237
|
+
"description": "Key UUID (from list_keys or a11y_report / list_a11y_gaps items).",
|
|
1238
|
+
},
|
|
1239
|
+
"language_code": {
|
|
1240
|
+
"type": "string",
|
|
1241
|
+
"description": "Target locale CODE, e.g. 'fr' (the `locale` field of an a11y_report item). The backend resolves it to the language; 404 if unknown.",
|
|
1242
|
+
},
|
|
1243
|
+
"surface": {
|
|
1244
|
+
"type": "string",
|
|
1245
|
+
"enum": list(A11Y_SURFACES),
|
|
1246
|
+
"description": "Which a11y surface to set.",
|
|
1247
|
+
},
|
|
1248
|
+
"value": {
|
|
1249
|
+
"type": "string",
|
|
1250
|
+
"description": "The a11y text value for this (key, language, surface).",
|
|
1251
|
+
},
|
|
1252
|
+
},
|
|
1253
|
+
"required": ["key_uuid", "language_code", "surface", "value"],
|
|
1254
|
+
"additionalProperties": False,
|
|
1255
|
+
},
|
|
1256
|
+
),
|
|
1257
|
+
Tool(
|
|
1258
|
+
name="delete_a11y_variant",
|
|
1259
|
+
description=(
|
|
1260
|
+
"Delete ONE a11y variant override for (key, language, surface) "
|
|
1261
|
+
"(a11y V1). The cell re-inherits the base value per the locale/surface "
|
|
1262
|
+
"fallback chain; a 404 means there was no override to remove. Address "
|
|
1263
|
+
"by key_uuid + language_code + surface (same as set_a11y_variant)."
|
|
1264
|
+
),
|
|
1265
|
+
inputSchema={
|
|
1266
|
+
"type": "object",
|
|
1267
|
+
"properties": {
|
|
1268
|
+
"project_uuid": {
|
|
1269
|
+
"type": "string",
|
|
1270
|
+
"description": "Project UUID. Required when multiple projects are configured (SONENTA_PROJECTS); defaults to the single configured project otherwise.",
|
|
1271
|
+
},
|
|
1272
|
+
"key_uuid": {
|
|
1273
|
+
"type": "string",
|
|
1274
|
+
"description": "Key UUID (from list_keys or a11y_report / list_a11y_gaps items).",
|
|
1275
|
+
},
|
|
1276
|
+
"language_code": {
|
|
1277
|
+
"type": "string",
|
|
1278
|
+
"description": "Target locale CODE, e.g. 'fr' (the `locale` field of an a11y_report item).",
|
|
1279
|
+
},
|
|
1280
|
+
"surface": {
|
|
1281
|
+
"type": "string",
|
|
1282
|
+
"enum": list(A11Y_SURFACES),
|
|
1283
|
+
"description": "Which a11y surface to clear.",
|
|
1284
|
+
},
|
|
1285
|
+
},
|
|
1286
|
+
"required": ["key_uuid", "language_code", "surface"],
|
|
1287
|
+
"additionalProperties": False,
|
|
1288
|
+
},
|
|
1289
|
+
),
|
|
942
1290
|
]
|
|
943
1291
|
|
|
944
1292
|
@server.call_tool()
|
|
@@ -1131,7 +1479,7 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1131
1479
|
project_uuid = _project_uuid(args, client)
|
|
1132
1480
|
body: dict[str, Any] = {}
|
|
1133
1481
|
for opt in ("language_code", "namespace", "version_slug"):
|
|
1134
|
-
if
|
|
1482
|
+
if args.get(opt):
|
|
1135
1483
|
body[opt] = args[opt]
|
|
1136
1484
|
data = await client.post(
|
|
1137
1485
|
f"/v1/mcp/projects/{project_uuid}/cdn/releases", json=body
|
|
@@ -1273,6 +1621,131 @@ def register(server: Server, client: VerbumiaClient) -> tuple[Any, Any]:
|
|
|
1273
1621
|
f"/v1/mcp/projects/{project_uuid}/keys/trashed", params=params
|
|
1274
1622
|
)
|
|
1275
1623
|
return _text(data)
|
|
1624
|
+
if name == "a11y_estimate":
|
|
1625
|
+
project_uuid = _project_uuid(args, client)
|
|
1626
|
+
mode = args.get("mode")
|
|
1627
|
+
if mode not in ("generate", "translate"):
|
|
1628
|
+
return _err("mode must be 'generate' or 'translate'")
|
|
1629
|
+
body: dict[str, Any] = {"mode": mode, "tier": args.get("tier", "standard")}
|
|
1630
|
+
for opt in ("surfaces", "language_codes", "key_uuids", "namespace_uuid"):
|
|
1631
|
+
if args.get(opt):
|
|
1632
|
+
body[opt] = args[opt]
|
|
1633
|
+
data = await client.post(
|
|
1634
|
+
f"/v1/mcp/projects/{project_uuid}/a11y/estimate", json=body
|
|
1635
|
+
)
|
|
1636
|
+
return _text(data)
|
|
1637
|
+
if name == "generate_a11y_variant":
|
|
1638
|
+
project_uuid = _project_uuid(args, client)
|
|
1639
|
+
body: dict[str, Any] = {
|
|
1640
|
+
"tier": args.get("tier", "standard"),
|
|
1641
|
+
"idempotency_key": args.get("idempotency_key") or str(uuid.uuid4()),
|
|
1642
|
+
}
|
|
1643
|
+
for opt in ("surfaces", "key_uuids", "namespace_uuid"):
|
|
1644
|
+
if args.get(opt):
|
|
1645
|
+
body[opt] = args[opt]
|
|
1646
|
+
data = await client.post(
|
|
1647
|
+
f"/v1/mcp/projects/{project_uuid}/a11y/generate", json=body
|
|
1648
|
+
)
|
|
1649
|
+
return _text(data)
|
|
1650
|
+
if name == "translate_a11y_variants":
|
|
1651
|
+
project_uuid = _project_uuid(args, client)
|
|
1652
|
+
language_codes = args.get("language_codes")
|
|
1653
|
+
if not isinstance(language_codes, list) or not language_codes:
|
|
1654
|
+
return _err("language_codes must be a non-empty list")
|
|
1655
|
+
body: dict[str, Any] = {
|
|
1656
|
+
"tier": args.get("tier", "standard"),
|
|
1657
|
+
"language_codes": language_codes,
|
|
1658
|
+
"idempotency_key": args.get("idempotency_key") or str(uuid.uuid4()),
|
|
1659
|
+
}
|
|
1660
|
+
for opt in ("surfaces", "key_uuids", "namespace_uuid"):
|
|
1661
|
+
if args.get(opt):
|
|
1662
|
+
body[opt] = args[opt]
|
|
1663
|
+
data = await client.post(
|
|
1664
|
+
f"/v1/mcp/projects/{project_uuid}/a11y/translate", json=body
|
|
1665
|
+
)
|
|
1666
|
+
return _text(data)
|
|
1667
|
+
if name in ("a11y_report", "list_a11y_gaps"):
|
|
1668
|
+
project_uuid = _project_uuid(args, client)
|
|
1669
|
+
params: dict[str, Any] = {}
|
|
1670
|
+
if args.get("version_id"):
|
|
1671
|
+
params["version_id"] = args["version_id"]
|
|
1672
|
+
if args.get("require_surface"):
|
|
1673
|
+
# repeatable query param: httpx expands a list into repeats
|
|
1674
|
+
params["require_surface"] = args["require_surface"]
|
|
1675
|
+
if args.get("reading_level_max_words") is not None:
|
|
1676
|
+
params["reading_level_max_words"] = args["reading_level_max_words"]
|
|
1677
|
+
report = await client.get(
|
|
1678
|
+
f"/v1/mcp/projects/{project_uuid}/a11y-report", params=params
|
|
1679
|
+
)
|
|
1680
|
+
if name == "a11y_report":
|
|
1681
|
+
return _text(report)
|
|
1682
|
+
# list_a11y_gaps: flatten + client-side filter the items[] list.
|
|
1683
|
+
items = report.get("items", []) if isinstance(report, dict) else []
|
|
1684
|
+
gap_f = args.get("gap")
|
|
1685
|
+
surface_f = args.get("surface")
|
|
1686
|
+
locale_f = args.get("locale")
|
|
1687
|
+
gaps = [
|
|
1688
|
+
it
|
|
1689
|
+
for it in items
|
|
1690
|
+
if (gap_f is None or it.get("gap") == gap_f)
|
|
1691
|
+
and (surface_f is None or it.get("surface") == surface_f)
|
|
1692
|
+
and (locale_f is None or it.get("locale") == locale_f)
|
|
1693
|
+
]
|
|
1694
|
+
applied = {
|
|
1695
|
+
k: v
|
|
1696
|
+
for k, v in {
|
|
1697
|
+
"gap": gap_f,
|
|
1698
|
+
"surface": surface_f,
|
|
1699
|
+
"locale": locale_f,
|
|
1700
|
+
"require_surface": args.get("require_surface"),
|
|
1701
|
+
}.items()
|
|
1702
|
+
if v
|
|
1703
|
+
}
|
|
1704
|
+
return _text(
|
|
1705
|
+
{
|
|
1706
|
+
"project_uuid": report.get("project_uuid", project_uuid)
|
|
1707
|
+
if isinstance(report, dict)
|
|
1708
|
+
else project_uuid,
|
|
1709
|
+
"version_uuid": report.get("version_uuid")
|
|
1710
|
+
if isinstance(report, dict)
|
|
1711
|
+
else None,
|
|
1712
|
+
"keys_scanned": report.get("keys_scanned")
|
|
1713
|
+
if isinstance(report, dict)
|
|
1714
|
+
else None,
|
|
1715
|
+
"filters": applied,
|
|
1716
|
+
"count": len(gaps),
|
|
1717
|
+
"gaps": gaps,
|
|
1718
|
+
}
|
|
1719
|
+
)
|
|
1720
|
+
if name in ("set_a11y_variant", "delete_a11y_variant"):
|
|
1721
|
+
project_uuid = _project_uuid(args, client)
|
|
1722
|
+
key_uuid = args.get("key_uuid")
|
|
1723
|
+
language_code = args.get("language_code")
|
|
1724
|
+
surface = args.get("surface")
|
|
1725
|
+
if not key_uuid or not language_code or not surface:
|
|
1726
|
+
return _err("key_uuid, language_code and surface are required")
|
|
1727
|
+
if surface not in A11Y_SURFACES:
|
|
1728
|
+
return _err(f"surface must be one of {', '.join(A11Y_SURFACES)}")
|
|
1729
|
+
# mcp:* code-addressed route: backend resolves the locale code -> uuid
|
|
1730
|
+
# and validates the a11y surface server-side.
|
|
1731
|
+
path = (
|
|
1732
|
+
f"/v1/mcp/projects/{project_uuid}/keys/{key_uuid}"
|
|
1733
|
+
f"/a11y-variants/{language_code}/{surface}"
|
|
1734
|
+
)
|
|
1735
|
+
if name == "delete_a11y_variant":
|
|
1736
|
+
await client.delete(path)
|
|
1737
|
+
return _text(
|
|
1738
|
+
{
|
|
1739
|
+
"deleted": True,
|
|
1740
|
+
"key_uuid": key_uuid,
|
|
1741
|
+
"language_code": language_code,
|
|
1742
|
+
"surface": surface,
|
|
1743
|
+
}
|
|
1744
|
+
)
|
|
1745
|
+
if "value" not in args:
|
|
1746
|
+
return _err("value is required")
|
|
1747
|
+
data = await client.put(path, json={"value": args["value"]})
|
|
1748
|
+
return _text(data)
|
|
1276
1749
|
return _err(f"unknown tool: {name}")
|
|
1277
1750
|
except VerbumiaApiError as exc:
|
|
1278
1751
|
return _err(f"API {exc.status}: {exc.body}")
|
|
Binary file
|