@agentvalet/mcp-server 0.3.9 → 0.3.10
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/dist/bind.js +26 -8
- package/dist/config.js +20 -4
- package/dist/pem.js +22 -6
- package/package.json +1 -1
package/dist/bind.js
CHANGED
|
@@ -79,14 +79,32 @@ export async function attemptInviteBind(opts) {
|
|
|
79
79
|
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
80
80
|
});
|
|
81
81
|
const url = `${opts.proxyUrl.replace(/\/$/, "")}/v1/invites/bind`;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
// 15s hard timeout: Claude Desktop kills the transport at ~12-15s of
|
|
83
|
+
// silence. If the proxy hangs, fail visibly with stderr rather than
|
|
84
|
+
// letting the host record an unexplained "transport closed".
|
|
85
|
+
const ac = new AbortController();
|
|
86
|
+
const timer = setTimeout(() => ac.abort(), 15_000);
|
|
87
|
+
let res;
|
|
88
|
+
try {
|
|
89
|
+
res = await fetch(url, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: { "Content-Type": "application/json" },
|
|
92
|
+
body: JSON.stringify({
|
|
93
|
+
bind_secret: opts.bindSecret,
|
|
94
|
+
public_key_pem: publicKey,
|
|
95
|
+
}),
|
|
96
|
+
signal: ac.signal,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
101
|
+
throw new Error("Bind request timed out after 15s — proxy unreachable or slow.");
|
|
102
|
+
}
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
}
|
|
90
108
|
if (!res.ok) {
|
|
91
109
|
let detail = `HTTP ${res.status}`;
|
|
92
110
|
try {
|
package/dist/config.js
CHANGED
|
@@ -20,11 +20,27 @@ const DEFAULT_PROXY_URL = "https://api.agentvalet.ai";
|
|
|
20
20
|
* then invite-bind. The bind path runs at most once per machine —
|
|
21
21
|
* the secret is consumed after first use.
|
|
22
22
|
*/
|
|
23
|
+
// Treat empty strings AND unresolved MCPB template placeholders (e.g.
|
|
24
|
+
// "${user_config.agent_id}") as "not set". Claude Desktop on some platforms
|
|
25
|
+
// passes the literal placeholder through when a user_config field has no
|
|
26
|
+
// value and no manifest default — that would otherwise false-trigger the
|
|
27
|
+
// env-based Path 1 below and skip the invite-bind handshake.
|
|
28
|
+
function envOrNull(name) {
|
|
29
|
+
const v = process.env[name];
|
|
30
|
+
if (v === undefined)
|
|
31
|
+
return null;
|
|
32
|
+
const t = v.trim();
|
|
33
|
+
if (t === "")
|
|
34
|
+
return null;
|
|
35
|
+
if (t.startsWith("${") && t.endsWith("}"))
|
|
36
|
+
return null;
|
|
37
|
+
return t;
|
|
38
|
+
}
|
|
23
39
|
export async function validateConfig() {
|
|
24
40
|
// Path 1 — env-based (legacy).
|
|
25
|
-
const envAgentId =
|
|
26
|
-
const envOwnerId =
|
|
27
|
-
const envProxyUrl =
|
|
41
|
+
const envAgentId = envOrNull("AGENT_ID");
|
|
42
|
+
const envOwnerId = envOrNull("OWNER_ID");
|
|
43
|
+
const envProxyUrl = envOrNull("PROXY_URL");
|
|
28
44
|
if (envAgentId && envOwnerId && envProxyUrl) {
|
|
29
45
|
return buildConfig({
|
|
30
46
|
agentId: envAgentId,
|
|
@@ -43,7 +59,7 @@ export async function validateConfig() {
|
|
|
43
59
|
});
|
|
44
60
|
}
|
|
45
61
|
// Path 3 — first-run invite bind.
|
|
46
|
-
const inviteBindSecret =
|
|
62
|
+
const inviteBindSecret = envOrNull("INVITE_BIND_SECRET");
|
|
47
63
|
if (inviteBindSecret) {
|
|
48
64
|
const proxyUrl = (envProxyUrl ?? DEFAULT_PROXY_URL).replace(/\/$/, "");
|
|
49
65
|
process.stderr.write(`[mcp-server] First-run invite bind against ${proxyUrl}…\n`);
|
package/dist/pem.js
CHANGED
|
@@ -12,23 +12,39 @@ import { readBoundPrivateKey } from "./bind.js";
|
|
|
12
12
|
* having pem.ts honour it means subsequent invocations of the
|
|
13
13
|
* mcp-server don't need any env vars at all.
|
|
14
14
|
*/
|
|
15
|
+
// Treat empty + unresolved MCPB placeholders ("${user_config.foo}") as
|
|
16
|
+
// not-set — see config.ts for the same defence on identity envs.
|
|
17
|
+
function readEnv(name) {
|
|
18
|
+
const v = process.env[name];
|
|
19
|
+
if (v === undefined)
|
|
20
|
+
return null;
|
|
21
|
+
const t = v.trim();
|
|
22
|
+
if (t === "")
|
|
23
|
+
return null;
|
|
24
|
+
if (t.startsWith("${") && t.endsWith("}"))
|
|
25
|
+
return null;
|
|
26
|
+
return t;
|
|
27
|
+
}
|
|
15
28
|
export function readPrivateKeyFromEnv() {
|
|
16
29
|
// 1. Base64-encoded PEM
|
|
17
|
-
|
|
18
|
-
|
|
30
|
+
const b64 = readEnv("AGENT_PRIVATE_KEY_B64");
|
|
31
|
+
if (b64) {
|
|
32
|
+
return Buffer.from(b64, "base64").toString("utf-8").trim();
|
|
19
33
|
}
|
|
20
34
|
// 2. Path to PEM file
|
|
21
|
-
|
|
35
|
+
const path = readEnv("AGENT_PRIVATE_KEY_PATH");
|
|
36
|
+
if (path) {
|
|
22
37
|
try {
|
|
23
|
-
return readFileSync(
|
|
38
|
+
return readFileSync(path, "utf-8").trim();
|
|
24
39
|
}
|
|
25
40
|
catch (err) {
|
|
26
41
|
throw new Error(`Cannot read AGENT_PRIVATE_KEY_PATH: ${err instanceof Error ? err.message : err}`);
|
|
27
42
|
}
|
|
28
43
|
}
|
|
29
44
|
// 3. Raw PEM content (multi-line or \n-escaped)
|
|
30
|
-
|
|
31
|
-
|
|
45
|
+
const rawEnv = readEnv("AGENT_PRIVATE_KEY");
|
|
46
|
+
if (rawEnv) {
|
|
47
|
+
const raw = rawEnv;
|
|
32
48
|
// Unescape \n sequences
|
|
33
49
|
const unescaped = raw.replace(/\\n/g, "\n");
|
|
34
50
|
// Wrap bare base64 blob without headers
|