mpp32-mcp-server 1.1.0 → 1.1.2
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/CHANGELOG.md +72 -0
- package/dist/index.js +199 -28
- package/package.json +4 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `mpp32-mcp-server` are documented here. The format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
|
+
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.1.2] - 2026-05-10
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
* **Configuration robustness.** Every environment variable is now trimmed,
|
|
12
|
+
stripped of a leading byte-order mark, and unwrapped if the value was
|
|
13
|
+
pasted with literal surrounding quotes. A trailing newline or stray
|
|
14
|
+
whitespace in `MPP32_AGENT_KEY` used to make Node's HTTP client throw
|
|
15
|
+
`ERR_INVALID_CHAR` on every catalog call. That failure mode is now
|
|
16
|
+
impossible.
|
|
17
|
+
* **ASCII-only header guard.** Any non-ASCII byte in a value that would
|
|
18
|
+
end up in an HTTP header is rejected at startup with a clear message
|
|
19
|
+
pointing at the offending variable, instead of a low-level fetch error
|
|
20
|
+
at call time.
|
|
21
|
+
* **Format validation.** `MPP32_AGENT_KEY` must look like
|
|
22
|
+
`mpp32_agent_*`. `MPP32_PRIVATE_KEY` must be a 0x-prefixed 64-character
|
|
23
|
+
EVM hex key. `MPP32_SOLANA_PRIVATE_KEY` is recognized as base58, hex,
|
|
24
|
+
or a JSON byte-array. Malformed values warn at startup with the first
|
|
25
|
+
hex bytes so invisible characters are easy to spot.
|
|
26
|
+
* **Case-insensitive payment challenge parsing.** `Payment-Required` and
|
|
27
|
+
`payment-required` headers are now treated identically.
|
|
28
|
+
* **Liberal `WWW-Authenticate` parser.** Token regex broadened to RFC
|
|
29
|
+
7235 token68 so valid challenges with `+`, `/`, `:`, and `,` no longer
|
|
30
|
+
get silently dropped.
|
|
31
|
+
* **`walletAddress` sanitization.** The optional `walletAddress` argument
|
|
32
|
+
to `get_solana_token_intelligence` is trimmed and ASCII-validated
|
|
33
|
+
before going into the `X-Wallet-Address` header.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
* **Request timeouts.** Every outbound fetch is wrapped with an
|
|
38
|
+
`AbortController`. Default 30 seconds, configurable through
|
|
39
|
+
`MPP32_TIMEOUT_MS` (1000-300000).
|
|
40
|
+
* **Startup banner.** The server prints the version, API base URL,
|
|
41
|
+
configured keys, and timeout to stderr on start, so misconfigurations
|
|
42
|
+
are visible without making a call.
|
|
43
|
+
|
|
44
|
+
### Internal
|
|
45
|
+
|
|
46
|
+
* `SERVER_VERSION` is now a single constant, so the protocol handshake,
|
|
47
|
+
banner, and `package.json` cannot drift apart.
|
|
48
|
+
|
|
49
|
+
## [1.1.1] - 2026-05-09
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
* `mcpName` field added to `package.json` to satisfy the MCP registry
|
|
54
|
+
publishing requirement.
|
|
55
|
+
* First registry listing under `io.github.MPP32/mpp32-mcp-server` at
|
|
56
|
+
`registry.modelcontextprotocol.io`.
|
|
57
|
+
|
|
58
|
+
## [1.1.0] - 2026-05-09
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
* Three tools: `list_mpp32_services`, `call_mpp32_endpoint`,
|
|
63
|
+
`get_solana_token_intelligence`.
|
|
64
|
+
* End to end support for five payment rails: x402, Tempo, ACP, AP2,
|
|
65
|
+
AGTP. Server picks the rail the wallet is funded for and falls back
|
|
66
|
+
if the first attempt does not settle.
|
|
67
|
+
* Federated catalog (native, curated free, x402 Bazaar, MCP registry).
|
|
68
|
+
* Automatic 402 sign-and-retry through `/api/agent/execute`.
|
|
69
|
+
|
|
70
|
+
## [1.0.0] - 2026-05-08
|
|
71
|
+
|
|
72
|
+
* Initial public release on npm.
|
package/dist/index.js
CHANGED
|
@@ -2,20 +2,165 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
5
|
+
const SERVER_VERSION = "1.1.2";
|
|
6
|
+
// ── Env loading: trim and sanitize aggressively ─────────────────────────────
|
|
7
|
+
// Copy-paste from Claude Desktop / Cursor / Windsurf JSON config UIs frequently
|
|
8
|
+
// adds trailing \n, \r, NBSP, BOM, or wraps the value in literal quotes. Any
|
|
9
|
+
// non-ASCII byte (including a stray newline) in a value that ends up in an
|
|
10
|
+
// HTTP header makes Node's undici fetch throw ERR_INVALID_CHAR, which used to
|
|
11
|
+
// surface as a confusing "invalid byte character" error on every catalog call.
|
|
12
|
+
function readEnv(name) {
|
|
13
|
+
const raw = process.env[name];
|
|
14
|
+
if (raw === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
let v = raw;
|
|
17
|
+
if (v.charCodeAt(0) === 0xfeff)
|
|
18
|
+
v = v.slice(1);
|
|
19
|
+
v = v.trim();
|
|
20
|
+
if (v.length >= 2 &&
|
|
21
|
+
((v.startsWith('"') && v.endsWith('"')) ||
|
|
22
|
+
(v.startsWith("'") && v.endsWith("'")))) {
|
|
23
|
+
v = v.slice(1, -1).trim();
|
|
24
|
+
}
|
|
25
|
+
if (v.length === 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
return v;
|
|
28
|
+
}
|
|
29
|
+
function isPrintableAscii(v) {
|
|
30
|
+
for (let i = 0; i < v.length; i++) {
|
|
31
|
+
const c = v.charCodeAt(i);
|
|
32
|
+
if (c < 0x20 || c > 0x7e)
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function safeHeaderValue(name, value) {
|
|
38
|
+
if (!isPrintableAscii(value)) {
|
|
39
|
+
throw new Error(`Environment variable contains non-printable or non-ASCII characters that cannot be sent as an HTTP header (${name}). ` +
|
|
40
|
+
`Re-copy the value from https://mpp32.org/agent-console without any surrounding whitespace, quotes, or newlines.`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function describeEnvProblem(name, raw, expected) {
|
|
45
|
+
const hex = Array.from(raw.slice(0, 4))
|
|
46
|
+
.map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
|
|
47
|
+
.join(" ");
|
|
48
|
+
return `${name} looks malformed. Expected ${expected}. First bytes: 0x${hex}. Re-copy from ${API_URL}/agent-console.`;
|
|
49
|
+
}
|
|
50
|
+
const RAW_API_URL = readEnv("MPP32_API_URL") ?? "https://mpp32.org";
|
|
51
|
+
const API_URL = (() => {
|
|
52
|
+
try {
|
|
53
|
+
const u = new URL(RAW_API_URL.replace(/\/+$/, ""));
|
|
54
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") {
|
|
55
|
+
throw new Error(`MPP32_API_URL must be http(s), got ${u.protocol}`);
|
|
56
|
+
}
|
|
57
|
+
return u.toString().replace(/\/+$/, "");
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error(`[mpp32] MPP32_API_URL is not a valid URL: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
61
|
+
`Falling back to https://mpp32.org.`);
|
|
62
|
+
return "https://mpp32.org";
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
// Default request timeout. Configurable via MPP32_TIMEOUT_MS.
|
|
66
|
+
const TIMEOUT_MS = (() => {
|
|
67
|
+
const raw = readEnv("MPP32_TIMEOUT_MS");
|
|
68
|
+
if (!raw)
|
|
69
|
+
return 30_000;
|
|
70
|
+
const n = Number.parseInt(raw, 10);
|
|
71
|
+
if (!Number.isFinite(n) || n < 1_000 || n > 300_000) {
|
|
72
|
+
console.error(`[mpp32] MPP32_TIMEOUT_MS=${raw} out of range. Using 30000ms.`);
|
|
73
|
+
return 30_000;
|
|
74
|
+
}
|
|
75
|
+
return n;
|
|
76
|
+
})();
|
|
77
|
+
// MPP32_AGENT_KEY is canonical; MPP32_API_KEY is an accepted alias from older docs.
|
|
78
|
+
const AGENT_KEY = (() => {
|
|
79
|
+
const v = readEnv("MPP32_AGENT_KEY") ?? readEnv("MPP32_API_KEY");
|
|
80
|
+
if (!v)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (!isPrintableAscii(v)) {
|
|
83
|
+
console.error(`[mpp32] MPP32_AGENT_KEY contains non-ASCII characters and will be ignored. ` +
|
|
84
|
+
`Re-copy the key from ${API_URL}/agent-console.`);
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
if (!/^mpp32_agent_[A-Za-z0-9_-]+$/.test(v)) {
|
|
88
|
+
console.error(`[mpp32] ${describeEnvProblem("MPP32_AGENT_KEY", v, "a value starting with 'mpp32_agent_'")}`);
|
|
89
|
+
}
|
|
90
|
+
return v;
|
|
91
|
+
})();
|
|
92
|
+
const PRIVATE_KEY = (() => {
|
|
93
|
+
const v = readEnv("MPP32_PRIVATE_KEY");
|
|
94
|
+
if (!v)
|
|
95
|
+
return undefined;
|
|
96
|
+
if (!isPrintableAscii(v)) {
|
|
97
|
+
console.error(`[mpp32] MPP32_PRIVATE_KEY contains non-ASCII characters and will be ignored. ` +
|
|
98
|
+
`Re-paste the hex key (0x-prefixed or 64 hex chars).`);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
if (!/^(0x)?[0-9a-fA-F]{64}$/.test(v)) {
|
|
102
|
+
console.error(`[mpp32] ${describeEnvProblem("MPP32_PRIVATE_KEY", v, "0x-prefixed 64-hex-char EVM private key")}`);
|
|
103
|
+
}
|
|
104
|
+
return v;
|
|
105
|
+
})();
|
|
106
|
+
const SOLANA_PRIVATE_KEY = (() => {
|
|
107
|
+
const v = readEnv("MPP32_SOLANA_PRIVATE_KEY");
|
|
108
|
+
if (!v)
|
|
109
|
+
return undefined;
|
|
110
|
+
if (!isPrintableAscii(v)) {
|
|
111
|
+
console.error(`[mpp32] MPP32_SOLANA_PRIVATE_KEY contains non-ASCII characters and will be ignored. ` +
|
|
112
|
+
`Re-paste the base58 (or [byte,byte,...] array, or hex) key.`);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const looksValid = v.startsWith("[") ||
|
|
116
|
+
/^[0-9a-fA-F]+$/.test(v) ||
|
|
117
|
+
/^[1-9A-HJ-NP-Za-km-z]{43,90}$/.test(v); // base58
|
|
118
|
+
if (!looksValid) {
|
|
119
|
+
console.error(`[mpp32] ${describeEnvProblem("MPP32_SOLANA_PRIVATE_KEY", v, "base58 string, hex string, or [byte,byte,...] array")}`);
|
|
120
|
+
}
|
|
121
|
+
return v;
|
|
122
|
+
})();
|
|
123
|
+
// Wrap fetch with a default timeout. AbortSignal.timeout exists in Node 20+,
|
|
124
|
+
// but we ship for Node 18+, so we build the signal ourselves.
|
|
125
|
+
async function fetchWithTimeout(url, init) {
|
|
126
|
+
const controller = new AbortController();
|
|
127
|
+
const timer = setTimeout(() => controller.abort(), init?.timeoutMs ?? TIMEOUT_MS);
|
|
128
|
+
try {
|
|
129
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
133
|
+
throw new Error(`Request to ${url} timed out after ${init?.timeoutMs ?? TIMEOUT_MS}ms. ` +
|
|
134
|
+
`Set MPP32_TIMEOUT_MS in your MCP config to extend.`);
|
|
135
|
+
}
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Lowercase all keys in a headers-like object. The backend may emit
|
|
143
|
+
// "Payment-Required" or "payment-required"; downstream code must not care.
|
|
144
|
+
function lowercaseHeaderKeys(obj) {
|
|
145
|
+
if (!obj)
|
|
146
|
+
return {};
|
|
147
|
+
const out = {};
|
|
148
|
+
for (const [k, v] of Object.entries(obj))
|
|
149
|
+
out[k.toLowerCase()] = v;
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
11
152
|
const server = new McpServer({
|
|
12
153
|
name: "mpp32",
|
|
13
|
-
version:
|
|
154
|
+
version: SERVER_VERSION,
|
|
14
155
|
});
|
|
15
156
|
function buildHeaders(extra = {}) {
|
|
16
|
-
const headers = {
|
|
17
|
-
|
|
18
|
-
headers[
|
|
157
|
+
const headers = {};
|
|
158
|
+
for (const [k, v] of Object.entries(extra)) {
|
|
159
|
+
headers[k] = safeHeaderValue(k, v);
|
|
160
|
+
}
|
|
161
|
+
if (AGENT_KEY) {
|
|
162
|
+
headers["X-Agent-Key"] = safeHeaderValue("MPP32_AGENT_KEY", AGENT_KEY);
|
|
163
|
+
}
|
|
19
164
|
return headers;
|
|
20
165
|
}
|
|
21
166
|
function isHttpCallable(svc) {
|
|
@@ -59,7 +204,7 @@ server.tool("list_mpp32_services", "Browse the MPP32 federated catalog of machin
|
|
|
59
204
|
if (source)
|
|
60
205
|
url.searchParams.set("source", source);
|
|
61
206
|
url.searchParams.set("limit", String(limit ?? 100));
|
|
62
|
-
const res = await
|
|
207
|
+
const res = await fetchWithTimeout(url.toString(), { headers: buildHeaders() });
|
|
63
208
|
if (!res.ok) {
|
|
64
209
|
return {
|
|
65
210
|
content: [
|
|
@@ -194,7 +339,7 @@ async function callViaAgentExecute(service, method, body, query) {
|
|
|
194
339
|
...(query ? { query } : {}),
|
|
195
340
|
});
|
|
196
341
|
// Round 1: no payment headers
|
|
197
|
-
const firstRes = await
|
|
342
|
+
const firstRes = await fetchWithTimeout(execUrl, {
|
|
198
343
|
method: "POST",
|
|
199
344
|
headers: buildHeaders({ "Content-Type": "application/json" }),
|
|
200
345
|
body: reqBody,
|
|
@@ -231,7 +376,7 @@ function detectPaymentRequired(resp) {
|
|
|
231
376
|
const result = resp?.data?.result;
|
|
232
377
|
if (!result?.error || result.error.code !== "PAYMENT_REQUIRED")
|
|
233
378
|
return null;
|
|
234
|
-
const headers = result.error.challenge?.headers
|
|
379
|
+
const headers = lowercaseHeaderKeys(result.error.challenge?.headers);
|
|
235
380
|
return {
|
|
236
381
|
wwwAuthenticate: headers["www-authenticate"],
|
|
237
382
|
paymentRequired: headers["payment-required"],
|
|
@@ -308,7 +453,7 @@ async function signAndRetry(execUrl, reqBody, challenge) {
|
|
|
308
453
|
};
|
|
309
454
|
}
|
|
310
455
|
// Round 2: with payment headers
|
|
311
|
-
const secondRes = await
|
|
456
|
+
const secondRes = await fetchWithTimeout(execUrl, {
|
|
312
457
|
method: "POST",
|
|
313
458
|
headers: buildHeaders({ "Content-Type": "application/json", ...paymentHeaders }),
|
|
314
459
|
body: reqBody,
|
|
@@ -437,7 +582,9 @@ function paymentKeyMissingMessage(resp, challenge) {
|
|
|
437
582
|
' "command": "npx",',
|
|
438
583
|
' "args": ["mpp32-mcp-server"],',
|
|
439
584
|
' "env": {',
|
|
440
|
-
AGENT_KEY
|
|
585
|
+
AGENT_KEY
|
|
586
|
+
? ` "MPP32_AGENT_KEY": "${AGENT_KEY.slice(0, 12).replace(/[^A-Za-z0-9_-]/g, "?")}…",`
|
|
587
|
+
: "",
|
|
441
588
|
' "MPP32_SOLANA_PRIVATE_KEY": "<solana-base58-key for USDC>",',
|
|
442
589
|
' "MPP32_PRIVATE_KEY": "<EVM-hex-key for pathUSD>"',
|
|
443
590
|
" }",
|
|
@@ -477,7 +624,7 @@ async function callViaLegacyProxy(slug, method, body, query) {
|
|
|
477
624
|
// Without an agent key, only native /api/proxy/<slug> is reachable.
|
|
478
625
|
// We fetch /info first to detect that the slug exists as a native service.
|
|
479
626
|
const infoUrl = new URL(`/api/proxy/${encodeURIComponent(slug)}/info`, API_URL).toString();
|
|
480
|
-
const infoRes = await
|
|
627
|
+
const infoRes = await fetchWithTimeout(infoUrl);
|
|
481
628
|
if (!infoRes.ok) {
|
|
482
629
|
return {
|
|
483
630
|
content: [
|
|
@@ -500,7 +647,7 @@ async function callViaLegacyProxy(slug, method, body, query) {
|
|
|
500
647
|
const baseHeaders = { Accept: "application/json" };
|
|
501
648
|
if (body !== undefined)
|
|
502
649
|
baseHeaders["Content-Type"] = "application/json";
|
|
503
|
-
const challengeRes = await
|
|
650
|
+
const challengeRes = await fetchWithTimeout(proxyUrl.toString(), {
|
|
504
651
|
method,
|
|
505
652
|
headers: baseHeaders,
|
|
506
653
|
body: method !== "GET" && body !== undefined ? JSON.stringify(body) : undefined,
|
|
@@ -592,7 +739,7 @@ async function callViaLegacyProxy(slug, method, body, query) {
|
|
|
592
739
|
],
|
|
593
740
|
};
|
|
594
741
|
}
|
|
595
|
-
const paidRes = await
|
|
742
|
+
const paidRes = await fetchWithTimeout(proxyUrl.toString(), {
|
|
596
743
|
method,
|
|
597
744
|
headers: { ...baseHeaders, ...paymentHeaders },
|
|
598
745
|
body: method !== "GET" && body !== undefined ? JSON.stringify(body) : undefined,
|
|
@@ -628,9 +775,21 @@ async function callViaLegacyProxy(slug, method, body, query) {
|
|
|
628
775
|
async function legacyIntelligenceCall(token, walletAddress) {
|
|
629
776
|
try {
|
|
630
777
|
const reqHeaders = { "Content-Type": "application/json" };
|
|
631
|
-
if (walletAddress)
|
|
632
|
-
|
|
633
|
-
|
|
778
|
+
if (walletAddress) {
|
|
779
|
+
const trimmed = walletAddress.trim();
|
|
780
|
+
if (!isPrintableAscii(trimmed)) {
|
|
781
|
+
return {
|
|
782
|
+
content: [
|
|
783
|
+
{
|
|
784
|
+
type: "text",
|
|
785
|
+
text: `walletAddress contains non-ASCII characters. Pass a Solana base58 address only.`,
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
reqHeaders["X-Wallet-Address"] = trimmed;
|
|
791
|
+
}
|
|
792
|
+
const res = await fetchWithTimeout(`${API_URL}/api/intelligence`, {
|
|
634
793
|
method: "POST",
|
|
635
794
|
headers: reqHeaders,
|
|
636
795
|
body: JSON.stringify({ token }),
|
|
@@ -723,7 +882,7 @@ async function legacyIntelligenceCall(token, walletAddress) {
|
|
|
723
882
|
};
|
|
724
883
|
}
|
|
725
884
|
}
|
|
726
|
-
const paidRes = await
|
|
885
|
+
const paidRes = await fetchWithTimeout(`${API_URL}/api/intelligence`, {
|
|
727
886
|
method: "POST",
|
|
728
887
|
headers: { ...reqHeaders, ...paymentHeaders },
|
|
729
888
|
body: JSON.stringify({ token }),
|
|
@@ -762,13 +921,19 @@ function parseWwwAuthenticate(header) {
|
|
|
762
921
|
const match = header.match(/^(\w+)\s+(.+)$/);
|
|
763
922
|
if (!match)
|
|
764
923
|
return { scheme: null, params: {} };
|
|
765
|
-
const scheme = match[1];
|
|
766
|
-
const rest = match[2];
|
|
924
|
+
const scheme = match[1] ?? null;
|
|
925
|
+
const rest = match[2] ?? "";
|
|
767
926
|
const params = {};
|
|
768
|
-
|
|
927
|
+
// Tokens per RFC 7235: quoted-string OR a token68-ish value covering all
|
|
928
|
+
// base64url, base58, hex, JSON-pointers, etc. Liberal on purpose so we do
|
|
929
|
+
// not silently drop valid challenges.
|
|
930
|
+
const paramRegex = /([A-Za-z0-9_-]+)=(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))/g;
|
|
769
931
|
let m;
|
|
770
932
|
while ((m = paramRegex.exec(rest)) !== null) {
|
|
771
|
-
|
|
933
|
+
const key = m[1];
|
|
934
|
+
const val = m[2] ?? m[3];
|
|
935
|
+
if (key && val !== undefined)
|
|
936
|
+
params[key] = val;
|
|
772
937
|
}
|
|
773
938
|
return { scheme, params };
|
|
774
939
|
}
|
|
@@ -865,8 +1030,14 @@ async function completeX402Payment(paymentRequiredHeader, solanaPrivateKey) {
|
|
|
865
1030
|
async function main() {
|
|
866
1031
|
const transport = new StdioServerTransport();
|
|
867
1032
|
await server.connect(transport);
|
|
868
|
-
const
|
|
869
|
-
|
|
1033
|
+
const features = [
|
|
1034
|
+
AGENT_KEY ? "agent-key" : null,
|
|
1035
|
+
SOLANA_PRIVATE_KEY ? "x402-key" : null,
|
|
1036
|
+
PRIVATE_KEY ? "tempo-key" : null,
|
|
1037
|
+
]
|
|
1038
|
+
.filter(Boolean)
|
|
1039
|
+
.join(", ") || "no keys (catalog-only legacy mode)";
|
|
1040
|
+
console.error(`[mpp32] MCP server v${SERVER_VERSION} on stdio. API ${API_URL}. Configured: ${features}. Timeout ${TIMEOUT_MS}ms.`);
|
|
870
1041
|
}
|
|
871
1042
|
main().catch((err) => {
|
|
872
1043
|
console.error("Fatal:", err);
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mpp32-mcp-server",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"mcpName": "io.github.MPP32/mpp32-mcp-server",
|
|
5
|
+
"description": "Payment layer for AI agents. One MCP, five protocols, thousands of paid APIs your agent can call.",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|
|
7
8
|
"mpp32-mcp-server": "./dist/index.js"
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"dist/**/*.js",
|
|
19
20
|
"dist/**/*.d.ts",
|
|
20
21
|
"README.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
21
23
|
"LICENSE"
|
|
22
24
|
],
|
|
23
25
|
"sideEffects": false,
|