@agentwonderland/mcp 0.1.2 → 0.1.4
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/file-upload.d.ts +9 -0
- package/dist/core/file-upload.js +101 -0
- package/dist/core/formatters.d.ts +1 -1
- package/dist/core/formatters.js +2 -2
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/ows-adapter.d.ts +1 -1
- package/dist/core/ows-adapter.js +15 -4
- package/dist/core/payments.d.ts +1 -1
- package/dist/core/payments.js +4 -4
- package/dist/tools/run.js +3 -1
- package/dist/tools/solve.js +5 -2
- package/package.json +1 -1
- package/src/core/file-upload.ts +114 -0
- package/src/core/formatters.ts +2 -2
- package/src/core/index.ts +1 -0
- package/src/core/ows-adapter.ts +14 -4
- package/src/core/payments.ts +4 -4
- package/src/tools/run.ts +3 -1
- package/src/tools/solve.ts +5 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a string looks like a local file path (not a URL).
|
|
3
|
+
*/
|
|
4
|
+
export declare function isLocalFilePath(value: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Scan input object for local file paths, upload them to temporary storage,
|
|
7
|
+
* and replace with download URLs. Non-file values are left unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export declare function uploadLocalFiles(input: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { basename, resolve } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { getApiUrl } from "./config.js";
|
|
5
|
+
const EXT_TO_MIME = {
|
|
6
|
+
pdf: "application/pdf",
|
|
7
|
+
png: "image/png",
|
|
8
|
+
jpg: "image/jpeg",
|
|
9
|
+
jpeg: "image/jpeg",
|
|
10
|
+
gif: "image/gif",
|
|
11
|
+
webp: "image/webp",
|
|
12
|
+
svg: "image/svg+xml",
|
|
13
|
+
mp3: "audio/mpeg",
|
|
14
|
+
mp4: "video/mp4",
|
|
15
|
+
wav: "audio/wav",
|
|
16
|
+
csv: "text/csv",
|
|
17
|
+
json: "application/json",
|
|
18
|
+
txt: "text/plain",
|
|
19
|
+
html: "text/html",
|
|
20
|
+
xml: "application/xml",
|
|
21
|
+
zip: "application/zip",
|
|
22
|
+
doc: "application/msword",
|
|
23
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
24
|
+
xls: "application/vnd.ms-excel",
|
|
25
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
26
|
+
};
|
|
27
|
+
const MAX_UPLOAD_SIZE = 50 * 1024 * 1024; // 50MB
|
|
28
|
+
/**
|
|
29
|
+
* Check if a string looks like a local file path (not a URL).
|
|
30
|
+
*/
|
|
31
|
+
export function isLocalFilePath(value) {
|
|
32
|
+
if (value.startsWith("http://") || value.startsWith("https://") || value.startsWith("data:")) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (value.includes("\n"))
|
|
36
|
+
return false;
|
|
37
|
+
// Unix absolute path, home dir path, or Windows drive path
|
|
38
|
+
return value.startsWith("/") || value.startsWith("~/") || /^[A-Za-z]:\\/.test(value);
|
|
39
|
+
}
|
|
40
|
+
function resolvePath(filePath) {
|
|
41
|
+
if (filePath.startsWith("~/")) {
|
|
42
|
+
return resolve(homedir(), filePath.slice(2));
|
|
43
|
+
}
|
|
44
|
+
return resolve(filePath);
|
|
45
|
+
}
|
|
46
|
+
function getMimeType(filename) {
|
|
47
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
48
|
+
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Upload a local file to the gateway's temporary storage.
|
|
52
|
+
* Returns the presigned download URL.
|
|
53
|
+
*/
|
|
54
|
+
async function uploadFile(filePath) {
|
|
55
|
+
const resolved = resolvePath(filePath);
|
|
56
|
+
const buffer = readFileSync(resolved);
|
|
57
|
+
if (buffer.length > MAX_UPLOAD_SIZE) {
|
|
58
|
+
throw new Error(`File ${basename(resolved)} exceeds 50MB upload limit (${(buffer.length / (1024 * 1024)).toFixed(1)}MB)`);
|
|
59
|
+
}
|
|
60
|
+
const filename = basename(resolved);
|
|
61
|
+
const contentType = getMimeType(filename);
|
|
62
|
+
const apiUrl = getApiUrl();
|
|
63
|
+
const res = await fetch(`${apiUrl}/uploads`, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "Content-Type": "application/json" },
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
file: buffer.toString("base64"),
|
|
68
|
+
filename,
|
|
69
|
+
content_type: contentType,
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const body = await res.json().catch(() => ({}));
|
|
74
|
+
throw new Error(`Upload failed: ${body.error ?? res.statusText}`);
|
|
75
|
+
}
|
|
76
|
+
const result = await res.json();
|
|
77
|
+
return result.url;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Scan input object for local file paths, upload them to temporary storage,
|
|
81
|
+
* and replace with download URLs. Non-file values are left unchanged.
|
|
82
|
+
*/
|
|
83
|
+
export async function uploadLocalFiles(input) {
|
|
84
|
+
const result = { ...input };
|
|
85
|
+
for (const [key, value] of Object.entries(result)) {
|
|
86
|
+
if (typeof value !== "string")
|
|
87
|
+
continue;
|
|
88
|
+
if (!isLocalFilePath(value))
|
|
89
|
+
continue;
|
|
90
|
+
const resolved = resolvePath(value);
|
|
91
|
+
if (!existsSync(resolved))
|
|
92
|
+
continue;
|
|
93
|
+
try {
|
|
94
|
+
result[key] = await uploadFile(value);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Leave original value if upload fails
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
package/dist/core/formatters.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared display formatters for human-readable MCP
|
|
2
|
+
* Shared display formatters for human-readable MCP output.
|
|
3
3
|
* Plain text only — no ANSI color codes.
|
|
4
4
|
*/
|
|
5
5
|
// ── Stars ────────────────────────────────────────────────────────
|
|
@@ -180,7 +180,7 @@ export function formatFeedbackSummary(stats) {
|
|
|
180
180
|
}
|
|
181
181
|
export function formatWalletStatus(info) {
|
|
182
182
|
if (info.wallets.length === 0 && !info.card) {
|
|
183
|
-
return "No payment methods configured.\
|
|
183
|
+
return "No payment methods configured.\nUse the wallet_setup tool to create or import a wallet.";
|
|
184
184
|
}
|
|
185
185
|
const lines = ["Payment methods:"];
|
|
186
186
|
for (const w of info.wallets) {
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* any other viem-based flow can use OWS-managed keys transparently.
|
|
6
6
|
*
|
|
7
7
|
* The OWS SDK (`@open-wallet-standard/core`) is a NAPI native module.
|
|
8
|
-
* ALL imports are dynamic so the
|
|
8
|
+
* ALL imports are dynamic so the MCP server works without OWS installed.
|
|
9
9
|
*/
|
|
10
10
|
import type { LocalAccount } from "viem/accounts";
|
|
11
11
|
/**
|
package/dist/core/ows-adapter.js
CHANGED
|
@@ -5,17 +5,29 @@
|
|
|
5
5
|
* any other viem-based flow can use OWS-managed keys transparently.
|
|
6
6
|
*
|
|
7
7
|
* The OWS SDK (`@open-wallet-standard/core`) is a NAPI native module.
|
|
8
|
-
* ALL imports are dynamic so the
|
|
8
|
+
* ALL imports are dynamic so the MCP server works without OWS installed.
|
|
9
9
|
*/
|
|
10
10
|
// ── Helpers ──────────────────────────────────────────────────────
|
|
11
11
|
const OWS_INSTALL_HINT = "OWS is not installed. Install with: npm install -g @open-wallet-standard/core";
|
|
12
12
|
async function loadOws() {
|
|
13
|
+
// Try local/project resolution first
|
|
13
14
|
try {
|
|
14
15
|
// @ts-ignore — optional peer dep, dynamically imported
|
|
15
16
|
return (await import("@open-wallet-standard/core"));
|
|
16
17
|
}
|
|
17
18
|
catch {
|
|
18
|
-
|
|
19
|
+
// Fallback: resolve from global npm prefix using createRequire
|
|
20
|
+
// (npx runs in an isolated context that can't see globally-installed native modules)
|
|
21
|
+
try {
|
|
22
|
+
const { execSync } = await import("node:child_process");
|
|
23
|
+
const { createRequire } = await import("node:module");
|
|
24
|
+
const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
|
|
25
|
+
const globalRequire = createRequire(globalRoot + "/");
|
|
26
|
+
return globalRequire("@open-wallet-standard/core");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new Error(OWS_INSTALL_HINT);
|
|
30
|
+
}
|
|
19
31
|
}
|
|
20
32
|
}
|
|
21
33
|
function findEvmAccount(wallet) {
|
|
@@ -32,8 +44,7 @@ function findEvmAccount(wallet) {
|
|
|
32
44
|
*/
|
|
33
45
|
export async function isOwsAvailable() {
|
|
34
46
|
try {
|
|
35
|
-
|
|
36
|
-
await import("@open-wallet-standard/core");
|
|
47
|
+
await loadOws();
|
|
37
48
|
return true;
|
|
38
49
|
}
|
|
39
50
|
catch {
|
package/dist/core/payments.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare function getConfiguredMethods(): string[];
|
|
|
27
27
|
export declare function paymentMethodDisplayName(method: string): string;
|
|
28
28
|
/**
|
|
29
29
|
* Return the consumer's configured payment methods in registry API format.
|
|
30
|
-
* Maps
|
|
30
|
+
* Maps method names (tempo, base, card) to registry identifiers
|
|
31
31
|
* (tempo_usdc, base_usdc, stripe_card).
|
|
32
32
|
*/
|
|
33
33
|
export declare function getAcceptedPaymentMethods(): string[];
|
package/dist/core/payments.js
CHANGED
|
@@ -102,13 +102,13 @@ export async function getPaymentFetch(method) {
|
|
|
102
102
|
fetchCache.set(ck, pf);
|
|
103
103
|
return pf;
|
|
104
104
|
}
|
|
105
|
-
throw new Error('Payment method "card" is not configured.
|
|
105
|
+
throw new Error('Payment method "card" is not configured. Use the wallet_setup tool to configure a wallet');
|
|
106
106
|
}
|
|
107
107
|
// Explicit method requested (wallet ID or chain name)
|
|
108
108
|
if (method) {
|
|
109
109
|
const resolved = resolveWalletAndChain(method);
|
|
110
110
|
if (!resolved) {
|
|
111
|
-
throw new Error(`Payment method "${method}" is not configured.
|
|
111
|
+
throw new Error(`Payment method "${method}" is not configured. Use the wallet_setup tool to configure a wallet`);
|
|
112
112
|
}
|
|
113
113
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
114
114
|
if (fetchCache.has(ck))
|
|
@@ -118,7 +118,7 @@ export async function getPaymentFetch(method) {
|
|
|
118
118
|
fetchCache.set(ck, pf);
|
|
119
119
|
return pf;
|
|
120
120
|
}
|
|
121
|
-
throw new Error(`Payment method "${method}" is not configured.
|
|
121
|
+
throw new Error(`Payment method "${method}" is not configured. Use the wallet_setup tool to configure a wallet`);
|
|
122
122
|
}
|
|
123
123
|
// Auto-detect: try default wallet, then card
|
|
124
124
|
const configured = getConfiguredMethods();
|
|
@@ -197,7 +197,7 @@ export function paymentMethodDisplayName(method) {
|
|
|
197
197
|
}
|
|
198
198
|
/**
|
|
199
199
|
* Return the consumer's configured payment methods in registry API format.
|
|
200
|
-
* Maps
|
|
200
|
+
* Maps method names (tempo, base, card) to registry identifiers
|
|
201
201
|
* (tempo_usdc, base_usdc, stripe_card).
|
|
202
202
|
*/
|
|
203
203
|
export function getAcceptedPaymentMethods() {
|
package/dist/tools/run.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { apiGet, apiPostWithPayment } from "../core/api-client.js";
|
|
3
|
+
import { uploadLocalFiles } from "../core/file-upload.js";
|
|
3
4
|
import { getConfiguredMethods, hasWalletConfigured } from "../core/payments.js";
|
|
4
5
|
import { formatRunResult } from "../core/formatters.js";
|
|
5
6
|
import { storeFeedbackToken } from "./_token-cache.js";
|
|
@@ -30,9 +31,10 @@ export function registerRunTools(server) {
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
const method = pay_with;
|
|
34
|
+
const processedInput = await uploadLocalFiles(input);
|
|
33
35
|
let result;
|
|
34
36
|
try {
|
|
35
|
-
result = await apiPostWithPayment(`/agents/${resolvedId}/run`, { input }, method);
|
|
37
|
+
result = await apiPostWithPayment(`/agents/${resolvedId}/run`, { input: processedInput }, method);
|
|
36
38
|
}
|
|
37
39
|
catch (err) {
|
|
38
40
|
const apiErr = err;
|
package/dist/tools/solve.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
|
|
3
3
|
import { hasWalletConfigured, getConfiguredMethods, getAcceptedPaymentMethods, } from "../core/payments.js";
|
|
4
4
|
import { agentList, formatRunResult } from "../core/formatters.js";
|
|
5
|
+
import { uploadLocalFiles } from "../core/file-upload.js";
|
|
5
6
|
import { storeFeedbackToken } from "./_token-cache.js";
|
|
6
7
|
function text(t) {
|
|
7
8
|
return { content: [{ type: "text", text: t }] };
|
|
@@ -46,9 +47,10 @@ export function registerSolveTools(server) {
|
|
|
46
47
|
const method = pay_with ?? getConfiguredMethods()[0];
|
|
47
48
|
// Path 1: If authenticated, use the platform /solve route
|
|
48
49
|
try {
|
|
50
|
+
const processedInput = await uploadLocalFiles(input);
|
|
49
51
|
const result = await apiPost("/solve", {
|
|
50
52
|
intent,
|
|
51
|
-
input,
|
|
53
|
+
input: processedInput,
|
|
52
54
|
budget,
|
|
53
55
|
});
|
|
54
56
|
const jobId = result.job_id ?? "";
|
|
@@ -91,8 +93,9 @@ export function registerSolveTools(server) {
|
|
|
91
93
|
? selectedPrice
|
|
92
94
|
: (inputTokens / 1000) * selectedPrice;
|
|
93
95
|
let result;
|
|
96
|
+
const processedInput2 = await uploadLocalFiles(input);
|
|
94
97
|
try {
|
|
95
|
-
result = await apiPostWithPayment(`/agents/${selected.id}/run`, { input }, method);
|
|
98
|
+
result = await apiPostWithPayment(`/agents/${selected.id}/run`, { input: processedInput2 }, method);
|
|
96
99
|
}
|
|
97
100
|
catch (err) {
|
|
98
101
|
const apiErr = err;
|
package/package.json
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { basename, resolve } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { getApiUrl } from "./config.js";
|
|
5
|
+
|
|
6
|
+
const EXT_TO_MIME: Record<string, string> = {
|
|
7
|
+
pdf: "application/pdf",
|
|
8
|
+
png: "image/png",
|
|
9
|
+
jpg: "image/jpeg",
|
|
10
|
+
jpeg: "image/jpeg",
|
|
11
|
+
gif: "image/gif",
|
|
12
|
+
webp: "image/webp",
|
|
13
|
+
svg: "image/svg+xml",
|
|
14
|
+
mp3: "audio/mpeg",
|
|
15
|
+
mp4: "video/mp4",
|
|
16
|
+
wav: "audio/wav",
|
|
17
|
+
csv: "text/csv",
|
|
18
|
+
json: "application/json",
|
|
19
|
+
txt: "text/plain",
|
|
20
|
+
html: "text/html",
|
|
21
|
+
xml: "application/xml",
|
|
22
|
+
zip: "application/zip",
|
|
23
|
+
doc: "application/msword",
|
|
24
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
25
|
+
xls: "application/vnd.ms-excel",
|
|
26
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const MAX_UPLOAD_SIZE = 50 * 1024 * 1024; // 50MB
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a string looks like a local file path (not a URL).
|
|
33
|
+
*/
|
|
34
|
+
export function isLocalFilePath(value: string): boolean {
|
|
35
|
+
if (value.startsWith("http://") || value.startsWith("https://") || value.startsWith("data:")) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (value.includes("\n")) return false;
|
|
39
|
+
// Unix absolute path, home dir path, or Windows drive path
|
|
40
|
+
return value.startsWith("/") || value.startsWith("~/") || /^[A-Za-z]:\\/.test(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolvePath(filePath: string): string {
|
|
44
|
+
if (filePath.startsWith("~/")) {
|
|
45
|
+
return resolve(homedir(), filePath.slice(2));
|
|
46
|
+
}
|
|
47
|
+
return resolve(filePath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getMimeType(filename: string): string {
|
|
51
|
+
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
|
52
|
+
return EXT_TO_MIME[ext] ?? "application/octet-stream";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Upload a local file to the gateway's temporary storage.
|
|
57
|
+
* Returns the presigned download URL.
|
|
58
|
+
*/
|
|
59
|
+
async function uploadFile(filePath: string): Promise<string> {
|
|
60
|
+
const resolved = resolvePath(filePath);
|
|
61
|
+
const buffer = readFileSync(resolved);
|
|
62
|
+
|
|
63
|
+
if (buffer.length > MAX_UPLOAD_SIZE) {
|
|
64
|
+
throw new Error(`File ${basename(resolved)} exceeds 50MB upload limit (${(buffer.length / (1024 * 1024)).toFixed(1)}MB)`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const filename = basename(resolved);
|
|
68
|
+
const contentType = getMimeType(filename);
|
|
69
|
+
const apiUrl = getApiUrl();
|
|
70
|
+
|
|
71
|
+
const res = await fetch(`${apiUrl}/uploads`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: JSON.stringify({
|
|
75
|
+
file: buffer.toString("base64"),
|
|
76
|
+
filename,
|
|
77
|
+
content_type: contentType,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
const body = await res.json().catch(() => ({})) as Record<string, unknown>;
|
|
83
|
+
throw new Error(`Upload failed: ${body.error ?? res.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = await res.json() as { url: string };
|
|
87
|
+
return result.url;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Scan input object for local file paths, upload them to temporary storage,
|
|
92
|
+
* and replace with download URLs. Non-file values are left unchanged.
|
|
93
|
+
*/
|
|
94
|
+
export async function uploadLocalFiles(
|
|
95
|
+
input: Record<string, unknown>,
|
|
96
|
+
): Promise<Record<string, unknown>> {
|
|
97
|
+
const result = { ...input };
|
|
98
|
+
|
|
99
|
+
for (const [key, value] of Object.entries(result)) {
|
|
100
|
+
if (typeof value !== "string") continue;
|
|
101
|
+
if (!isLocalFilePath(value)) continue;
|
|
102
|
+
|
|
103
|
+
const resolved = resolvePath(value);
|
|
104
|
+
if (!existsSync(resolved)) continue;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
result[key] = await uploadFile(value);
|
|
108
|
+
} catch {
|
|
109
|
+
// Leave original value if upload fails
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
}
|
package/src/core/formatters.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared display formatters for human-readable MCP
|
|
2
|
+
* Shared display formatters for human-readable MCP output.
|
|
3
3
|
* Plain text only — no ANSI color codes.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -244,7 +244,7 @@ interface WalletInfo {
|
|
|
244
244
|
|
|
245
245
|
export function formatWalletStatus(info: WalletInfo): string {
|
|
246
246
|
if (info.wallets.length === 0 && !info.card) {
|
|
247
|
-
return "No payment methods configured.\
|
|
247
|
+
return "No payment methods configured.\nUse the wallet_setup tool to create or import a wallet.";
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
const lines = ["Payment methods:"];
|
package/src/core/index.ts
CHANGED
package/src/core/ows-adapter.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* any other viem-based flow can use OWS-managed keys transparently.
|
|
6
6
|
*
|
|
7
7
|
* The OWS SDK (`@open-wallet-standard/core`) is a NAPI native module.
|
|
8
|
-
* ALL imports are dynamic so the
|
|
8
|
+
* ALL imports are dynamic so the MCP server works without OWS installed.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { LocalAccount } from "viem/accounts";
|
|
@@ -93,11 +93,22 @@ const OWS_INSTALL_HINT =
|
|
|
93
93
|
"OWS is not installed. Install with: npm install -g @open-wallet-standard/core";
|
|
94
94
|
|
|
95
95
|
async function loadOws(): Promise<OwsSdk> {
|
|
96
|
+
// Try local/project resolution first
|
|
96
97
|
try {
|
|
97
98
|
// @ts-ignore — optional peer dep, dynamically imported
|
|
98
99
|
return (await import("@open-wallet-standard/core")) as unknown as OwsSdk;
|
|
99
100
|
} catch {
|
|
100
|
-
|
|
101
|
+
// Fallback: resolve from global npm prefix using createRequire
|
|
102
|
+
// (npx runs in an isolated context that can't see globally-installed native modules)
|
|
103
|
+
try {
|
|
104
|
+
const { execSync } = await import("node:child_process");
|
|
105
|
+
const { createRequire } = await import("node:module");
|
|
106
|
+
const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
|
|
107
|
+
const globalRequire = createRequire(globalRoot + "/");
|
|
108
|
+
return globalRequire("@open-wallet-standard/core") as unknown as OwsSdk;
|
|
109
|
+
} catch {
|
|
110
|
+
throw new Error(OWS_INSTALL_HINT);
|
|
111
|
+
}
|
|
101
112
|
}
|
|
102
113
|
}
|
|
103
114
|
|
|
@@ -123,8 +134,7 @@ function findEvmAccount(
|
|
|
123
134
|
*/
|
|
124
135
|
export async function isOwsAvailable(): Promise<boolean> {
|
|
125
136
|
try {
|
|
126
|
-
|
|
127
|
-
await import("@open-wallet-standard/core");
|
|
137
|
+
await loadOws();
|
|
128
138
|
return true;
|
|
129
139
|
} catch {
|
|
130
140
|
return false;
|
package/src/core/payments.ts
CHANGED
|
@@ -121,14 +121,14 @@ export async function getPaymentFetch(method?: string): Promise<typeof fetch> {
|
|
|
121
121
|
fetchCache.set(ck, pf);
|
|
122
122
|
return pf;
|
|
123
123
|
}
|
|
124
|
-
throw new Error('Payment method "card" is not configured.
|
|
124
|
+
throw new Error('Payment method "card" is not configured. Use the wallet_setup tool to configure a wallet');
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// Explicit method requested (wallet ID or chain name)
|
|
128
128
|
if (method) {
|
|
129
129
|
const resolved = resolveWalletAndChain(method);
|
|
130
130
|
if (!resolved) {
|
|
131
|
-
throw new Error(`Payment method "${method}" is not configured.
|
|
131
|
+
throw new Error(`Payment method "${method}" is not configured. Use the wallet_setup tool to configure a wallet`);
|
|
132
132
|
}
|
|
133
133
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
134
134
|
if (fetchCache.has(ck)) return fetchCache.get(ck)!;
|
|
@@ -137,7 +137,7 @@ export async function getPaymentFetch(method?: string): Promise<typeof fetch> {
|
|
|
137
137
|
fetchCache.set(ck, pf);
|
|
138
138
|
return pf;
|
|
139
139
|
}
|
|
140
|
-
throw new Error(`Payment method "${method}" is not configured.
|
|
140
|
+
throw new Error(`Payment method "${method}" is not configured. Use the wallet_setup tool to configure a wallet`);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// Auto-detect: try default wallet, then card
|
|
@@ -219,7 +219,7 @@ export function paymentMethodDisplayName(method: string): string {
|
|
|
219
219
|
|
|
220
220
|
/**
|
|
221
221
|
* Return the consumer's configured payment methods in registry API format.
|
|
222
|
-
* Maps
|
|
222
|
+
* Maps method names (tempo, base, card) to registry identifiers
|
|
223
223
|
* (tempo_usdc, base_usdc, stripe_card).
|
|
224
224
|
*/
|
|
225
225
|
export function getAcceptedPaymentMethods(): string[] {
|
package/src/tools/run.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { apiGet, apiPostWithPayment } from "../core/api-client.js";
|
|
4
|
+
import { uploadLocalFiles } from "../core/file-upload.js";
|
|
4
5
|
import { getConfiguredMethods, hasWalletConfigured } from "../core/payments.js";
|
|
5
6
|
import { formatRunResult } from "../core/formatters.js";
|
|
6
7
|
import { storeFeedbackToken } from "./_token-cache.js";
|
|
@@ -40,11 +41,12 @@ export function registerRunTools(server: McpServer): void {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
const method = pay_with;
|
|
44
|
+
const processedInput = await uploadLocalFiles(input);
|
|
43
45
|
let result: Record<string, unknown>;
|
|
44
46
|
try {
|
|
45
47
|
result = await apiPostWithPayment<Record<string, unknown>>(
|
|
46
48
|
`/agents/${resolvedId}/run`,
|
|
47
|
-
{ input },
|
|
49
|
+
{ input: processedInput },
|
|
48
50
|
method,
|
|
49
51
|
);
|
|
50
52
|
} catch (err: unknown) {
|
package/src/tools/solve.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getAcceptedPaymentMethods,
|
|
8
8
|
} from "../core/payments.js";
|
|
9
9
|
import { agentList, formatRunResult } from "../core/formatters.js";
|
|
10
|
+
import { uploadLocalFiles } from "../core/file-upload.js";
|
|
10
11
|
import type { AgentRecord } from "../core/types.js";
|
|
11
12
|
import { storeFeedbackToken } from "./_token-cache.js";
|
|
12
13
|
|
|
@@ -65,9 +66,10 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
65
66
|
|
|
66
67
|
// Path 1: If authenticated, use the platform /solve route
|
|
67
68
|
try {
|
|
69
|
+
const processedInput = await uploadLocalFiles(input);
|
|
68
70
|
const result = await apiPost<Record<string, unknown>>("/solve", {
|
|
69
71
|
intent,
|
|
70
|
-
input,
|
|
72
|
+
input: processedInput,
|
|
71
73
|
budget,
|
|
72
74
|
});
|
|
73
75
|
const jobId = (result as Record<string, unknown>).job_id as string ?? "";
|
|
@@ -119,10 +121,11 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
119
121
|
: (inputTokens / 1000) * selectedPrice;
|
|
120
122
|
|
|
121
123
|
let result: Record<string, unknown>;
|
|
124
|
+
const processedInput2 = await uploadLocalFiles(input);
|
|
122
125
|
try {
|
|
123
126
|
result = await apiPostWithPayment<Record<string, unknown>>(
|
|
124
127
|
`/agents/${selected.id}/run`,
|
|
125
|
-
{ input },
|
|
128
|
+
{ input: processedInput2 },
|
|
126
129
|
method,
|
|
127
130
|
);
|
|
128
131
|
} catch (err: unknown) {
|