@agentwonderland/mcp 0.1.40 → 0.1.42
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/core/__tests__/api-client.test.js +1 -1
- package/dist/core/__tests__/setup.test.d.ts +1 -0
- package/dist/core/__tests__/setup.test.js +72 -0
- package/dist/core/api-client.js +1 -1
- package/dist/core/ows-adapter.js +58 -3
- package/dist/core/payments.js +52 -41
- package/dist/index.js +27 -17
- package/dist/setup.d.ts +36 -0
- package/dist/setup.js +346 -0
- package/dist/tools/wallet.js +14 -0
- package/package.json +3 -2
- package/src/core/__tests__/api-client.test.ts +1 -1
- package/src/core/__tests__/setup.test.ts +88 -0
- package/src/core/api-client.ts +1 -1
- package/src/core/ows-adapter.ts +67 -3
- package/src/core/payments.ts +52 -40
- package/src/index.ts +27 -22
- package/src/setup.ts +411 -0
- package/src/tools/wallet.ts +19 -5
package/src/core/payments.ts
CHANGED
|
@@ -76,52 +76,44 @@ async function initEvmMppForChain(
|
|
|
76
76
|
wallet: WalletEntry,
|
|
77
77
|
chain: "tempo" | "base",
|
|
78
78
|
): Promise<typeof fetch | null> {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
let account;
|
|
82
|
-
|
|
83
|
-
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
84
|
-
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
85
|
-
account = await owsAccountFromWalletId(wallet.owsWalletId);
|
|
86
|
-
} else if (wallet.key) {
|
|
87
|
-
const { privateKeyToAccount } = await import("viem/accounts");
|
|
88
|
-
account = privateKeyToAccount(normalizeKey(wallet.key));
|
|
89
|
-
} else {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const methods = [];
|
|
94
|
-
if (chain === "tempo") {
|
|
95
|
-
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
96
|
-
methods.push(tempoChargeClient({ account }));
|
|
97
|
-
} else {
|
|
98
|
-
const { baseChargeClient } = await import("./base-charge.js");
|
|
99
|
-
methods.push(baseChargeClient({ account }));
|
|
100
|
-
}
|
|
79
|
+
const { Mppx } = await import("./mpp-client.js");
|
|
80
|
+
let account;
|
|
101
81
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
82
|
+
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
83
|
+
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
84
|
+
account = await owsAccountFromWalletId(wallet.owsWalletId);
|
|
85
|
+
} else if (wallet.key) {
|
|
86
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
87
|
+
account = privateKeyToAccount(normalizeKey(wallet.key));
|
|
88
|
+
} else {
|
|
105
89
|
return null;
|
|
106
90
|
}
|
|
91
|
+
|
|
92
|
+
const methods = [];
|
|
93
|
+
if (chain === "tempo") {
|
|
94
|
+
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
95
|
+
methods.push(tempoChargeClient({ account }));
|
|
96
|
+
} else {
|
|
97
|
+
const { baseChargeClient } = await import("./base-charge.js");
|
|
98
|
+
methods.push(baseChargeClient({ account }));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const mppx = Mppx.create({ methods: methods as any, polyfill: false });
|
|
102
|
+
return mppx.fetch.bind(mppx) as typeof fetch;
|
|
107
103
|
}
|
|
108
104
|
|
|
109
105
|
async function initSolanaMpp(wallet: WalletEntry): Promise<typeof fetch | null> {
|
|
110
|
-
|
|
111
|
-
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const { Mppx } = await import("./mpp-client.js");
|
|
116
|
-
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
117
|
-
const mppx = Mppx.create({
|
|
118
|
-
methods: [solanaChargeClient({ wallet })] as any,
|
|
119
|
-
polyfill: false,
|
|
120
|
-
});
|
|
121
|
-
return mppx.fetch.bind(mppx) as typeof fetch;
|
|
122
|
-
} catch {
|
|
106
|
+
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
123
107
|
return null;
|
|
124
108
|
}
|
|
109
|
+
|
|
110
|
+
const { Mppx } = await import("./mpp-client.js");
|
|
111
|
+
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
112
|
+
const mppx = Mppx.create({
|
|
113
|
+
methods: [solanaChargeClient({ wallet })] as any,
|
|
114
|
+
polyfill: false,
|
|
115
|
+
});
|
|
116
|
+
return mppx.fetch.bind(mppx) as typeof fetch;
|
|
125
117
|
}
|
|
126
118
|
|
|
127
119
|
async function initCard(): Promise<typeof fetch | null> {
|
|
@@ -178,6 +170,11 @@ async function initForChain(wallet: WalletEntry, chain: string): Promise<typeof
|
|
|
178
170
|
return null;
|
|
179
171
|
}
|
|
180
172
|
|
|
173
|
+
function initFailureMessage(method: string, wallet: WalletEntry, chain: string, err: unknown): string {
|
|
174
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
175
|
+
return `Payment method "${method}" is configured as ${chain} on wallet "${wallet.id}", but the signer could not initialize: ${reason}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
181
178
|
// ── Public API ──────────────────────────────────────────────────
|
|
182
179
|
|
|
183
180
|
/**
|
|
@@ -214,7 +211,12 @@ export async function getPaymentFetch(method?: string): Promise<typeof fetch> {
|
|
|
214
211
|
}
|
|
215
212
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
216
213
|
if (fetchCache.has(ck)) return fetchCache.get(ck)!;
|
|
217
|
-
|
|
214
|
+
let pf: typeof fetch | null;
|
|
215
|
+
try {
|
|
216
|
+
pf = await initForChain(resolved.wallet, resolved.chain);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
throw new Error(initFailureMessage(method, resolved.wallet, resolved.chain, err));
|
|
219
|
+
}
|
|
218
220
|
if (pf) {
|
|
219
221
|
fetchCache.set(ck, pf);
|
|
220
222
|
return pf;
|
|
@@ -264,7 +266,17 @@ export async function getPaymentFetch(method?: string): Promise<typeof fetch> {
|
|
|
264
266
|
}
|
|
265
267
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
266
268
|
if (fetchCache.has(ck)) return fetchCache.get(ck)!;
|
|
267
|
-
|
|
269
|
+
let pf: typeof fetch | null;
|
|
270
|
+
try {
|
|
271
|
+
pf = await initForChain(resolved.wallet, resolved.chain);
|
|
272
|
+
} catch (err) {
|
|
273
|
+
if (m === defaultMethod) {
|
|
274
|
+
const others = configured.filter((x) => x !== m);
|
|
275
|
+
const altText = others.length > 0 ? ` Alternatives: ${others.join(", ")}` : "";
|
|
276
|
+
throw new Error(`${initFailureMessage(m, resolved.wallet, resolved.chain, err)}.${altText}`);
|
|
277
|
+
}
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
268
280
|
if (pf) {
|
|
269
281
|
fetchCache.set(ck, pf);
|
|
270
282
|
return pf;
|
package/src/index.ts
CHANGED
|
@@ -25,8 +25,9 @@ import { registerJobResources } from "./resources/jobs.js";
|
|
|
25
25
|
|
|
26
26
|
// ── Prompts ──────────────────────────────────────────────────────
|
|
27
27
|
import { registerPrompts } from "./prompts/index.js";
|
|
28
|
+
import { runSetupCli } from "./setup.js";
|
|
28
29
|
|
|
29
|
-
const PACKAGE_VERSION = "0.1.
|
|
30
|
+
const PACKAGE_VERSION = "0.1.42";
|
|
30
31
|
|
|
31
32
|
export async function startMcpServer(): Promise<void> {
|
|
32
33
|
const server = new McpServer(
|
|
@@ -112,43 +113,47 @@ const isCli =
|
|
|
112
113
|
if (isCli) {
|
|
113
114
|
const args = process.argv.slice(2);
|
|
114
115
|
|
|
115
|
-
if (args
|
|
116
|
+
if (args[0] === "setup" || args[0] === "install" || args[0] === "configure") {
|
|
117
|
+
runSetupCli(args.slice(1)).catch((err) => {
|
|
118
|
+
console.error("Setup failed:", err instanceof Error ? err.message : err);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
|
121
|
+
} else if (args.includes("--help") || args.includes("-h")) {
|
|
116
122
|
console.log([
|
|
117
123
|
"Agent Wonderland MCP server",
|
|
118
124
|
"",
|
|
119
125
|
"Usage:",
|
|
120
|
-
" npx @agentwonderland/mcp",
|
|
126
|
+
" npx @agentwonderland/mcp setup Configure MCP clients",
|
|
127
|
+
" npx @agentwonderland/mcp Start the stdio MCP server",
|
|
121
128
|
"",
|
|
122
|
-
"
|
|
129
|
+
"The bare command starts a stdio MCP server. It is meant to be launched by",
|
|
123
130
|
"Codex, Cursor, Claude Desktop, or another MCP client. When run directly",
|
|
124
131
|
"in a terminal it waits silently for MCP JSON-RPC messages on stdin.",
|
|
125
132
|
"",
|
|
126
133
|
"MCP client config:",
|
|
127
|
-
' { "mcpServers": { "agentwonderland": { "command": "npx", "args": ["@agentwonderland/mcp"] } } }',
|
|
134
|
+
' { "mcpServers": { "agentwonderland": { "command": "npx", "args": ["-y", "@agentwonderland/mcp"] } } }',
|
|
128
135
|
"",
|
|
129
136
|
"Options:",
|
|
130
137
|
" -h, --help Show this help",
|
|
131
138
|
" -v, --version Show package version",
|
|
132
139
|
].join("\n"));
|
|
133
140
|
process.exit(0);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (args.includes("--version") || args.includes("-v")) {
|
|
141
|
+
} else if (args.includes("--version") || args.includes("-v")) {
|
|
137
142
|
console.log(PACKAGE_VERSION);
|
|
138
143
|
process.exit(0);
|
|
144
|
+
} else {
|
|
145
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
146
|
+
console.error([
|
|
147
|
+
"Agent Wonderland MCP server is running.",
|
|
148
|
+
"This process speaks MCP over stdio, so it waits for a client instead of printing a prompt.",
|
|
149
|
+
"Add it to an MCP client config, or press Ctrl+C to stop.",
|
|
150
|
+
"Run `npx @agentwonderland/mcp setup` to configure your MCP clients.",
|
|
151
|
+
].join("\n"));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
startMcpServer().catch((err) => {
|
|
155
|
+
console.error("MCP server error:", err);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
});
|
|
139
158
|
}
|
|
140
|
-
|
|
141
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
142
|
-
console.error([
|
|
143
|
-
"Agent Wonderland MCP server is running.",
|
|
144
|
-
"This process speaks MCP over stdio, so it waits for a client instead of printing a prompt.",
|
|
145
|
-
"Add it to an MCP client config, or press Ctrl+C to stop.",
|
|
146
|
-
"Run `npx @agentwonderland/mcp --help` for setup details.",
|
|
147
|
-
].join("\n"));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
startMcpServer().catch((err) => {
|
|
151
|
-
console.error("MCP server error:", err);
|
|
152
|
-
process.exit(1);
|
|
153
|
-
});
|
|
154
159
|
}
|
package/src/setup.ts
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir, platform } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { createInterface } from "node:readline/promises";
|
|
6
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
|
|
9
|
+
export const SERVER_NAME = "agentwonderland";
|
|
10
|
+
export const SERVER_CONFIG = {
|
|
11
|
+
command: "npx",
|
|
12
|
+
args: ["-y", "@agentwonderland/mcp"],
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type SetupClientId =
|
|
16
|
+
| "codex"
|
|
17
|
+
| "claude-code"
|
|
18
|
+
| "claude-desktop"
|
|
19
|
+
| "cursor"
|
|
20
|
+
| "antigravity";
|
|
21
|
+
|
|
22
|
+
type ConfigFormat = "json" | "toml" | "claude-code-cli";
|
|
23
|
+
|
|
24
|
+
export type SetupClient = {
|
|
25
|
+
id: SetupClientId;
|
|
26
|
+
name: string;
|
|
27
|
+
path: string;
|
|
28
|
+
format: ConfigFormat;
|
|
29
|
+
detected: boolean;
|
|
30
|
+
note: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type SetupOptions = {
|
|
34
|
+
all?: boolean;
|
|
35
|
+
yes?: boolean;
|
|
36
|
+
clients?: SetupClientId[];
|
|
37
|
+
dryRun?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type InstallResult = {
|
|
41
|
+
client: SetupClient;
|
|
42
|
+
status: "created" | "updated" | "unchanged" | "skipped" | "failed";
|
|
43
|
+
backupPath?: string;
|
|
44
|
+
message?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const CLIENT_ORDER: SetupClientId[] = [
|
|
48
|
+
"codex",
|
|
49
|
+
"claude-code",
|
|
50
|
+
"claude-desktop",
|
|
51
|
+
"cursor",
|
|
52
|
+
"antigravity",
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const CLIENT_LABELS: Record<SetupClientId, string> = {
|
|
56
|
+
codex: "Codex",
|
|
57
|
+
"claude-code": "Claude Code",
|
|
58
|
+
"claude-desktop": "Claude Desktop",
|
|
59
|
+
cursor: "Cursor",
|
|
60
|
+
antigravity: "Google Antigravity",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export async function runSetupCli(args: string[] = process.argv.slice(2)): Promise<void> {
|
|
64
|
+
const options = parseSetupArgs(args);
|
|
65
|
+
if (options.help) {
|
|
66
|
+
printSetupHelp();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const clients = getSetupClients();
|
|
71
|
+
const selected = await chooseClients(clients, options);
|
|
72
|
+
|
|
73
|
+
if (selected.length === 0) {
|
|
74
|
+
console.log("No MCP clients selected. Nothing changed.");
|
|
75
|
+
printManualConfig();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log("");
|
|
80
|
+
console.log("Agent Wonderland will add this MCP server entry:");
|
|
81
|
+
console.log(JSON.stringify({ mcpServers: { [SERVER_NAME]: SERVER_CONFIG } }, null, 2));
|
|
82
|
+
console.log("");
|
|
83
|
+
|
|
84
|
+
const results: InstallResult[] = [];
|
|
85
|
+
for (const client of selected) {
|
|
86
|
+
results.push(await installClient(client, options));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
printResults(results, options);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function parseSetupArgs(args: string[]): SetupOptions & { help?: boolean } {
|
|
93
|
+
const options: SetupOptions & { help?: boolean } = {};
|
|
94
|
+
|
|
95
|
+
for (let index = 0; index < args.length; index++) {
|
|
96
|
+
const arg = args[index];
|
|
97
|
+
if (arg === "--help" || arg === "-h") {
|
|
98
|
+
options.help = true;
|
|
99
|
+
} else if (arg === "--all") {
|
|
100
|
+
options.all = true;
|
|
101
|
+
} else if (arg === "--yes" || arg === "-y") {
|
|
102
|
+
options.yes = true;
|
|
103
|
+
} else if (arg === "--dry-run") {
|
|
104
|
+
options.dryRun = true;
|
|
105
|
+
} else if (arg === "--clients") {
|
|
106
|
+
const value = args[index + 1];
|
|
107
|
+
if (!value) throw new Error("--clients requires a comma-separated value");
|
|
108
|
+
options.clients = parseClientList(value);
|
|
109
|
+
index++;
|
|
110
|
+
} else if (arg.startsWith("--clients=")) {
|
|
111
|
+
options.clients = parseClientList(arg.slice("--clients=".length));
|
|
112
|
+
} else {
|
|
113
|
+
throw new Error(`Unknown setup option: ${arg}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return options;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parseClientList(value: string): SetupClientId[] {
|
|
121
|
+
const selected = value
|
|
122
|
+
.split(",")
|
|
123
|
+
.map((item) => item.trim().toLowerCase())
|
|
124
|
+
.filter(Boolean) as SetupClientId[];
|
|
125
|
+
const invalid = selected.filter((item) => !CLIENT_ORDER.includes(item));
|
|
126
|
+
if (invalid.length > 0) {
|
|
127
|
+
throw new Error(`Unknown client(s): ${invalid.join(", ")}`);
|
|
128
|
+
}
|
|
129
|
+
return selected;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function getSetupClients(home = homedir(), env = process.env): SetupClient[] {
|
|
133
|
+
const appData = env.APPDATA ?? path.join(home, "AppData", "Roaming");
|
|
134
|
+
const isWindows = platform() === "win32";
|
|
135
|
+
const isMac = platform() === "darwin";
|
|
136
|
+
const claudeDesktopPath = isMac
|
|
137
|
+
? path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")
|
|
138
|
+
: isWindows
|
|
139
|
+
? path.join(appData, "Claude", "claude_desktop_config.json")
|
|
140
|
+
: path.join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
141
|
+
|
|
142
|
+
const clients: SetupClient[] = [
|
|
143
|
+
{
|
|
144
|
+
id: "codex",
|
|
145
|
+
name: CLIENT_LABELS.codex,
|
|
146
|
+
path: path.join(home, ".codex", "config.toml"),
|
|
147
|
+
format: "toml",
|
|
148
|
+
detected: existsSync(path.join(home, ".codex")) || commandExists("codex"),
|
|
149
|
+
note: "Global Codex CLI/Desktop config",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "claude-code",
|
|
153
|
+
name: CLIENT_LABELS["claude-code"],
|
|
154
|
+
path: "claude mcp add-json --scope user",
|
|
155
|
+
format: "claude-code-cli",
|
|
156
|
+
detected: commandExists("claude"),
|
|
157
|
+
note: "Claude Code user-scope MCP config via the Claude CLI",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: "claude-desktop",
|
|
161
|
+
name: CLIENT_LABELS["claude-desktop"],
|
|
162
|
+
path: claudeDesktopPath,
|
|
163
|
+
format: "json",
|
|
164
|
+
detected: existsSync(claudeDesktopPath) || existsSync(path.dirname(claudeDesktopPath)),
|
|
165
|
+
note: "Claude Desktop developer config",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "cursor",
|
|
169
|
+
name: CLIENT_LABELS.cursor,
|
|
170
|
+
path: path.join(home, ".cursor", "mcp.json"),
|
|
171
|
+
format: "json",
|
|
172
|
+
detected: existsSync(path.join(home, ".cursor")),
|
|
173
|
+
note: "Global Cursor MCP config",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: "antigravity",
|
|
177
|
+
name: CLIENT_LABELS.antigravity,
|
|
178
|
+
path: path.join(home, ".gemini", "antigravity", "mcp_config.json"),
|
|
179
|
+
format: "json",
|
|
180
|
+
detected: existsSync(path.join(home, ".gemini", "antigravity")),
|
|
181
|
+
note: "Antigravity raw MCP config",
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
return clients;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function chooseClients(
|
|
189
|
+
clients: SetupClient[],
|
|
190
|
+
options: SetupOptions,
|
|
191
|
+
): Promise<SetupClient[]> {
|
|
192
|
+
if (options.clients?.length) {
|
|
193
|
+
return clients.filter((client) => options.clients?.includes(client.id));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (options.all) {
|
|
197
|
+
return clients;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const detected = clients.filter((client) => client.detected);
|
|
201
|
+
if (options.yes) {
|
|
202
|
+
return detected.length > 0 ? detected : clients.filter((client) => client.id === "codex");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log("Agent Wonderland MCP setup");
|
|
206
|
+
console.log("");
|
|
207
|
+
console.log("Detected clients:");
|
|
208
|
+
for (const client of clients) {
|
|
209
|
+
const marker = client.detected ? "found" : "not found";
|
|
210
|
+
console.log(` ${client.id.padEnd(15)} ${marker.padEnd(9)} ${client.path}`);
|
|
211
|
+
}
|
|
212
|
+
console.log("");
|
|
213
|
+
|
|
214
|
+
const rl = createInterface({ input, output });
|
|
215
|
+
try {
|
|
216
|
+
const selected: SetupClient[] = [];
|
|
217
|
+
for (const client of clients) {
|
|
218
|
+
const defaultAnswer = client.detected ? "Y/n" : "y/N";
|
|
219
|
+
const answer = (await rl.question(`Install for ${client.name}? (${defaultAnswer}) `)).trim().toLowerCase();
|
|
220
|
+
const yes = answer === ""
|
|
221
|
+
? client.detected
|
|
222
|
+
: answer === "y" || answer === "yes";
|
|
223
|
+
if (yes) selected.push(client);
|
|
224
|
+
}
|
|
225
|
+
return selected;
|
|
226
|
+
} finally {
|
|
227
|
+
rl.close();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function installClient(client: SetupClient, options: SetupOptions = {}): Promise<InstallResult> {
|
|
232
|
+
try {
|
|
233
|
+
if (client.format === "claude-code-cli") {
|
|
234
|
+
return installClaudeCode(client, options);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const existing = await readExisting(client.path);
|
|
238
|
+
const next = client.format === "json"
|
|
239
|
+
? mergeJsonMcpConfig(existing)
|
|
240
|
+
: mergeCodexTomlConfig(existing);
|
|
241
|
+
|
|
242
|
+
if (next === existing) {
|
|
243
|
+
return { client, status: "unchanged", message: "already configured" };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const backupPath = existing ? `${client.path}.bak-${timestamp()}` : undefined;
|
|
247
|
+
if (!options.dryRun) {
|
|
248
|
+
await mkdir(path.dirname(client.path), { recursive: true });
|
|
249
|
+
if (backupPath) {
|
|
250
|
+
await writeFile(backupPath, existing, "utf8");
|
|
251
|
+
}
|
|
252
|
+
await writeFile(client.path, next, "utf8");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
client,
|
|
257
|
+
status: existing ? "updated" : "created",
|
|
258
|
+
backupPath,
|
|
259
|
+
};
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
client,
|
|
263
|
+
status: "failed",
|
|
264
|
+
message: error instanceof Error ? error.message : String(error),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function installClaudeCode(client: SetupClient, options: SetupOptions): InstallResult {
|
|
270
|
+
if (!commandExists("claude")) {
|
|
271
|
+
return {
|
|
272
|
+
client,
|
|
273
|
+
status: "failed",
|
|
274
|
+
message: [
|
|
275
|
+
"Claude Code CLI was not found on PATH.",
|
|
276
|
+
"Run manually after installing Claude Code:",
|
|
277
|
+
`claude mcp add-json ${SERVER_NAME} '${JSON.stringify({ type: "stdio", ...SERVER_CONFIG })}' --scope user`,
|
|
278
|
+
].join(" "),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!options.dryRun) {
|
|
283
|
+
execFileSync("claude", [
|
|
284
|
+
"mcp",
|
|
285
|
+
"add-json",
|
|
286
|
+
SERVER_NAME,
|
|
287
|
+
JSON.stringify({ type: "stdio", ...SERVER_CONFIG }),
|
|
288
|
+
"--scope",
|
|
289
|
+
"user",
|
|
290
|
+
], { stdio: "pipe" });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
client,
|
|
295
|
+
status: options.dryRun ? "skipped" : "updated",
|
|
296
|
+
message: options.dryRun ? "would run Claude Code CLI installer" : "installed with Claude Code CLI",
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function readExisting(filePath: string): Promise<string> {
|
|
301
|
+
try {
|
|
302
|
+
return await readFile(filePath, "utf8");
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return "";
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function mergeJsonMcpConfig(existing: string): string {
|
|
310
|
+
const parsed = existing.trim()
|
|
311
|
+
? JSON.parse(existing) as Record<string, unknown>
|
|
312
|
+
: {};
|
|
313
|
+
|
|
314
|
+
const mcpServers = typeof parsed.mcpServers === "object" && parsed.mcpServers !== null && !Array.isArray(parsed.mcpServers)
|
|
315
|
+
? parsed.mcpServers as Record<string, unknown>
|
|
316
|
+
: {};
|
|
317
|
+
|
|
318
|
+
const next = {
|
|
319
|
+
...parsed,
|
|
320
|
+
mcpServers: {
|
|
321
|
+
...mcpServers,
|
|
322
|
+
[SERVER_NAME]: SERVER_CONFIG,
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
return `${JSON.stringify(next, null, 2)}\n`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function mergeCodexTomlConfig(existing: string): string {
|
|
330
|
+
const block = [
|
|
331
|
+
`[mcp_servers.${SERVER_NAME}]`,
|
|
332
|
+
`command = "npx"`,
|
|
333
|
+
`args = ["-y", "@agentwonderland/mcp"]`,
|
|
334
|
+
"",
|
|
335
|
+
].join("\n");
|
|
336
|
+
|
|
337
|
+
const pattern = new RegExp(
|
|
338
|
+
`\\n?\\[mcp_servers\\.${SERVER_NAME.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\][\\s\\S]*?(?=\\n\\[[^\\]]+\\]|$)`,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const trimmed = existing.trimEnd();
|
|
342
|
+
if (!trimmed) return block;
|
|
343
|
+
if (pattern.test(trimmed)) {
|
|
344
|
+
const next = trimmed.replace(pattern, `\n${block.trimEnd()}`);
|
|
345
|
+
return `${next.trimEnd()}\n`;
|
|
346
|
+
}
|
|
347
|
+
return `${trimmed}\n\n${block}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function printResults(results: InstallResult[], options: SetupOptions): void {
|
|
351
|
+
console.log(options.dryRun ? "Dry run complete:" : "Setup complete:");
|
|
352
|
+
for (const result of results) {
|
|
353
|
+
const suffix = result.backupPath ? ` (backup: ${result.backupPath})` : "";
|
|
354
|
+
const detail = result.message ? ` - ${result.message}` : "";
|
|
355
|
+
console.log(` ${result.status.padEnd(9)} ${result.client.name}: ${result.client.path}${suffix}${detail}`);
|
|
356
|
+
}
|
|
357
|
+
console.log("");
|
|
358
|
+
console.log("Restart any clients you updated, then ask your coding agent:");
|
|
359
|
+
console.log(' "Check my Agent Wonderland wallet status."');
|
|
360
|
+
console.log("");
|
|
361
|
+
console.log("If no wallet is configured yet, ask:");
|
|
362
|
+
console.log(' "Set up payment for Agent Wonderland."');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function printSetupHelp(): void {
|
|
366
|
+
console.log([
|
|
367
|
+
"Agent Wonderland MCP setup",
|
|
368
|
+
"",
|
|
369
|
+
"Usage:",
|
|
370
|
+
" npx @agentwonderland/mcp setup",
|
|
371
|
+
" npx @agentwonderland/mcp setup --clients codex,claude-code",
|
|
372
|
+
" npx @agentwonderland/mcp setup --all --yes",
|
|
373
|
+
"",
|
|
374
|
+
"Supported clients:",
|
|
375
|
+
` ${CLIENT_ORDER.join(", ")}`,
|
|
376
|
+
"",
|
|
377
|
+
"Options:",
|
|
378
|
+
" --clients <list> Comma-separated client ids",
|
|
379
|
+
" --all Offer/install every supported config target",
|
|
380
|
+
" -y, --yes Accept defaults without prompts",
|
|
381
|
+
" --dry-run Show what would be changed without writing files",
|
|
382
|
+
" -h, --help Show this help",
|
|
383
|
+
].join("\n"));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function printManualConfig(): void {
|
|
387
|
+
console.log("");
|
|
388
|
+
console.log("Manual MCP config:");
|
|
389
|
+
console.log(JSON.stringify({ mcpServers: { [SERVER_NAME]: SERVER_CONFIG } }, null, 2));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function timestamp(): string {
|
|
393
|
+
return new Date().toISOString().replace(/[:.]/g, "-");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function commandExists(command: string): boolean {
|
|
397
|
+
try {
|
|
398
|
+
if (platform() === "win32") {
|
|
399
|
+
execFileSync("where", [command], { stdio: "ignore" });
|
|
400
|
+
} else {
|
|
401
|
+
execFileSync("sh", ["-lc", `command -v ${shellQuote(command)}`], { stdio: "ignore" });
|
|
402
|
+
}
|
|
403
|
+
return true;
|
|
404
|
+
} catch {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function shellQuote(value: string): string {
|
|
410
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
411
|
+
}
|
package/src/tools/wallet.ts
CHANGED
|
@@ -280,6 +280,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
280
280
|
"",
|
|
281
281
|
chainStatus,
|
|
282
282
|
"",
|
|
283
|
+
"No MCP restart is required for wallet changes.",
|
|
284
|
+
"",
|
|
283
285
|
`Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
|
|
284
286
|
].join("\n"));
|
|
285
287
|
}
|
|
@@ -313,6 +315,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
313
315
|
` Chains: solana`,
|
|
314
316
|
` Consumer principal: ${principal}`,
|
|
315
317
|
"",
|
|
318
|
+
"No MCP restart is required for wallet changes.",
|
|
319
|
+
"",
|
|
316
320
|
"Fund this address with USDC on Solana to start using agents.",
|
|
317
321
|
].join("\n") + owsNudge,
|
|
318
322
|
);
|
|
@@ -340,6 +344,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
340
344
|
` Chains: tempo, base`,
|
|
341
345
|
` Consumer principal: ${principal}`,
|
|
342
346
|
"",
|
|
347
|
+
"No MCP restart is required for wallet changes.",
|
|
348
|
+
"",
|
|
343
349
|
`Fund this address with USDC on Tempo or Base to start using agents.`,
|
|
344
350
|
].join("\n") + owsNudge,
|
|
345
351
|
);
|
|
@@ -369,6 +375,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
369
375
|
` Storage: ~/.ows/ (AES-256-GCM encrypted)`,
|
|
370
376
|
` Consumer principal: ${principal}`,
|
|
371
377
|
"",
|
|
378
|
+
"No MCP restart is required for wallet changes.",
|
|
379
|
+
"",
|
|
372
380
|
`Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
|
|
373
381
|
"For testnet: npx mppx account fund",
|
|
374
382
|
].join("\n"),
|
|
@@ -395,11 +403,13 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
395
403
|
`Key imported to OWS [encrypted]:`,
|
|
396
404
|
` ID: ${result.walletId}`,
|
|
397
405
|
` Address: ${result.address}`,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
406
|
+
` Name: ${walletName}`,
|
|
407
|
+
` Chains: ${selectedChains.join(", ")}`,
|
|
408
|
+
` Consumer principal: ${principal}`,
|
|
409
|
+
"",
|
|
410
|
+
"No MCP restart is required for wallet changes.",
|
|
411
|
+
].join("\n"),
|
|
412
|
+
);
|
|
403
413
|
}
|
|
404
414
|
|
|
405
415
|
if (defaultCh === "solana") {
|
|
@@ -429,6 +439,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
429
439
|
` Name: ${walletName}`,
|
|
430
440
|
` Chains: solana`,
|
|
431
441
|
` Consumer principal: ${principal}`,
|
|
442
|
+
"",
|
|
443
|
+
"No MCP restart is required for wallet changes.",
|
|
432
444
|
].join("\n") + owsNudge,
|
|
433
445
|
);
|
|
434
446
|
} catch (err) {
|
|
@@ -468,6 +480,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
468
480
|
` Name: ${walletName}`,
|
|
469
481
|
` Chains: ${selectedChains.join(", ")}`,
|
|
470
482
|
` Consumer principal: ${principal}`,
|
|
483
|
+
"",
|
|
484
|
+
"No MCP restart is required for wallet changes.",
|
|
471
485
|
].join("\n") + owsNudge,
|
|
472
486
|
);
|
|
473
487
|
} catch {
|