@floatonnear/mcp 0.0.0-stage → 0.1.0

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/sign.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The ONLY place in this server that touches a key.
3
+ *
4
+ * It signs exactly one method — `request_payment` — with the restricted key
5
+ * issued by `create_agent_account`. That key is cryptographically incapable of
6
+ * doing anything else: its `receiver_id` is the vault, its `method_names` is
7
+ * `["request_payment"]`, and a NEAR function-call key cannot attach a deposit
8
+ * at all. So the worst a compromised copy of this server can do is make
9
+ * payments the vault's on-chain policy already permits.
10
+ *
11
+ * Owner methods are deliberately NOT here. They are emitted as unsigned
12
+ * payloads (see payloads.ts) for the human to approve in their own wallet,
13
+ * which keeps this server out of the trust path for anything that changes
14
+ * policy — the property that distinguishes the design from a custodial agent
15
+ * wallet.
16
+ */
17
+ import { Account, JsonRpcProvider, KeyPair, KeyPairSigner } from "near-api-js";
18
+ /** Must match AGENT_ALLOWED_METHOD in the contract; pinned by constants.test.ts. */
19
+ const ALLOWED_METHOD = "request_payment";
20
+ export function buildRoute(rail, intents) {
21
+ if (rail === "near")
22
+ return "near_direct";
23
+ if (!intents) {
24
+ throw new Error("rail 'intents' requires depositAddress, quoteDeadlineNs and signature");
25
+ }
26
+ if (!/^[0-9]+$/.test(intents.quoteDeadlineNs)) {
27
+ // U64 crosses the ABI as a decimal string; a JSON number loses precision
28
+ // above 2^53 and a nanosecond deadline is far past that.
29
+ throw new Error(`quoteDeadlineNs must be a decimal string, got ${intents.quoteDeadlineNs}`);
30
+ }
31
+ if (!intents.depositAddress) {
32
+ // An empty deposit address is what a DRY quote returns. Sending one would
33
+ // transfer real funds to "", which the token contract rejects only after
34
+ // the vault has already reserved the budget.
35
+ throw new Error("depositAddress is empty — a dry quote cannot be paid");
36
+ }
37
+ return {
38
+ intents: {
39
+ deposit_address: intents.depositAddress,
40
+ quote_deadline_ns: intents.quoteDeadlineNs,
41
+ signature: intents.signature,
42
+ },
43
+ };
44
+ }
45
+ export async function sendRequestPayment(a) {
46
+ const provider = new JsonRpcProvider({ url: a.rpcUrl });
47
+ const signer = new KeyPairSigner(KeyPair.fromString(a.agentKey));
48
+ const account = new Account(a.agentId, provider, signer);
49
+ return account.callFunction({
50
+ contractId: a.vault,
51
+ methodName: ALLOWED_METHOD,
52
+ args: {
53
+ request_id: a.requestId,
54
+ token: a.token,
55
+ // U128 crosses the ABI as a decimal string; a JSON number would lose
56
+ // precision above 2^53.
57
+ amount: a.amount.toString(),
58
+ recipient: a.recipient,
59
+ memo: a.memo,
60
+ // Omitted entirely when undefined: the contract takes `Option<Route>`
61
+ // and a missing field deserialises to None, which is the NEAR rail.
62
+ ...(a.route === undefined ? {} : { route: a.route }),
63
+ },
64
+ gas: BigInt(a.gasTgas) * 10n ** 12n,
65
+ // Hardcoded zero, and not a parameter. A function-call key that attaches
66
+ // any deposit is rejected by the protocol with
67
+ // InvalidAccessKeyError(DepositWithFunctionCall) — so a non-zero value
68
+ // here would not be a security hole, it would simply make every payment
69
+ // fail. Keeping it a constant means that cannot be introduced by a caller.
70
+ deposit: 0n,
71
+ });
72
+ }
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
- {
2
- "name": "@floatonnear/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@floatonnear/mcp",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "MCP server for the hierarchical budget vault. The agent key signs request_payment only; owner methods emit unsigned payloads for wallet approval.",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "mcp": "dist/server.js",
9
+ "float-mcp": "dist/server.js"
10
+ },
11
+ "files": [
12
+ "dist/"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "prepack": "npm run build",
17
+ "test": "vitest run",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit"
19
+ },
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.32.2",
22
+ "@noble/curves": "^1.9.7",
23
+ "@noble/hashes": "^1.8.0",
24
+ "near-api-js": "^7.4.0",
25
+ "zod": "^4.6.7"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^24.0.0",
29
+ "typescript": "^5.9.0",
30
+ "vitest": "^5.0.5"
31
+ },
32
+ "exports": {
33
+ ".": "./dist/index.js",
34
+ "./amounts": "./dist/amounts.js",
35
+ "./sign": "./dist/sign.js",
36
+ "./payloads": "./dist/payloads.js",
37
+ "./quote": {
38
+ "types": "./dist/quote.d.ts",
39
+ "import": "./dist/quote.js"
40
+ }
41
+ }
42
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.