@sonenta/mcp 0.34.0 → 0.34.1
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/bin/sonenta-mcp.js
CHANGED
|
@@ -32,32 +32,81 @@ function which(cmd) {
|
|
|
32
32
|
return !probe.error && probe.status === 0;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// A runner that exits non-zero almost immediately never actually started the
|
|
36
|
+
// server — the usual cause is a Python-interpreter mismatch (e.g. the wheel's
|
|
37
|
+
// requires-python floor is unmet, so uv reports the resolution "unsatisfiable").
|
|
38
|
+
// Below this many ms, a non-zero exit is treated as "this launcher can't run
|
|
39
|
+
// it" and we hand off to the next one instead of dying. A real server runs far
|
|
40
|
+
// longer (it blocks on stdin until the MCP client disconnects).
|
|
41
|
+
const FAST_FAIL_MS = 4000;
|
|
42
|
+
|
|
43
|
+
// Give uv the freedom to pick/provision a compatible interpreter rather than
|
|
44
|
+
// hard-failing on the ambient python3, and never silence its errors.
|
|
45
|
+
const UV_ENV = {
|
|
46
|
+
...process.env,
|
|
47
|
+
// Prefer a uv-managed interpreter, but still accept a compatible system one.
|
|
48
|
+
UV_PYTHON_PREFERENCE: process.env.UV_PYTHON_PREFERENCE || "managed",
|
|
49
|
+
// Allow uv to download a matching interpreter when none on PATH satisfies the
|
|
50
|
+
// wheel's requires-python (no-op in offline/locked-down envs — the lowered
|
|
51
|
+
// >=3.11 floor is what makes the common ambient interpreter satisfy).
|
|
52
|
+
UV_PYTHON_DOWNLOADS: process.env.UV_PYTHON_DOWNLOADS || "automatic",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Spawn a runner with stdio passthrough. Calls onFastFail(code) if the child
|
|
57
|
+
* dies non-zero within FAST_FAIL_MS (so the caller can try the next launcher);
|
|
58
|
+
* otherwise propagates the child's exit code. Runners fail fast BEFORE reading
|
|
59
|
+
* stdin, so stdin is untouched and handing off is safe.
|
|
60
|
+
*/
|
|
61
|
+
function runOrHandoff(cmd, args, env, onFastFail) {
|
|
62
|
+
const started = Date.now();
|
|
63
|
+
const child = spawn(cmd, args, { stdio: "inherit", env });
|
|
64
|
+
let settled = false;
|
|
65
|
+
child.on("error", (err) => {
|
|
66
|
+
if (settled) return;
|
|
67
|
+
settled = true;
|
|
68
|
+
process.stderr.write("@sonenta/mcp: " + cmd + " failed to start: " + err.message + "\n");
|
|
69
|
+
onFastFail(1);
|
|
70
|
+
});
|
|
71
|
+
child.on("exit", (code, signal) => {
|
|
72
|
+
if (settled) return;
|
|
73
|
+
settled = true;
|
|
74
|
+
const elapsed = Date.now() - started;
|
|
75
|
+
if (code === 0 || elapsed >= FAST_FAIL_MS || signal) {
|
|
76
|
+
process.exit(code ?? (signal ? 1 : 0));
|
|
77
|
+
}
|
|
78
|
+
process.stderr.write(
|
|
79
|
+
"@sonenta/mcp: " + cmd + " exited " + code + " after " + elapsed +
|
|
80
|
+
"ms (interpreter/launch problem) — trying the next launcher...\n",
|
|
81
|
+
);
|
|
82
|
+
onFastFail(code);
|
|
83
|
+
});
|
|
39
84
|
}
|
|
40
85
|
|
|
41
|
-
function tryUvx(passthroughArgs) {
|
|
86
|
+
function tryUvx(passthroughArgs, next) {
|
|
42
87
|
if (!which("uvx") || !BUNDLED_WHEEL) return false;
|
|
43
|
-
|
|
88
|
+
runOrHandoff("uvx", ["--from", BUNDLED_WHEEL, "sonenta-mcp", ...passthroughArgs], UV_ENV, next);
|
|
44
89
|
return true;
|
|
45
90
|
}
|
|
46
91
|
|
|
47
|
-
function tryUvRun(passthroughArgs) {
|
|
92
|
+
function tryUvRun(passthroughArgs, next) {
|
|
48
93
|
if (!which("uv")) return false;
|
|
49
|
-
|
|
94
|
+
runOrHandoff(
|
|
50
95
|
"uv",
|
|
51
96
|
["run", "--project", BUNDLED_PY_DIR, "sonenta-mcp", ...passthroughArgs],
|
|
97
|
+
UV_ENV,
|
|
98
|
+
next,
|
|
52
99
|
);
|
|
53
100
|
return true;
|
|
54
101
|
}
|
|
55
102
|
|
|
56
|
-
function tryPipxRun(passthroughArgs) {
|
|
103
|
+
function tryPipxRun(passthroughArgs, next) {
|
|
57
104
|
if (!which("pipx") || !BUNDLED_WHEEL) return false;
|
|
58
|
-
|
|
105
|
+
runOrHandoff(
|
|
59
106
|
"pipx",
|
|
60
107
|
["run", "--spec", BUNDLED_WHEEL, "sonenta-mcp", ...passthroughArgs],
|
|
108
|
+
process.env,
|
|
109
|
+
next,
|
|
61
110
|
);
|
|
62
111
|
return true;
|
|
63
112
|
}
|
|
@@ -68,7 +117,7 @@ function tryPipxRun(passthroughArgs) {
|
|
|
68
117
|
* first run; subsequent runs reuse the venv (keyed by wheel sha so a
|
|
69
118
|
* future sonenta-mcp version triggers a fresh venv automatically).
|
|
70
119
|
*/
|
|
71
|
-
function tryAdhocVenv(passthroughArgs) {
|
|
120
|
+
function tryAdhocVenv(passthroughArgs, next) {
|
|
72
121
|
if (!which("python3") || !BUNDLED_WHEEL) return false;
|
|
73
122
|
const wheelHash = crypto
|
|
74
123
|
.createHash("sha256")
|
|
@@ -93,14 +142,24 @@ function tryAdhocVenv(passthroughArgs) {
|
|
|
93
142
|
{ stdio: "inherit" },
|
|
94
143
|
);
|
|
95
144
|
} catch (err) {
|
|
96
|
-
process.stderr.write(
|
|
145
|
+
process.stderr.write(
|
|
146
|
+
"@sonenta/mcp: venv bootstrap failed (python3 " + pythonVersion() +
|
|
147
|
+
" may be older than the required >=3.11): " + err.message + "\n",
|
|
148
|
+
);
|
|
149
|
+
if (typeof next === "function") return next(1), true;
|
|
97
150
|
return false;
|
|
98
151
|
}
|
|
99
152
|
}
|
|
100
|
-
|
|
153
|
+
runOrHandoff(venvEntry, passthroughArgs, process.env, next || (() => process.exit(1)));
|
|
101
154
|
return true;
|
|
102
155
|
}
|
|
103
156
|
|
|
157
|
+
// Best-effort ambient python3 version string for diagnostics.
|
|
158
|
+
function pythonVersion() {
|
|
159
|
+
const probe = spawnSync("python3", ["--version"], { encoding: "utf8" });
|
|
160
|
+
return (probe.stdout || probe.stderr || "unknown").trim();
|
|
161
|
+
}
|
|
162
|
+
|
|
104
163
|
function selfCheck() {
|
|
105
164
|
const hits = [];
|
|
106
165
|
if (which("uvx")) hits.push("uvx");
|
|
@@ -119,11 +178,13 @@ function selfCheck() {
|
|
|
119
178
|
|
|
120
179
|
function fail() {
|
|
121
180
|
process.stderr.write(
|
|
122
|
-
"@sonenta/mcp:
|
|
123
|
-
"
|
|
124
|
-
"
|
|
181
|
+
"@sonenta/mcp: could not start the MCP server with any Python launcher.\n" +
|
|
182
|
+
"Every available launcher failed — the most common cause is an ambient\n" +
|
|
183
|
+
"python3 older than the required >=3.11 (detected: " + pythonVersion() + ").\n" +
|
|
184
|
+
"Install or expose one of these on PATH and retry:\n" +
|
|
185
|
+
" - uv (recommended, auto-provisions Python): https://docs.astral.sh/uv/\n" +
|
|
125
186
|
" - pipx https://pipx.pypa.io/\n" +
|
|
126
|
-
" - python3 (>=3.
|
|
187
|
+
" - python3 (>=3.11)\n",
|
|
127
188
|
);
|
|
128
189
|
process.exit(127);
|
|
129
190
|
}
|
|
@@ -132,11 +193,17 @@ function main() {
|
|
|
132
193
|
const argv = process.argv.slice(2);
|
|
133
194
|
if (argv.includes("--self-check")) return selfCheck();
|
|
134
195
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
196
|
+
// Chain the launchers: each hands off to the next when it can't actually run
|
|
197
|
+
// the server (e.g. Python-floor mismatch), so one unusable launcher on PATH
|
|
198
|
+
// never causes a silent total failure.
|
|
199
|
+
const chain = [tryUvx, tryUvRun, tryPipxRun, tryAdhocVenv];
|
|
200
|
+
const step = (i) => {
|
|
201
|
+
for (let j = i; j < chain.length; j++) {
|
|
202
|
+
if (chain[j](argv, () => step(j + 1))) return;
|
|
203
|
+
}
|
|
204
|
+
fail();
|
|
205
|
+
};
|
|
206
|
+
step(0);
|
|
140
207
|
}
|
|
141
208
|
|
|
142
209
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sonenta/mcp",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.1",
|
|
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",
|
package/python/dist/{sonenta_mcp-0.34.0-py3-none-any.whl → sonenta_mcp-0.34.1-py3-none-any.whl}
RENAMED
|
Binary file
|
package/python/pyproject.toml
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sonenta-mcp"
|
|
3
|
-
version = "0.34.
|
|
3
|
+
version = "0.34.1"
|
|
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
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.11,<3.14"
|
|
7
7
|
license = { text = "MIT" }
|
|
8
8
|
authors = [{ name = "Sonenta" }]
|
|
9
9
|
keywords = ["mcp", "sonenta", "i18n", "translations", "claude"]
|
|
10
10
|
classifiers = [
|
|
11
11
|
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Programming Language :: Python :: 3.11",
|
|
12
13
|
"Programming Language :: Python :: 3.12",
|
|
13
14
|
"Topic :: Software Development :: Internationalization",
|
|
14
15
|
]
|