@cmdoss/suipay-mcp 0.2.2-dev.1 → 0.2.2-dev.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/README.md +11 -1
- package/dist/bin/suipay.js +2 -2
- package/dist/{chunk-NVTYOOMY.js → chunk-62LFIYB7.js} +132 -1
- package/dist/{chunk-K6WM4Z7H.js → chunk-AFEXT5X4.js} +1 -1
- package/dist/{http-B4YTVw0k.d.ts → http-IivHoDeb.d.ts} +8 -0
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
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`); 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,16 @@ 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
|
+
|
|
81
|
+
```
|
|
82
|
+
url: https://gateway.example/v1/storage/store
|
|
83
|
+
method: POST
|
|
84
|
+
json: {"contentType":"text/plain"}
|
|
85
|
+
file: /absolute/path/to/note.txt
|
|
86
|
+
fileField: contentBase64
|
|
87
|
+
```
|
|
88
|
+
|
|
79
89
|
`pay` uses SDK `createPayer` internally. A delivered image is returned as an MCP
|
|
80
90
|
image block, not as a base64 wall in the JSON summary.
|
|
81
91
|
|
package/dist/bin/suipay.js
CHANGED
|
@@ -6918,6 +6918,115 @@ function settlePayResult(result) {
|
|
|
6918
6918
|
return error(result);
|
|
6919
6919
|
}
|
|
6920
6920
|
|
|
6921
|
+
// src/pay-file.ts
|
|
6922
|
+
import { isAbsolute } from "path";
|
|
6923
|
+
import { openSync as openSync2, closeSync as closeSync2, fstatSync, readFileSync as readFileSync2, constants } from "fs";
|
|
6924
|
+
var MCP_MAX_FILE_FIELD_BYTES = 32 * 1024;
|
|
6925
|
+
var FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
|
|
6926
|
+
var FORBIDDEN = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
6927
|
+
function fail2(code, detail) {
|
|
6928
|
+
return { code, detail };
|
|
6929
|
+
}
|
|
6930
|
+
function parseField(value) {
|
|
6931
|
+
if (typeof value !== "string" || !FIELD.test(value) || FORBIDDEN.has(value)) {
|
|
6932
|
+
return fail2("INVALID_FILE_FIELD", "fileField must be a simple JSON key");
|
|
6933
|
+
}
|
|
6934
|
+
return value;
|
|
6935
|
+
}
|
|
6936
|
+
function parsePath(value) {
|
|
6937
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
|
|
6938
|
+
return fail2("INVALID_FILE", "file must be a non-empty absolute path");
|
|
6939
|
+
}
|
|
6940
|
+
if (!isAbsolute(value) || value.includes("://")) {
|
|
6941
|
+
return fail2("INVALID_FILE", "file must be an absolute filesystem path, not a URL");
|
|
6942
|
+
}
|
|
6943
|
+
return value;
|
|
6944
|
+
}
|
|
6945
|
+
function readRegularFile(path) {
|
|
6946
|
+
let fd;
|
|
6947
|
+
try {
|
|
6948
|
+
const flags = constants.O_RDONLY | (constants.O_NOFOLLOW !== void 0 ? constants.O_NOFOLLOW : 0);
|
|
6949
|
+
fd = openSync2(path, flags);
|
|
6950
|
+
const st = fstatSync(fd);
|
|
6951
|
+
if (!st.isFile()) {
|
|
6952
|
+
return fail2("INVALID_FILE", "file must be a regular file");
|
|
6953
|
+
}
|
|
6954
|
+
if (st.size === 0) {
|
|
6955
|
+
return fail2("EMPTY_FILE", "file is empty");
|
|
6956
|
+
}
|
|
6957
|
+
if (st.size > MCP_MAX_FILE_FIELD_BYTES) {
|
|
6958
|
+
return fail2(
|
|
6959
|
+
"FILE_TOO_LARGE",
|
|
6960
|
+
`file exceeds ${MCP_MAX_FILE_FIELD_BYTES} decoded bytes`
|
|
6961
|
+
);
|
|
6962
|
+
}
|
|
6963
|
+
const bytes = readFileSync2(fd);
|
|
6964
|
+
if (bytes.byteLength === 0) {
|
|
6965
|
+
return fail2("EMPTY_FILE", "file is empty");
|
|
6966
|
+
}
|
|
6967
|
+
if (bytes.byteLength > MCP_MAX_FILE_FIELD_BYTES) {
|
|
6968
|
+
return fail2(
|
|
6969
|
+
"FILE_TOO_LARGE",
|
|
6970
|
+
`file exceeds ${MCP_MAX_FILE_FIELD_BYTES} decoded bytes`
|
|
6971
|
+
);
|
|
6972
|
+
}
|
|
6973
|
+
return bytes;
|
|
6974
|
+
} catch (err) {
|
|
6975
|
+
const code = err.code;
|
|
6976
|
+
if (code === "ENOENT") return fail2("FILE_NOT_FOUND", "file does not exist");
|
|
6977
|
+
if (code === "ELOOP" || code === "EMLINK") {
|
|
6978
|
+
return fail2("INVALID_FILE", "file must be a regular file");
|
|
6979
|
+
}
|
|
6980
|
+
return fail2("INVALID_FILE", "file could not be read");
|
|
6981
|
+
} finally {
|
|
6982
|
+
if (fd !== void 0) closeSync2(fd);
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
function toCanonicalBase64(bytes) {
|
|
6986
|
+
return Buffer.from(bytes).toString("base64");
|
|
6987
|
+
}
|
|
6988
|
+
function jsonWithLocalFile(input) {
|
|
6989
|
+
const hasFile = input.file !== void 0 && input.file !== null && input.file !== "";
|
|
6990
|
+
const hasField = input.fileField !== void 0 && input.fileField !== null && input.fileField !== "";
|
|
6991
|
+
if (!hasFile && !hasField) return { json: input.json };
|
|
6992
|
+
if (hasFile !== hasField) {
|
|
6993
|
+
return {
|
|
6994
|
+
error: fail2(
|
|
6995
|
+
"INVALID_FILE_ARGS",
|
|
6996
|
+
"file and fileField are required together"
|
|
6997
|
+
)
|
|
6998
|
+
};
|
|
6999
|
+
}
|
|
7000
|
+
const method = input.method === void 0 || input.method === null || input.method === "" ? "GET" : String(input.method).trim().toUpperCase();
|
|
7001
|
+
if (method !== "POST") {
|
|
7002
|
+
return { error: fail2("INVALID_FILE_ARGS", "file is only valid on POST") };
|
|
7003
|
+
}
|
|
7004
|
+
const field = parseField(input.fileField);
|
|
7005
|
+
if (typeof field !== "string") return { error: field };
|
|
7006
|
+
const path = parsePath(input.file);
|
|
7007
|
+
if (typeof path !== "string") return { error: path };
|
|
7008
|
+
let json;
|
|
7009
|
+
if (input.json === void 0) {
|
|
7010
|
+
json = {};
|
|
7011
|
+
} else if (typeof input.json === "object" && input.json !== null && !Array.isArray(input.json)) {
|
|
7012
|
+
json = { ...input.json };
|
|
7013
|
+
} else {
|
|
7014
|
+
return { error: fail2("MALFORMED_REQUEST", "POST requires json object") };
|
|
7015
|
+
}
|
|
7016
|
+
if (Object.prototype.hasOwnProperty.call(json, field)) {
|
|
7017
|
+
return {
|
|
7018
|
+
error: fail2(
|
|
7019
|
+
"FILE_FIELD_EXISTS",
|
|
7020
|
+
"json already has fileField; refuse to overwrite"
|
|
7021
|
+
)
|
|
7022
|
+
};
|
|
7023
|
+
}
|
|
7024
|
+
const bytes = readRegularFile(path);
|
|
7025
|
+
if (!(bytes instanceof Uint8Array)) return { error: bytes };
|
|
7026
|
+
json[field] = toCanonicalBase64(bytes);
|
|
7027
|
+
return { json };
|
|
7028
|
+
}
|
|
7029
|
+
|
|
6921
7030
|
// src/tools.ts
|
|
6922
7031
|
function payerTrace(deps) {
|
|
6923
7032
|
const hooks = deps.trace;
|
|
@@ -6996,15 +7105,27 @@ function intentViolation(intent, prepared) {
|
|
|
6996
7105
|
}
|
|
6997
7106
|
async function pay(args, cfg, deps = {}) {
|
|
6998
7107
|
if (!args.url) return error("url is required");
|
|
7108
|
+
const hasFileArg = args.file !== void 0 && args.file !== null && args.file !== "" || args.fileField !== void 0 && args.fileField !== null && args.fileField !== "";
|
|
7109
|
+
if (hasFileArg && deps.auth) {
|
|
7110
|
+
return closedPayError(
|
|
7111
|
+
"FILE_STDIO_ONLY",
|
|
7112
|
+
"file is stdio only; hosted /mcp does not read the buyer disk"
|
|
7113
|
+
);
|
|
7114
|
+
}
|
|
6999
7115
|
let intent;
|
|
7000
7116
|
try {
|
|
7001
7117
|
intent = normalizePayIntent(args);
|
|
7002
7118
|
} catch {
|
|
7003
7119
|
return closedPayError("INVALID_INTENT", "payment intent is invalid");
|
|
7004
7120
|
}
|
|
7121
|
+
const attached = jsonWithLocalFile(args);
|
|
7122
|
+
if ("error" in attached) {
|
|
7123
|
+
return closedPayError(attached.error.code, attached.error.detail);
|
|
7124
|
+
}
|
|
7125
|
+
const paidArgs = { ...args, json: attached.json };
|
|
7005
7126
|
let request;
|
|
7006
7127
|
try {
|
|
7007
|
-
request = normalizeMcpPaidHttpRequest(
|
|
7128
|
+
request = normalizeMcpPaidHttpRequest(paidArgs);
|
|
7008
7129
|
} catch (err) {
|
|
7009
7130
|
const raw = err instanceof Error ? err.message : "";
|
|
7010
7131
|
const detail = /method/i.test(raw) ? "method must be GET or POST" : /json/i.test(raw) ? "POST requires json" : "request could not be normalized";
|
|
@@ -7540,6 +7661,14 @@ var TOOLS = [
|
|
|
7540
7661
|
type: "object",
|
|
7541
7662
|
description: "JSON request body for a POST. Sent as application/json and bound to the payment."
|
|
7542
7663
|
},
|
|
7664
|
+
file: {
|
|
7665
|
+
type: "string",
|
|
7666
|
+
description: "Absolute local path whose bytes fill json[fileField] as canonical base64. Stdio only; requires fileField. GET must omit this."
|
|
7667
|
+
},
|
|
7668
|
+
fileField: {
|
|
7669
|
+
type: "string",
|
|
7670
|
+
description: "JSON key to set from file (e.g. contentBase64). Required with file. Must not already be present on json."
|
|
7671
|
+
},
|
|
7543
7672
|
recipient: {
|
|
7544
7673
|
type: "string",
|
|
7545
7674
|
description: "Sui address you expect to be paid. The payment is refused, unsigned, if the prepared transaction pays anyone else."
|
|
@@ -7678,6 +7807,8 @@ function createServer2(cfg, auth, trace, options = {}) {
|
|
|
7678
7807
|
url: String(args.url ?? ""),
|
|
7679
7808
|
...args.method !== void 0 ? { method: args.method } : {},
|
|
7680
7809
|
...args.json !== void 0 ? { json: args.json } : {},
|
|
7810
|
+
...args.file !== void 0 ? { file: args.file } : {},
|
|
7811
|
+
...args.fileField !== void 0 ? { fileField: args.fileField } : {},
|
|
7681
7812
|
...args.recipient !== void 0 ? { recipient: args.recipient } : {},
|
|
7682
7813
|
...args.maxAmount !== void 0 ? { maxAmount: args.maxAmount } : {}
|
|
7683
7814
|
},
|
|
@@ -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 local path whose bytes fill json[fileField] as canonical base64. Stdio only; requires fileField. GET must omit this.";
|
|
288
|
+
};
|
|
289
|
+
readonly fileField: {
|
|
290
|
+
readonly type: "string";
|
|
291
|
+
readonly description: "JSON key to set from file (e.g. contentBase64). Required with file. 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.";
|
package/dist/http.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { m as handleSuipayMcpHttpRequest } from './http-
|
|
1
|
+
export { m as handleSuipayMcpHttpRequest } from './http-IivHoDeb.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-IivHoDeb.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-IivHoDeb.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';
|
|
@@ -382,6 +382,8 @@ declare function pay(args: {
|
|
|
382
382
|
url: string;
|
|
383
383
|
method?: unknown;
|
|
384
384
|
json?: unknown;
|
|
385
|
+
file?: unknown;
|
|
386
|
+
fileField?: unknown;
|
|
385
387
|
recipient?: unknown;
|
|
386
388
|
maxAmount?: unknown;
|
|
387
389
|
}, 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-AFEXT5X4.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-62LFIYB7.js";
|
|
30
30
|
export {
|
|
31
31
|
MCP_TARGET_PRESETS,
|
|
32
32
|
PACKAGE_NAME,
|
package/package.json
CHANGED