@cmdoss/suipay-mcp 0.2.2-dev.5 → 0.2.2-dev.6
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/README.md +17 -1
- package/dist/bin/suipay.js +2 -2
- package/dist/{chunk-P54ZYBPN.js → chunk-AEV6Q6JC.js} +2 -2
- package/dist/{chunk-THLDEMGS.js → chunk-UTKVBAEZ.js} +248 -4
- package/dist/{http-B4YTVw0k.d.ts → http-DBP9qgVw.d.ts} +13 -0
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ missing, expired, or revoked.
|
|
|
51
51
|
| Tool | Purpose |
|
|
52
52
|
| --- | --- |
|
|
53
53
|
| `discover` | Search live gateway resources. After login: `name`, `path`, `url`, `targetHash`, catalog price, and `affordable` (can this session cover one call — not authorization). `pay({ url })` still settles |
|
|
54
|
-
| `pay` | Fetch resource, prepare `spend_account` settlement, settle when this process holds the grant key |
|
|
54
|
+
| `pay` | Fetch resource, prepare `spend_account` settlement, settle when this process holds the grant key. POST `json` is the paid body. Optional stdio `file` + `fileField` fill one JSON key from a local file as canonical base64 (Walrus `contentBase64`); requires `SUIPAY_MCP_FILE_ROOT`; hosted `/mcp` refuses `file` |
|
|
55
55
|
| `receipts` | List settlement receipts, optionally by challenge ID |
|
|
56
56
|
| `access_context` | Report current session / delegate / policy state (no secret material) |
|
|
57
57
|
| `suipay_login` | Mint or reuse the local delegate key and return a clickable console URL (does not wait for the wallet). Default target is Railway suipay-dev; pass `target: "local"` for the laptop stack |
|
|
@@ -76,6 +76,22 @@ json: {"prompt":"a cat"}
|
|
|
76
76
|
maxAmount: 50000
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
To store local bytes (Walrus), do not paste base64. Name the file and field.
|
|
80
|
+
Set `SUIPAY_MCP_FILE_ROOT` to an absolute directory; `file` must resolve inside
|
|
81
|
+
it (unset disables attach). Paths under `~/.suipay` are refused even if that
|
|
82
|
+
directory falls inside the root.
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Pay this SuiPay resource with the suipay MCP `pay` tool. Do not write SDK code.
|
|
86
|
+
|
|
87
|
+
url: https://gateway.example/v1/walrus/store
|
|
88
|
+
method: POST
|
|
89
|
+
json: {"contentType":"text/plain"}
|
|
90
|
+
file: /absolute/path/to/note.txt
|
|
91
|
+
fileField: contentBase64
|
|
92
|
+
maxAmount: 50000
|
|
93
|
+
```
|
|
94
|
+
|
|
79
95
|
`pay` uses SDK `createPayer` internally. When this process settles, a binary
|
|
80
96
|
body is written once under `~/.suipay/deliveries/` and named first in the
|
|
81
97
|
JSON as `savedTo` (absolute path) and `savedToUrl` (`file://`). Hosted `/mcp`
|
package/dist/bin/suipay.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
loadLocalSigningKeyRecord,
|
|
5
5
|
loadMcpConfig,
|
|
6
6
|
loadPaymentProfile
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UTKVBAEZ.js";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
10
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -19,7 +19,7 @@ async function main() {
|
|
|
19
19
|
const argv = process.argv.slice(2);
|
|
20
20
|
const cfg = loadMcpConfig(process.env, argv);
|
|
21
21
|
const target = cfg.target ?? "dev";
|
|
22
|
-
const server = createServer(cfg);
|
|
22
|
+
const server = createServer(cfg, void 0, void 0, { transport: "stdio" });
|
|
23
23
|
await server.connect(new StdioServerTransport());
|
|
24
24
|
try {
|
|
25
25
|
const boundGateway = loadLocalSigningKeyRecord()?.gatewayUrl ?? cfg.gatewayUrl;
|
|
@@ -7002,6 +7002,205 @@ function settlePayResult(result) {
|
|
|
7002
7002
|
return error(result);
|
|
7003
7003
|
}
|
|
7004
7004
|
|
|
7005
|
+
// src/pay-file.ts
|
|
7006
|
+
import { isAbsolute, dirname as dirname3, sep } from "path";
|
|
7007
|
+
import {
|
|
7008
|
+
openSync as openSync2,
|
|
7009
|
+
closeSync as closeSync2,
|
|
7010
|
+
fstatSync,
|
|
7011
|
+
readFileSync as readFileSync2,
|
|
7012
|
+
realpathSync,
|
|
7013
|
+
constants
|
|
7014
|
+
} from "fs";
|
|
7015
|
+
var JSON_WRAP_SLACK_BYTES = 256;
|
|
7016
|
+
var MCP_MAX_FILE_FIELD_BYTES = Math.floor(
|
|
7017
|
+
(MAX_PAID_BODY_BYTES - JSON_WRAP_SLACK_BYTES) * 3 / 4
|
|
7018
|
+
);
|
|
7019
|
+
var FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
7020
|
+
var FORBIDDEN = /* @__PURE__ */ new Set(["constructor", "prototype"]);
|
|
7021
|
+
function fail2(code, detail) {
|
|
7022
|
+
return { code, detail };
|
|
7023
|
+
}
|
|
7024
|
+
function hasLocalFileArgs(input) {
|
|
7025
|
+
return input.file !== void 0 && input.file !== null || input.fileField !== void 0 && input.fileField !== null;
|
|
7026
|
+
}
|
|
7027
|
+
function parseField(value) {
|
|
7028
|
+
if (typeof value !== "string" || !FIELD.test(value) || FORBIDDEN.has(value)) {
|
|
7029
|
+
return fail2("INVALID_FILE_FIELD", "fileField must be a simple JSON key");
|
|
7030
|
+
}
|
|
7031
|
+
return value;
|
|
7032
|
+
}
|
|
7033
|
+
function parsePath(value) {
|
|
7034
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
|
|
7035
|
+
return fail2("INVALID_FILE", "file must be a non-empty absolute path");
|
|
7036
|
+
}
|
|
7037
|
+
if (!isAbsolute(value) || value.includes("://")) {
|
|
7038
|
+
return fail2("INVALID_FILE", "file must be an absolute filesystem path, not a URL");
|
|
7039
|
+
}
|
|
7040
|
+
return value;
|
|
7041
|
+
}
|
|
7042
|
+
function parseMethod(value) {
|
|
7043
|
+
if (value === void 0 || value === null) return "GET";
|
|
7044
|
+
if (typeof value !== "string") {
|
|
7045
|
+
return fail2("MALFORMED_REQUEST", "method must be GET or POST");
|
|
7046
|
+
}
|
|
7047
|
+
const upper = value.trim().toUpperCase();
|
|
7048
|
+
if (upper === "GET" || upper === "POST") return upper;
|
|
7049
|
+
return fail2("MALFORMED_REQUEST", "method must be GET or POST");
|
|
7050
|
+
}
|
|
7051
|
+
function inside(root, candidate) {
|
|
7052
|
+
const prefix = root.endsWith(sep) ? root : root + sep;
|
|
7053
|
+
return candidate === root || candidate.startsWith(prefix);
|
|
7054
|
+
}
|
|
7055
|
+
function resolveContainedPath(raw) {
|
|
7056
|
+
const rootRaw = process.env.SUIPAY_MCP_FILE_ROOT;
|
|
7057
|
+
if (typeof rootRaw !== "string" || rootRaw.trim() === "") {
|
|
7058
|
+
return fail2(
|
|
7059
|
+
"FILE_ROOT_UNSET",
|
|
7060
|
+
"file attach requires SUIPAY_MCP_FILE_ROOT to be an absolute directory"
|
|
7061
|
+
);
|
|
7062
|
+
}
|
|
7063
|
+
if (!isAbsolute(rootRaw) || rootRaw.includes("://")) {
|
|
7064
|
+
return fail2(
|
|
7065
|
+
"FILE_ROOT_UNSET",
|
|
7066
|
+
"SUIPAY_MCP_FILE_ROOT must be an absolute filesystem path"
|
|
7067
|
+
);
|
|
7068
|
+
}
|
|
7069
|
+
let rootReal;
|
|
7070
|
+
let fileReal;
|
|
7071
|
+
try {
|
|
7072
|
+
rootReal = realpathSync(rootRaw);
|
|
7073
|
+
} catch {
|
|
7074
|
+
return fail2("FILE_ROOT_UNSET", "SUIPAY_MCP_FILE_ROOT is not a readable directory");
|
|
7075
|
+
}
|
|
7076
|
+
try {
|
|
7077
|
+
fileReal = realpathSync(raw);
|
|
7078
|
+
} catch (err) {
|
|
7079
|
+
const code = err.code;
|
|
7080
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
7081
|
+
return fail2("FILE_NOT_FOUND", "file does not exist");
|
|
7082
|
+
}
|
|
7083
|
+
if (code === "EACCES") {
|
|
7084
|
+
return fail2("FILE_EACCES", "file is not readable");
|
|
7085
|
+
}
|
|
7086
|
+
return fail2(
|
|
7087
|
+
"INVALID_FILE",
|
|
7088
|
+
`file could not be read (${code ?? "unknown error"})`
|
|
7089
|
+
);
|
|
7090
|
+
}
|
|
7091
|
+
if (!inside(rootReal, fileReal)) {
|
|
7092
|
+
return fail2("FILE_OUTSIDE_ROOT", "file must be under SUIPAY_MCP_FILE_ROOT");
|
|
7093
|
+
}
|
|
7094
|
+
const credsDir2 = dirname3(mcpCredentialsPath());
|
|
7095
|
+
let credsReal = credsDir2;
|
|
7096
|
+
try {
|
|
7097
|
+
credsReal = realpathSync(credsDir2);
|
|
7098
|
+
} catch {
|
|
7099
|
+
}
|
|
7100
|
+
if (inside(credsReal, fileReal)) {
|
|
7101
|
+
return fail2("FILE_FORBIDDEN", "file must not be under ~/.suipay");
|
|
7102
|
+
}
|
|
7103
|
+
return fileReal;
|
|
7104
|
+
}
|
|
7105
|
+
function readRegularFile(path) {
|
|
7106
|
+
if (constants.O_NOFOLLOW === void 0 || constants.O_NONBLOCK === void 0) {
|
|
7107
|
+
return fail2("INVALID_FILE", "symlink-safe nonblocking open is unavailable on this platform");
|
|
7108
|
+
}
|
|
7109
|
+
let fd;
|
|
7110
|
+
try {
|
|
7111
|
+
const flags = constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
7112
|
+
fd = openSync2(path, flags);
|
|
7113
|
+
const st = fstatSync(fd);
|
|
7114
|
+
if (!st.isFile()) {
|
|
7115
|
+
return fail2("INVALID_FILE", "file must be a regular file");
|
|
7116
|
+
}
|
|
7117
|
+
if (st.size > MCP_MAX_FILE_FIELD_BYTES) {
|
|
7118
|
+
return fail2(
|
|
7119
|
+
"FILE_TOO_LARGE",
|
|
7120
|
+
`file is ${st.size} bytes; exceeds ${MCP_MAX_FILE_FIELD_BYTES} decoded bytes`
|
|
7121
|
+
);
|
|
7122
|
+
}
|
|
7123
|
+
const bytes = readFileSync2(fd);
|
|
7124
|
+
if (bytes.byteLength === 0) {
|
|
7125
|
+
return fail2("EMPTY_FILE", "file is empty");
|
|
7126
|
+
}
|
|
7127
|
+
if (bytes.byteLength > MCP_MAX_FILE_FIELD_BYTES) {
|
|
7128
|
+
return fail2(
|
|
7129
|
+
"FILE_TOO_LARGE",
|
|
7130
|
+
`file is ${bytes.byteLength} bytes; exceeds ${MCP_MAX_FILE_FIELD_BYTES} decoded bytes`
|
|
7131
|
+
);
|
|
7132
|
+
}
|
|
7133
|
+
return bytes;
|
|
7134
|
+
} catch (err) {
|
|
7135
|
+
const code = err.code;
|
|
7136
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
7137
|
+
return fail2("FILE_NOT_FOUND", "file does not exist");
|
|
7138
|
+
}
|
|
7139
|
+
if (code === "ELOOP" || code === "EMLINK" || code === "ENXIO") {
|
|
7140
|
+
return fail2("INVALID_FILE", "file must be a regular file");
|
|
7141
|
+
}
|
|
7142
|
+
if (code === "EACCES") return fail2("FILE_EACCES", "file is not readable");
|
|
7143
|
+
return fail2(
|
|
7144
|
+
"INVALID_FILE",
|
|
7145
|
+
`file could not be read (${code ?? "unknown error"})`
|
|
7146
|
+
);
|
|
7147
|
+
} finally {
|
|
7148
|
+
if (fd !== void 0) {
|
|
7149
|
+
try {
|
|
7150
|
+
closeSync2(fd);
|
|
7151
|
+
} catch {
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
}
|
|
7156
|
+
function toCanonicalBase64(bytes) {
|
|
7157
|
+
return Buffer.from(bytes).toString("base64");
|
|
7158
|
+
}
|
|
7159
|
+
function jsonWithLocalFile(input) {
|
|
7160
|
+
const hasFile = input.file !== void 0 && input.file !== null;
|
|
7161
|
+
const hasField = input.fileField !== void 0 && input.fileField !== null;
|
|
7162
|
+
if (!hasFile && !hasField) return { json: input.json };
|
|
7163
|
+
if (hasFile !== hasField) {
|
|
7164
|
+
return {
|
|
7165
|
+
error: fail2(
|
|
7166
|
+
"INVALID_FILE_ARGS",
|
|
7167
|
+
"file and fileField are required together"
|
|
7168
|
+
)
|
|
7169
|
+
};
|
|
7170
|
+
}
|
|
7171
|
+
const method = parseMethod(input.method);
|
|
7172
|
+
if (typeof method !== "string") return { error: method };
|
|
7173
|
+
if (method !== "POST") {
|
|
7174
|
+
return { error: fail2("INVALID_FILE_ARGS", "file is only valid on POST") };
|
|
7175
|
+
}
|
|
7176
|
+
const field = parseField(input.fileField);
|
|
7177
|
+
if (typeof field !== "string") return { error: field };
|
|
7178
|
+
const path = parsePath(input.file);
|
|
7179
|
+
if (typeof path !== "string") return { error: path };
|
|
7180
|
+
let json;
|
|
7181
|
+
if (input.json === void 0) {
|
|
7182
|
+
json = {};
|
|
7183
|
+
} else if (typeof input.json === "object" && input.json !== null && !Array.isArray(input.json)) {
|
|
7184
|
+
json = { ...input.json };
|
|
7185
|
+
} else {
|
|
7186
|
+
return { error: fail2("MALFORMED_REQUEST", "POST requires json object") };
|
|
7187
|
+
}
|
|
7188
|
+
if (Object.prototype.hasOwnProperty.call(json, field)) {
|
|
7189
|
+
return {
|
|
7190
|
+
error: fail2(
|
|
7191
|
+
"FILE_FIELD_EXISTS",
|
|
7192
|
+
"json already has fileField; refuse to overwrite"
|
|
7193
|
+
)
|
|
7194
|
+
};
|
|
7195
|
+
}
|
|
7196
|
+
const contained = resolveContainedPath(path);
|
|
7197
|
+
if (typeof contained !== "string") return { error: contained };
|
|
7198
|
+
const bytes = readRegularFile(contained);
|
|
7199
|
+
if (!(bytes instanceof Uint8Array)) return { error: bytes };
|
|
7200
|
+
json[field] = toCanonicalBase64(bytes);
|
|
7201
|
+
return { json };
|
|
7202
|
+
}
|
|
7203
|
+
|
|
7005
7204
|
// src/tools.ts
|
|
7006
7205
|
function payerTrace(deps) {
|
|
7007
7206
|
const hooks = deps.trace;
|
|
@@ -7080,18 +7279,34 @@ function intentViolation(intent, prepared) {
|
|
|
7080
7279
|
}
|
|
7081
7280
|
async function pay(args, cfg, deps = {}) {
|
|
7082
7281
|
if (!args.url) return error("url is required");
|
|
7282
|
+
if (hasLocalFileArgs(args) && deps.transport === "http") {
|
|
7283
|
+
return closedPayError(
|
|
7284
|
+
"FILE_STDIO_ONLY",
|
|
7285
|
+
"file is stdio only; hosted /mcp does not read the buyer disk"
|
|
7286
|
+
);
|
|
7287
|
+
}
|
|
7083
7288
|
let intent;
|
|
7084
7289
|
try {
|
|
7085
7290
|
intent = normalizePayIntent(args);
|
|
7086
7291
|
} catch {
|
|
7087
7292
|
return closedPayError("INVALID_INTENT", "payment intent is invalid");
|
|
7088
7293
|
}
|
|
7294
|
+
let attached;
|
|
7295
|
+
try {
|
|
7296
|
+
attached = jsonWithLocalFile(args);
|
|
7297
|
+
} catch {
|
|
7298
|
+
return closedPayError("INVALID_FILE", "file could not be read");
|
|
7299
|
+
}
|
|
7300
|
+
if ("error" in attached) {
|
|
7301
|
+
return closedPayError(attached.error.code, attached.error.detail);
|
|
7302
|
+
}
|
|
7303
|
+
const paidArgs = { ...args, json: attached.json };
|
|
7089
7304
|
let request;
|
|
7090
7305
|
try {
|
|
7091
|
-
request = normalizeMcpPaidHttpRequest(
|
|
7306
|
+
request = normalizeMcpPaidHttpRequest(paidArgs);
|
|
7092
7307
|
} catch (err) {
|
|
7093
7308
|
const raw = err instanceof Error ? err.message : "";
|
|
7094
|
-
const detail = /method/i.test(raw) ? "method must be GET or POST" : /json/i.test(raw) ? "POST requires json" : "request could not be normalized";
|
|
7309
|
+
const detail = /too large/i.test(raw) ? `paid request body exceeds ${MAX_PAID_BODY_BYTES} bytes` : /method/i.test(raw) ? "method must be GET or POST" : /json/i.test(raw) ? "POST requires json" : "request could not be normalized";
|
|
7095
7310
|
return closedPayError("MALFORMED_REQUEST", detail);
|
|
7096
7311
|
}
|
|
7097
7312
|
if (deps.auth?.spendAccountPay) {
|
|
@@ -7624,6 +7839,14 @@ var TOOLS = [
|
|
|
7624
7839
|
type: "object",
|
|
7625
7840
|
description: "JSON request body for a POST. Sent as application/json and bound to the payment."
|
|
7626
7841
|
},
|
|
7842
|
+
file: {
|
|
7843
|
+
type: "string",
|
|
7844
|
+
description: "Absolute path under SUIPAY_MCP_FILE_ROOT (unset disables attach). Stdio POST only; requires fileField. Regular non-empty file whose decoded size fits the paid-body ceiling. Not a URL, FIFO, device, or path under ~/.suipay. Bytes fill json[fileField] as canonical base64."
|
|
7845
|
+
},
|
|
7846
|
+
fileField: {
|
|
7847
|
+
type: "string",
|
|
7848
|
+
description: "JSON key to set from file (e.g. contentBase64). Required with file. Pattern [A-Za-z][A-Za-z0-9_]{0,63}. Must not already be present on json."
|
|
7849
|
+
},
|
|
7627
7850
|
recipient: {
|
|
7628
7851
|
type: "string",
|
|
7629
7852
|
description: "Sui address you expect to be paid. The payment is refused, unsigned, if the prepared transaction pays anyone else."
|
|
@@ -7731,6 +7954,7 @@ function toolError(value) {
|
|
|
7731
7954
|
}
|
|
7732
7955
|
function createServer2(cfg, auth, trace, options = {}) {
|
|
7733
7956
|
const resolveAgentHeld = options.resolveAgentHeld ?? resolveAgentHeldSettlement;
|
|
7957
|
+
const transport = options.transport ?? "stdio";
|
|
7734
7958
|
const server = new Server(
|
|
7735
7959
|
{ name: "suipay", version: "1.0.0" },
|
|
7736
7960
|
{
|
|
@@ -7757,17 +7981,29 @@ function createServer2(cfg, auth, trace, options = {}) {
|
|
|
7757
7981
|
return await accessContext({ auth }, cfg);
|
|
7758
7982
|
case "pay": {
|
|
7759
7983
|
const agentHeld = auth?.spendAccountPay ? resolveAgentHeld(cfg, auth.spendAccountPay) : null;
|
|
7984
|
+
const fileArgs = {
|
|
7985
|
+
...args.file !== void 0 ? { file: args.file } : {},
|
|
7986
|
+
...args.fileField !== void 0 ? { fileField: args.fileField } : {}
|
|
7987
|
+
};
|
|
7988
|
+
if (transport === "http" && hasLocalFileArgs(fileArgs)) {
|
|
7989
|
+
return closedPayError(
|
|
7990
|
+
"FILE_STDIO_ONLY",
|
|
7991
|
+
"file is stdio only; hosted /mcp does not read the buyer disk"
|
|
7992
|
+
);
|
|
7993
|
+
}
|
|
7760
7994
|
return await pay(
|
|
7761
7995
|
{
|
|
7762
7996
|
url: String(args.url ?? ""),
|
|
7763
7997
|
...args.method !== void 0 ? { method: args.method } : {},
|
|
7764
7998
|
...args.json !== void 0 ? { json: args.json } : {},
|
|
7999
|
+
...transport === "stdio" ? fileArgs : {},
|
|
7765
8000
|
...args.recipient !== void 0 ? { recipient: args.recipient } : {},
|
|
7766
8001
|
...args.maxAmount !== void 0 ? { maxAmount: args.maxAmount } : {}
|
|
7767
8002
|
},
|
|
7768
8003
|
cfg,
|
|
7769
8004
|
{
|
|
7770
8005
|
auth,
|
|
8006
|
+
transport,
|
|
7771
8007
|
...trace ? { trace } : {},
|
|
7772
8008
|
...agentHeld ? { agentHeld } : {}
|
|
7773
8009
|
}
|
|
@@ -7856,6 +8092,11 @@ function canonicalMcpRequestId(id) {
|
|
|
7856
8092
|
function asRecord(value) {
|
|
7857
8093
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
7858
8094
|
}
|
|
8095
|
+
function redactPayFileArg(args) {
|
|
8096
|
+
const record = asRecord(args);
|
|
8097
|
+
if (!record || !("file" in record)) return args;
|
|
8098
|
+
return { ...record, file: "[redacted]" };
|
|
8099
|
+
}
|
|
7859
8100
|
function readEntry(message) {
|
|
7860
8101
|
const record = asRecord(message);
|
|
7861
8102
|
if (!record) return null;
|
|
@@ -7867,7 +8108,7 @@ function readEntry(message) {
|
|
|
7867
8108
|
jsonRpcId: typeof rawId === "string" || typeof rawId === "number" ? rawId : null,
|
|
7868
8109
|
requestId: canonicalMcpRequestId(rawId),
|
|
7869
8110
|
toolName: params && typeof params.name === "string" ? params.name : null,
|
|
7870
|
-
toolArguments: params ? params.arguments : void 0,
|
|
8111
|
+
toolArguments: params ? redactPayFileArg(params.arguments) : void 0,
|
|
7871
8112
|
clientName: clientInfo && typeof clientInfo.name === "string" ? clientInfo.name : null,
|
|
7872
8113
|
clientVersion: clientInfo && typeof clientInfo.version === "string" ? clientInfo.version : null,
|
|
7873
8114
|
protocolVersion: params && typeof params.protocolVersion === "string" ? params.protocolVersion : null
|
|
@@ -8297,7 +8538,10 @@ async function handleSuipayMcpHttpRequest(req, cfg, auth, trace, options) {
|
|
|
8297
8538
|
sessionIdGenerator: void 0,
|
|
8298
8539
|
enableJsonResponse: true
|
|
8299
8540
|
});
|
|
8300
|
-
const server = createServer2(cfg, auth, hooks,
|
|
8541
|
+
const server = createServer2(cfg, auth, hooks, {
|
|
8542
|
+
...options,
|
|
8543
|
+
transport: "http"
|
|
8544
|
+
});
|
|
8301
8545
|
await server.connect(transport);
|
|
8302
8546
|
return transport.handleRequest(request);
|
|
8303
8547
|
}
|
|
@@ -282,6 +282,14 @@ declare const TOOLS: readonly [{
|
|
|
282
282
|
readonly type: "object";
|
|
283
283
|
readonly description: "JSON request body for a POST. Sent as application/json and bound to the payment.";
|
|
284
284
|
};
|
|
285
|
+
readonly file: {
|
|
286
|
+
readonly type: "string";
|
|
287
|
+
readonly description: "Absolute path under SUIPAY_MCP_FILE_ROOT (unset disables attach). Stdio POST only; requires fileField. Regular non-empty file whose decoded size fits the paid-body ceiling. Not a URL, FIFO, device, or path under ~/.suipay. Bytes fill json[fileField] as canonical base64.";
|
|
288
|
+
};
|
|
289
|
+
readonly fileField: {
|
|
290
|
+
readonly type: "string";
|
|
291
|
+
readonly description: "JSON key to set from file (e.g. contentBase64). Required with file. Pattern [A-Za-z][A-Za-z0-9_]{0,63}. Must not already be present on json.";
|
|
292
|
+
};
|
|
285
293
|
readonly recipient: {
|
|
286
294
|
readonly type: "string";
|
|
287
295
|
readonly description: "Sui address you expect to be paid. The payment is refused, unsigned, if the prepared transaction pays anyone else.";
|
|
@@ -370,6 +378,11 @@ interface McpServerOptions {
|
|
|
370
378
|
* does not already authorize.
|
|
371
379
|
*/
|
|
372
380
|
resolveAgentHeld?: AgentHeldResolver;
|
|
381
|
+
/**
|
|
382
|
+
* Wire that created this server. Hosted HTTP never forwards `file` into `pay`.
|
|
383
|
+
* Stdio is the default so existing tests stay local.
|
|
384
|
+
*/
|
|
385
|
+
transport?: 'stdio' | 'http';
|
|
373
386
|
}
|
|
374
387
|
declare function createServer(cfg: McpConfig, auth?: McpAuthContext, trace?: McpTraceHooks, options?: McpServerOptions): Server;
|
|
375
388
|
|
package/dist/http.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { m as handleSuipayMcpHttpRequest } from './http-
|
|
1
|
+
export { m as handleSuipayMcpHttpRequest } from './http-DBP9qgVw.js';
|
|
2
2
|
import '@cmdoss/suipay-core/policy/grant-target';
|
|
3
3
|
import '@cmdoss/suipay-sdk/mcp/access-context';
|
|
4
4
|
import '@cmdoss/suipay-sdk/buyer/settlement-dto';
|
package/dist/http.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Wallet } from '@cmdoss/suipay-core/chain/types';
|
|
2
|
-
import { M as McpConfig, a as McpAuthContext, b as McpTraceHooks } from './http-
|
|
3
|
-
export { A as AgentHeldResolver, c as AgentHeldSettlement, d as MCP_TARGET_PRESETS, e as McpEnvelope, f as McpEnvelopeEntry, g as McpServerOptions, h as McpTarget, i as McpTraceContext, j as McpTraceRuntime, P as PACKAGE_NAME, T as TOOLS, k as canonicalMcpRequestId, l as createServer, m as handleSuipayMcpHttpRequest, n as inspectMcpEnvelope, o as loadMcpConfig, p as parseMcpTargetFlag, r as resetAgentHeldProcessLedger, q as resolveAgentHeldSettlement, s as resolveMcpTarget, t as sameMcpGateway, u as traceMcpHttpRequest } from './http-
|
|
2
|
+
import { M as McpConfig, a as McpAuthContext, b as McpTraceHooks } from './http-DBP9qgVw.js';
|
|
3
|
+
export { A as AgentHeldResolver, c as AgentHeldSettlement, d as MCP_TARGET_PRESETS, e as McpEnvelope, f as McpEnvelopeEntry, g as McpServerOptions, h as McpTarget, i as McpTraceContext, j as McpTraceRuntime, P as PACKAGE_NAME, T as TOOLS, k as canonicalMcpRequestId, l as createServer, m as handleSuipayMcpHttpRequest, n as inspectMcpEnvelope, o as loadMcpConfig, p as parseMcpTargetFlag, r as resetAgentHeldProcessLedger, q as resolveAgentHeldSettlement, s as resolveMcpTarget, t as sameMcpGateway, u as traceMcpHttpRequest } from './http-DBP9qgVw.js';
|
|
4
4
|
import { GrantSnapshot } from '@cmdoss/suipay-core/policy/grant-target';
|
|
5
5
|
export { resolveGrantTarget } from '@cmdoss/suipay-core/policy/grant-target';
|
|
6
6
|
import { PaidHttpRequest } from '@cmdoss/suipay-sdk/protocol/paid-http-request';
|
|
@@ -347,6 +347,8 @@ interface ToolDeps {
|
|
|
347
347
|
profileLoader?: () => PaymentProfile | null;
|
|
348
348
|
/** Remote HTTP auth (grant + scope + optional shared-pool pay session). */
|
|
349
349
|
auth?: McpAuthContext;
|
|
350
|
+
/** Wire transport. Hosted HTTP never reads a buyer path. */
|
|
351
|
+
transport?: 'stdio' | 'http';
|
|
350
352
|
/**
|
|
351
353
|
* Proxy-resolved live trace for this MCP request. Observation only: it never
|
|
352
354
|
* changes which rail runs, what is paid, or whether a payment is allowed.
|
|
@@ -377,6 +379,8 @@ declare function pay(args: {
|
|
|
377
379
|
url: string;
|
|
378
380
|
method?: unknown;
|
|
379
381
|
json?: unknown;
|
|
382
|
+
file?: unknown;
|
|
383
|
+
fileField?: unknown;
|
|
380
384
|
recipient?: unknown;
|
|
381
385
|
maxAmount?: unknown;
|
|
382
386
|
}, cfg: McpConfig, deps?: ToolDeps): Promise<ToolResult>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadBootProfile,
|
|
3
3
|
main
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AEV6Q6JC.js";
|
|
5
5
|
import {
|
|
6
6
|
MCP_TARGET_PRESETS,
|
|
7
7
|
PACKAGE_NAME,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
settlePreparedMcpPayment,
|
|
27
27
|
traceMcpHttpRequest,
|
|
28
28
|
verifySpendAccountPayReconstructs
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-UTKVBAEZ.js";
|
|
30
30
|
export {
|
|
31
31
|
MCP_TARGET_PRESETS,
|
|
32
32
|
PACKAGE_NAME,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmdoss/suipay-mcp",
|
|
3
|
-
"version": "0.2.2-dev.
|
|
3
|
+
"version": "0.2.2-dev.6",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "SuiPay MCP
|
|
5
|
+
"description": "SuiPay MCP \u2014 pay MPP-gated APIs from a spend-account grant with an agent-held delegate key.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|