@forestrie/mcp-register 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +178 -2
- package/bin/mcp-register.mjs +2 -0
- package/dist/core/base64.d.ts +7 -0
- package/dist/core/base64.d.ts.map +1 -0
- package/dist/core/base64.js +20 -0
- package/dist/core/base64.js.map +1 -0
- package/dist/core/delegation.d.ts +56 -0
- package/dist/core/delegation.d.ts.map +1 -0
- package/dist/core/delegation.js +130 -0
- package/dist/core/delegation.js.map +1 -0
- package/dist/core/grant.d.ts +41 -0
- package/dist/core/grant.d.ts.map +1 -0
- package/dist/core/grant.js +78 -0
- package/dist/core/grant.js.map +1 -0
- package/dist/core/hex.d.ts +4 -0
- package/dist/core/hex.d.ts.map +1 -0
- package/dist/core/hex.js +18 -0
- package/dist/core/hex.js.map +1 -0
- package/dist/core/index.d.ts +28 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +21 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/issue.d.ts +38 -0
- package/dist/core/issue.d.ts.map +1 -0
- package/dist/core/issue.js +135 -0
- package/dist/core/issue.js.map +1 -0
- package/dist/core/limits.d.ts +9 -0
- package/dist/core/limits.d.ts.map +1 -0
- package/dist/core/limits.js +9 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/scrapi.d.ts +66 -0
- package/dist/core/scrapi.d.ts.map +1 -0
- package/dist/core/scrapi.js +174 -0
- package/dist/core/scrapi.js.map +1 -0
- package/dist/core/signer.d.ts +24 -0
- package/dist/core/signer.d.ts.map +1 -0
- package/dist/core/signer.js +2 -0
- package/dist/core/signer.js.map +1 -0
- package/dist/core/statement.d.ts +26 -0
- package/dist/core/statement.d.ts.map +1 -0
- package/dist/core/statement.js +70 -0
- package/dist/core/statement.js.map +1 -0
- package/dist/core/version.d.ts +23 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +23 -0
- package/dist/core/version.js.map +1 -0
- package/dist/core/x402.d.ts +99 -0
- package/dist/core/x402.d.ts.map +1 -0
- package/dist/core/x402.js +202 -0
- package/dist/core/x402.js.map +1 -0
- package/dist/net/delegate.d.ts +56 -0
- package/dist/net/delegate.d.ts.map +1 -0
- package/dist/net/delegate.js +182 -0
- package/dist/net/delegate.js.map +1 -0
- package/dist/net/http.d.ts +17 -0
- package/dist/net/http.d.ts.map +1 -0
- package/dist/net/http.js +52 -0
- package/dist/net/http.js.map +1 -0
- package/dist/net/index.d.ts +14 -0
- package/dist/net/index.d.ts.map +1 -0
- package/dist/net/index.js +13 -0
- package/dist/net/index.js.map +1 -0
- package/dist/net/write.d.ts +55 -0
- package/dist/net/write.d.ts.map +1 -0
- package/dist/net/write.js +46 -0
- package/dist/net/write.js.map +1 -0
- package/dist/node/cli.d.ts +8 -0
- package/dist/node/cli.d.ts.map +1 -0
- package/dist/node/cli.js +143 -0
- package/dist/node/cli.js.map +1 -0
- package/dist/node/grant-store.d.ts +49 -0
- package/dist/node/grant-store.d.ts.map +1 -0
- package/dist/node/grant-store.js +95 -0
- package/dist/node/grant-store.js.map +1 -0
- package/dist/node/grant-tools.d.ts +29 -0
- package/dist/node/grant-tools.d.ts.map +1 -0
- package/dist/node/grant-tools.js +498 -0
- package/dist/node/grant-tools.js.map +1 -0
- package/dist/node/ledger.d.ts +26 -0
- package/dist/node/ledger.d.ts.map +1 -0
- package/dist/node/ledger.js +78 -0
- package/dist/node/ledger.js.map +1 -0
- package/dist/node/payer.d.ts +48 -0
- package/dist/node/payer.d.ts.map +1 -0
- package/dist/node/payer.js +170 -0
- package/dist/node/payer.js.map +1 -0
- package/dist/node/resolve-input.d.ts +19 -0
- package/dist/node/resolve-input.d.ts.map +1 -0
- package/dist/node/resolve-input.js +76 -0
- package/dist/node/resolve-input.js.map +1 -0
- package/dist/node/server.d.ts +47 -0
- package/dist/node/server.d.ts.map +1 -0
- package/dist/node/server.js +212 -0
- package/dist/node/server.js.map +1 -0
- package/dist/node/signer-file.d.ts +34 -0
- package/dist/node/signer-file.d.ts.map +1 -0
- package/dist/node/signer-file.js +183 -0
- package/dist/node/signer-file.js.map +1 -0
- package/dist/node/store.d.ts +40 -0
- package/dist/node/store.d.ts.map +1 -0
- package/dist/node/store.js +117 -0
- package/dist/node/store.js.map +1 -0
- package/dist/node/text.d.ts +29 -0
- package/dist/node/text.d.ts.map +1 -0
- package/dist/node/text.js +124 -0
- package/dist/node/text.js.map +1 -0
- package/dist/node/tools.d.ts +118 -0
- package/dist/node/tools.d.ts.map +1 -0
- package/dist/node/tools.js +548 -0
- package/dist/node/tools.js.map +1 -0
- package/package.json +108 -3
- package/src/core/base64.ts +18 -0
- package/src/core/delegation.ts +239 -0
- package/src/core/grant.ts +127 -0
- package/src/core/hex.ts +18 -0
- package/src/core/index.ts +84 -0
- package/src/core/issue.ts +209 -0
- package/src/core/limits.ts +8 -0
- package/src/core/scrapi.ts +236 -0
- package/src/core/signer.ts +23 -0
- package/src/core/statement.ts +133 -0
- package/src/core/version.ts +25 -0
- package/src/core/x402.ts +314 -0
- package/src/net/delegate.ts +274 -0
- package/src/net/http.ts +71 -0
- package/src/net/index.ts +26 -0
- package/src/net/write.ts +124 -0
- package/src/node/cli.ts +170 -0
- package/src/node/grant-store.ts +154 -0
- package/src/node/grant-tools.ts +707 -0
- package/src/node/ledger.ts +98 -0
- package/src/node/payer.ts +241 -0
- package/src/node/resolve-input.ts +96 -0
- package/src/node/server.ts +332 -0
- package/src/node/signer-file.ts +251 -0
- package/src/node/store.ts +175 -0
- package/src/node/text.ts +153 -0
- package/src/node/tools.ts +810 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type PaymentCaps, type PaymentOption, type PaymentRequired } from "../core/x402.js";
|
|
2
|
+
export interface PayerBackend {
|
|
3
|
+
/** The paying account, `0x…`: public, safe to report. */
|
|
4
|
+
readonly address: string;
|
|
5
|
+
/** Where the key lives, for humans: never key material. */
|
|
6
|
+
readonly description: string;
|
|
7
|
+
/**
|
|
8
|
+
* Sign a payment for exactly `option` (already checked against the caps)
|
|
9
|
+
* and return the `X-PAYMENT` header value: base64 of the x402 v2 payment
|
|
10
|
+
* payload. The authorisation MUST carry `nonce`; register_grant refuses
|
|
11
|
+
* to send one that does not.
|
|
12
|
+
*/
|
|
13
|
+
pay(request: PayRequest): Promise<string>;
|
|
14
|
+
}
|
|
15
|
+
export interface PayRequest {
|
|
16
|
+
required: PaymentRequired;
|
|
17
|
+
option: PaymentOption;
|
|
18
|
+
/** The EIP-3009 nonce, `0x` + 64 hex, derived from the grant. */
|
|
19
|
+
nonce: string;
|
|
20
|
+
/** Seconds since the epoch; validBefore is this plus maxTimeoutSeconds. */
|
|
21
|
+
nowSeconds: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class PayerError extends Error {
|
|
24
|
+
readonly code: "payer_unavailable" | "payer_misconfigured";
|
|
25
|
+
constructor(code: "payer_unavailable" | "payer_misconfigured", message: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load an EVM private key (`0x` + 64 hex, one line) from `path`. Refused,
|
|
29
|
+
* as the log key is, when the path is a symlink or the file is accessible to
|
|
30
|
+
* group or others.
|
|
31
|
+
*/
|
|
32
|
+
export declare function openKeyFilePayer(path: string): PayerBackend;
|
|
33
|
+
/** An EIP-3009 "exact" payer over a viem account. */
|
|
34
|
+
export declare function evmPayer(privateKey: `0x${string}`, description: string): PayerBackend;
|
|
35
|
+
/**
|
|
36
|
+
* The caps, from configuration. Both amount caps are required once a payer
|
|
37
|
+
* is configured: a payer with no cap is refused, never defaulted.
|
|
38
|
+
*
|
|
39
|
+
* - `MCP_REGISTER_PAY_MAX_PER_CALL`, `MCP_REGISTER_PAY_MAX_PER_DAY`: atomic
|
|
40
|
+
* units of the asset (USDC has 6 decimals: 10000 is $0.01);
|
|
41
|
+
* - `MCP_REGISTER_PAY_NETWORK` (default Base Sepolia, `eip155:84532`) and
|
|
42
|
+
* `MCP_REGISTER_PAY_ASSET` (default Circle's USDC on Base Sepolia);
|
|
43
|
+
* - `MCP_REGISTER_PAY_TO`: optional, the only payee;
|
|
44
|
+
* - `MCP_REGISTER_PAY_ALLOW_MAINNET=1`: pay on a network that is not a
|
|
45
|
+
* testnet. Off by default.
|
|
46
|
+
*/
|
|
47
|
+
export declare function readPaymentCaps(env: Record<string, string | undefined>): PaymentCaps;
|
|
48
|
+
//# sourceMappingURL=payer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payer.d.ts","sourceRoot":"","sources":["../../src/node/payer.ts"],"names":[],"mappings":"AA0BA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAW,SAAQ,KAAK;IAEjC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,GAAG,qBAAqB;gBAAjD,IAAI,EAAE,mBAAmB,GAAG,qBAAqB,EAC1D,OAAO,EAAE,MAAM;CAIlB;AAMD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA2B3D;AAaD,qDAAqD;AACrD,wBAAgB,QAAQ,CACtB,UAAU,EAAE,KAAK,MAAM,EAAE,EACzB,WAAW,EAAE,MAAM,GAClB,YAAY,CAuDd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,WAAW,CAkDb"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The payer backend: what pays canopy's 402 when register_grant meets one.
|
|
3
|
+
*
|
|
4
|
+
* Separate from the log key and the authority key (D6): those are ES256 and
|
|
5
|
+
* hold no funds; the payer is an EVM account that holds testnet USDC and
|
|
6
|
+
* signs only standard x402 authorisations (EIP-3009
|
|
7
|
+
* `transferWithAuthorization`, the "exact" scheme). The default backend is
|
|
8
|
+
* a viem account loaded from a local key file. An embedder passes its own
|
|
9
|
+
* `PayerBackend` (a hosted wallet) to `createServer({ payer })`.
|
|
10
|
+
*
|
|
11
|
+
* The default backend builds the x402 v2 payment payload exactly as the
|
|
12
|
+
* upstream client does (`@x402/evm`'s `createEIP3009Payload`), with one
|
|
13
|
+
* difference: the nonce is the one register_grant derives from the grant
|
|
14
|
+
* (`grantPaymentNonce`), where upstream draws a random one and offers no way
|
|
15
|
+
* to supply it. A derived nonce is what makes paying twice for one grant
|
|
16
|
+
* impossible rather than merely avoided. `test/node/payer.test.ts` checks
|
|
17
|
+
* the payload against the upstream client's, field for field.
|
|
18
|
+
*
|
|
19
|
+
* The payer key never crosses the MCP boundary: no tool takes or returns it,
|
|
20
|
+
* the file is refused if it is a symlink or readable by others, and no
|
|
21
|
+
* `{path}` input may read it. The caps are configuration
|
|
22
|
+
* (`readPaymentCaps`), never tool arguments.
|
|
23
|
+
*/
|
|
24
|
+
import { lstatSync, readFileSync } from "node:fs";
|
|
25
|
+
import { getAddress } from "viem";
|
|
26
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
27
|
+
import { DEFAULT_PAY_ASSET, DEFAULT_PAY_NETWORK, } from "../core/x402.js";
|
|
28
|
+
export class PayerError extends Error {
|
|
29
|
+
code;
|
|
30
|
+
constructor(code, message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const PRIVATE_KEY = /^0x[0-9a-fA-F]{64}$/;
|
|
36
|
+
const ATOMIC = /^[0-9]{1,78}$/;
|
|
37
|
+
const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
38
|
+
/**
|
|
39
|
+
* Load an EVM private key (`0x` + 64 hex, one line) from `path`. Refused,
|
|
40
|
+
* as the log key is, when the path is a symlink or the file is accessible to
|
|
41
|
+
* group or others.
|
|
42
|
+
*/
|
|
43
|
+
export function openKeyFilePayer(path) {
|
|
44
|
+
let stat;
|
|
45
|
+
try {
|
|
46
|
+
stat = lstatSync(path);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new PayerError("payer_unavailable", `cannot open the payer key file ${path}: ${err.message}`);
|
|
50
|
+
}
|
|
51
|
+
if (stat.isSymbolicLink() || !stat.isFile())
|
|
52
|
+
throw new PayerError("payer_unavailable", `the payer key path ${path} is not a regular file (a symlink or directory is refused)`);
|
|
53
|
+
if ((stat.mode & 0o077) !== 0)
|
|
54
|
+
throw new PayerError("payer_unavailable", `the payer key file ${path} is accessible to other users (mode ${(stat.mode & 0o777).toString(8)}); chmod 600 it`);
|
|
55
|
+
const text = readFileSync(path, "utf8").trim();
|
|
56
|
+
if (!PRIVATE_KEY.test(text))
|
|
57
|
+
throw new PayerError("payer_unavailable", `the payer key file ${path} must hold one EVM private key: 0x followed by 64 hex digits`);
|
|
58
|
+
return evmPayer(text, `local payer key file ${path}`);
|
|
59
|
+
}
|
|
60
|
+
const TRANSFER_WITH_AUTHORIZATION = {
|
|
61
|
+
TransferWithAuthorization: [
|
|
62
|
+
{ name: "from", type: "address" },
|
|
63
|
+
{ name: "to", type: "address" },
|
|
64
|
+
{ name: "value", type: "uint256" },
|
|
65
|
+
{ name: "validAfter", type: "uint256" },
|
|
66
|
+
{ name: "validBefore", type: "uint256" },
|
|
67
|
+
{ name: "nonce", type: "bytes32" },
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
/** An EIP-3009 "exact" payer over a viem account. */
|
|
71
|
+
export function evmPayer(privateKey, description) {
|
|
72
|
+
const account = privateKeyToAccount(privateKey);
|
|
73
|
+
return {
|
|
74
|
+
address: account.address,
|
|
75
|
+
description,
|
|
76
|
+
pay: async ({ required, option, nonce, nowSeconds }) => {
|
|
77
|
+
const chainId = Number(option.network.split(":")[1]);
|
|
78
|
+
const name = option.extra?.["name"];
|
|
79
|
+
const version = option.extra?.["version"];
|
|
80
|
+
if (!/^eip155:[0-9]+$/.test(option.network))
|
|
81
|
+
throw new Error(`not an EVM network: ${option.network}`);
|
|
82
|
+
if (typeof name !== "string" || typeof version !== "string")
|
|
83
|
+
throw new Error(`the payment requirements lack the EIP-712 domain (extra.name, extra.version) for asset ${option.asset}`);
|
|
84
|
+
if (!/^0x[0-9a-f]{64}$/.test(nonce))
|
|
85
|
+
throw new Error(`nonce must be 0x + 64 lowercase hex`);
|
|
86
|
+
const authorization = {
|
|
87
|
+
from: account.address,
|
|
88
|
+
to: getAddress(option.payTo),
|
|
89
|
+
value: option.amount,
|
|
90
|
+
validAfter: "0",
|
|
91
|
+
validBefore: String(nowSeconds + (option.maxTimeoutSeconds ?? 300)),
|
|
92
|
+
nonce,
|
|
93
|
+
};
|
|
94
|
+
const signature = await account.signTypedData({
|
|
95
|
+
domain: {
|
|
96
|
+
name,
|
|
97
|
+
version,
|
|
98
|
+
chainId,
|
|
99
|
+
verifyingContract: getAddress(option.asset),
|
|
100
|
+
},
|
|
101
|
+
types: TRANSFER_WITH_AUTHORIZATION,
|
|
102
|
+
primaryType: "TransferWithAuthorization",
|
|
103
|
+
message: {
|
|
104
|
+
from: authorization.from,
|
|
105
|
+
to: authorization.to,
|
|
106
|
+
value: BigInt(authorization.value),
|
|
107
|
+
validAfter: 0n,
|
|
108
|
+
validBefore: BigInt(authorization.validBefore),
|
|
109
|
+
nonce: nonce,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
// The x402 v2 PaymentPayload, in the upstream client's field order.
|
|
113
|
+
const payload = {
|
|
114
|
+
x402Version: 2,
|
|
115
|
+
payload: { authorization, signature },
|
|
116
|
+
...(required.resource !== undefined
|
|
117
|
+
? { resource: required.resource }
|
|
118
|
+
: {}),
|
|
119
|
+
accepted: option,
|
|
120
|
+
};
|
|
121
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The caps, from configuration. Both amount caps are required once a payer
|
|
127
|
+
* is configured: a payer with no cap is refused, never defaulted.
|
|
128
|
+
*
|
|
129
|
+
* - `MCP_REGISTER_PAY_MAX_PER_CALL`, `MCP_REGISTER_PAY_MAX_PER_DAY`: atomic
|
|
130
|
+
* units of the asset (USDC has 6 decimals: 10000 is $0.01);
|
|
131
|
+
* - `MCP_REGISTER_PAY_NETWORK` (default Base Sepolia, `eip155:84532`) and
|
|
132
|
+
* `MCP_REGISTER_PAY_ASSET` (default Circle's USDC on Base Sepolia);
|
|
133
|
+
* - `MCP_REGISTER_PAY_TO`: optional, the only payee;
|
|
134
|
+
* - `MCP_REGISTER_PAY_ALLOW_MAINNET=1`: pay on a network that is not a
|
|
135
|
+
* testnet. Off by default.
|
|
136
|
+
*/
|
|
137
|
+
export function readPaymentCaps(env) {
|
|
138
|
+
const misconfigured = (message) => new PayerError("payer_misconfigured", message);
|
|
139
|
+
const amount = (name) => {
|
|
140
|
+
const raw = env[name]?.trim();
|
|
141
|
+
if (!raw)
|
|
142
|
+
throw misconfigured(`${name} is required when a payer is configured: the server pays nothing without a cap`);
|
|
143
|
+
if (!ATOMIC.test(raw))
|
|
144
|
+
throw misconfigured(`${name} must be a whole number of atomic units (got ${raw})`);
|
|
145
|
+
return BigInt(raw);
|
|
146
|
+
};
|
|
147
|
+
const maxPerCallAtomic = amount("MCP_REGISTER_PAY_MAX_PER_CALL");
|
|
148
|
+
const maxPerDayAtomic = amount("MCP_REGISTER_PAY_MAX_PER_DAY");
|
|
149
|
+
const network = env["MCP_REGISTER_PAY_NETWORK"]?.trim() || DEFAULT_PAY_NETWORK;
|
|
150
|
+
if (!/^eip155:[0-9]+$/.test(network))
|
|
151
|
+
throw misconfigured(`MCP_REGISTER_PAY_NETWORK must be an EVM CAIP-2 id such as eip155:84532 (got ${network})`);
|
|
152
|
+
const asset = (env["MCP_REGISTER_PAY_ASSET"]?.trim() || DEFAULT_PAY_ASSET).toLowerCase();
|
|
153
|
+
if (!ADDRESS.test(asset))
|
|
154
|
+
throw misconfigured(`MCP_REGISTER_PAY_ASSET must be a 0x-prefixed 20-byte address (got ${asset})`);
|
|
155
|
+
if (env["MCP_REGISTER_PAY_ASSET"] === undefined &&
|
|
156
|
+
network !== DEFAULT_PAY_NETWORK)
|
|
157
|
+
throw misconfigured(`MCP_REGISTER_PAY_ASSET is required with MCP_REGISTER_PAY_NETWORK=${network}: the default asset is USDC on ${DEFAULT_PAY_NETWORK} only`);
|
|
158
|
+
const payTo = env["MCP_REGISTER_PAY_TO"]?.trim();
|
|
159
|
+
if (payTo && !ADDRESS.test(payTo))
|
|
160
|
+
throw misconfigured(`MCP_REGISTER_PAY_TO must be a 0x-prefixed 20-byte address (got ${payTo})`);
|
|
161
|
+
return {
|
|
162
|
+
network,
|
|
163
|
+
asset,
|
|
164
|
+
maxPerCallAtomic,
|
|
165
|
+
maxPerDayAtomic,
|
|
166
|
+
...(payTo ? { payTo } : {}),
|
|
167
|
+
allowMainnet: env["MCP_REGISTER_PAY_ALLOW_MAINNET"] === "1",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=payer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payer.js","sourceRoot":"","sources":["../../src/node/payer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class InputError extends Error {
|
|
2
|
+
readonly code: "invalid_input";
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/** Bytes cross the MCP boundary as base64 — `b64` (the verifier's name) or
|
|
6
|
+
* its alias `base64` — and stdio additionally accepts a path. */
|
|
7
|
+
export type BytesInput = {
|
|
8
|
+
b64: string;
|
|
9
|
+
} | {
|
|
10
|
+
base64: string;
|
|
11
|
+
} | {
|
|
12
|
+
path: string;
|
|
13
|
+
};
|
|
14
|
+
/** Resolve one `{ b64 } | { base64 } | { path }` input to bytes. Throws `InputError`
|
|
15
|
+
* — never a bare `Error` — so a caller can tell "your input was malformed"
|
|
16
|
+
* from a programming error and turn it into a `structuredContent.problem`
|
|
17
|
+
* rather than an uncaught exception. */
|
|
18
|
+
export declare function resolveBytes(input: BytesInput, what?: string): Uint8Array;
|
|
19
|
+
//# sourceMappingURL=resolve-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-input.d.ts","sourceRoot":"","sources":["../../src/node/resolve-input.ts"],"names":[],"mappings":"AAoBA,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAU;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED;kEACkE;AAClE,MAAM,MAAM,UAAU,GACpB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAoC1D;;;yCAGyC;AACzC,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,SAAU,GAAG,UAAU,CAwB1E"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `node:fs` boundary. Base64 in, or a path read from disk — nothing
|
|
3
|
+
* else in `src/node` reads a file. Every core and net call in this package
|
|
4
|
+
* takes `Uint8Array`, never a path and never a base64 string; this module
|
|
5
|
+
* is where the MCP wire form of "bytes" (`{ base64 } | { path }`) becomes
|
|
6
|
+
* one, for `tools.ts` to pass on.
|
|
7
|
+
*
|
|
8
|
+
* Validates aggressively rather than trusting a malformed input to fail
|
|
9
|
+
* cleanly downstream: a bad base64 string that `Buffer.from` decodes
|
|
10
|
+
* anyway (silently dropping what it does not understand) would otherwise
|
|
11
|
+
* surface many calls later as a confusing "genesis document malformed" or
|
|
12
|
+
* "receipt malformed" — a typo dressed up as a verification failure.
|
|
13
|
+
* Mirrors `@forestrie/mcp-verify`'s `src/node/resolve-input.ts`. The wire
|
|
14
|
+
* field is `b64`, as the verifier names it; `base64` is accepted as an
|
|
15
|
+
* alias, permanently, because this package documented that name first
|
|
16
|
+
* and a caller who learned either server's shape must not be turned away
|
|
17
|
+
* by the other (plan-2609-08 U2).
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
export class InputError extends Error {
|
|
21
|
+
code = "invalid_input";
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "InputError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Strict base64. `Buffer.from(s, "base64")` silently ignores anything it
|
|
29
|
+
* does not understand, so a plainly wrong string decodes to a few
|
|
30
|
+
* plausible bytes rather than failing here, where the message can still
|
|
31
|
+
* say which field was wrong.
|
|
32
|
+
*/
|
|
33
|
+
function decodeBase64(value, what, key) {
|
|
34
|
+
const trimmed = value.trim();
|
|
35
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(trimmed) || trimmed.length === 0) {
|
|
36
|
+
throw new InputError(`${what}.${key} is not valid base64 (standard alphabet, optional '=' padding)`);
|
|
37
|
+
}
|
|
38
|
+
const bytes = new Uint8Array(Buffer.from(trimmed, "base64"));
|
|
39
|
+
if (bytes.length === 0) {
|
|
40
|
+
throw new InputError(`${what}.${key} decoded to 0 bytes`);
|
|
41
|
+
}
|
|
42
|
+
// Round-trip guard: catches truncated / mis-padded input that Buffer would
|
|
43
|
+
// otherwise accept by discarding the incomplete trailing group.
|
|
44
|
+
if (Buffer.from(bytes).toString("base64").replace(/=+$/, "") !==
|
|
45
|
+
trimmed.replace(/=+$/, "")) {
|
|
46
|
+
throw new InputError(`${what}.${key} is not canonical base64 (it does not round-trip)`);
|
|
47
|
+
}
|
|
48
|
+
return bytes;
|
|
49
|
+
}
|
|
50
|
+
/** Resolve one `{ b64 } | { base64 } | { path }` input to bytes. Throws `InputError`
|
|
51
|
+
* — never a bare `Error` — so a caller can tell "your input was malformed"
|
|
52
|
+
* from a programming error and turn it into a `structuredContent.problem`
|
|
53
|
+
* rather than an uncaught exception. */
|
|
54
|
+
export function resolveBytes(input, what = "input") {
|
|
55
|
+
if ("b64" in input) {
|
|
56
|
+
return decodeBase64(input.b64, what, "b64");
|
|
57
|
+
}
|
|
58
|
+
if ("base64" in input) {
|
|
59
|
+
return decodeBase64(input.base64, what, "base64");
|
|
60
|
+
}
|
|
61
|
+
if (input.path.trim() === "") {
|
|
62
|
+
throw new InputError(`${what}.path is empty`);
|
|
63
|
+
}
|
|
64
|
+
let bytes;
|
|
65
|
+
try {
|
|
66
|
+
bytes = new Uint8Array(readFileSync(input.path));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new InputError(`cannot read ${what}.path '${input.path}': ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
}
|
|
71
|
+
if (bytes.length === 0) {
|
|
72
|
+
throw new InputError(`${what}.path '${input.path}' is empty (0 bytes)`);
|
|
73
|
+
}
|
|
74
|
+
return bytes;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=resolve-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-input.js","sourceRoot":"","sources":["../../src/node/resolve-input.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,GAAG,eAAwB,CAAC;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAOD;;;;;GAKG;AACH,SAAS,YAAY,CACnB,KAAa,EACb,IAAY,EACZ,GAAqB;IAErB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,UAAU,CAClB,GAAG,IAAI,IAAI,GAAG,gEAAgE,CAC/E,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,IAAI,GAAG,qBAAqB,CAAC,CAAC;IAC5D,CAAC;IACD,2EAA2E;IAC3E,gEAAgE;IAChE,IACE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACxD,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAC1B,CAAC;QACD,MAAM,IAAI,UAAU,CAClB,GAAG,IAAI,IAAI,GAAG,mDAAmD,CAClE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;yCAGyC;AACzC,MAAM,UAAU,YAAY,CAAC,KAAiB,EAAE,IAAI,GAAG,OAAO;IAC5D,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACnB,OAAO,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;QACtB,OAAO,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,gBAAgB,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,KAAiB,CAAC;IACtB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAClB,eAAe,IAAI,UAAU,KAAK,CAAC,IAAI,MACrC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,UAAU,KAAK,CAAC,IAAI,sBAAsB,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createServer(deps?)` — the MCP adapter. No transport: the caller connects
|
|
3
|
+
* one (stdio from `cli.ts`, `InMemoryTransport` from the smoke test), which
|
|
4
|
+
* is also what lets an embedder mount tools on their own transport without
|
|
5
|
+
* the CLI.
|
|
6
|
+
*
|
|
7
|
+
* Tools register from `TOOL_NAMES` and prompts from `PROMPT_NAMES`
|
|
8
|
+
* (`text.ts`), in that order.
|
|
9
|
+
*
|
|
10
|
+
* The signers are seams: pass `deps.signer` (the log key) or
|
|
11
|
+
* `deps.authoritySigner` (the requester's authority key) to use a KMS or a
|
|
12
|
+
* remote signer. Without them, the defaults open local key files
|
|
13
|
+
* (`MCP_REGISTER_KEY_FILE`, else `log-key.jwk` in the home;
|
|
14
|
+
* `MCP_REGISTER_AUTHORITY_KEY_FILE`, else `authority-key.jwk`) at the first
|
|
15
|
+
* tool call that needs each, creating it then if it does not exist.
|
|
16
|
+
*
|
|
17
|
+
* The payer is a seam too: pass `deps.payer` for a hosted wallet. Without
|
|
18
|
+
* it, `MCP_REGISTER_PAYER_KEY_FILE` names a local EVM key file; with
|
|
19
|
+
* neither, register_grant runs in relay mode. The payer is never created.
|
|
20
|
+
*/
|
|
21
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
22
|
+
import type { Es256Signer } from "../core/signer.js";
|
|
23
|
+
import { type PayerBackend } from "./payer.js";
|
|
24
|
+
export declare const SERVER_NAME = "forestrie-mcp-register";
|
|
25
|
+
export type { Es256Signer } from "../core/signer.js";
|
|
26
|
+
export { openKeyFileSigner, publicKeyPem } from "./signer-file.js";
|
|
27
|
+
export { evmPayer, openKeyFilePayer, readPaymentCaps } from "./payer.js";
|
|
28
|
+
export type { PayerBackend } from "./payer.js";
|
|
29
|
+
/** Every field optional; defaults to `process.env` / `globalThis.fetch` /
|
|
30
|
+
* `() => new Date()` / the local key file, so tests can inject fakes
|
|
31
|
+
* without touching the real network, environment or home directory. */
|
|
32
|
+
export type Deps = {
|
|
33
|
+
fetchImpl?: typeof fetch;
|
|
34
|
+
env?: Record<string, string | undefined>;
|
|
35
|
+
now?: () => Date;
|
|
36
|
+
signer?: Es256Signer;
|
|
37
|
+
/** The requester's authority key; defaults to a local key file. */
|
|
38
|
+
authoritySigner?: Es256Signer;
|
|
39
|
+
/** Pays canopy's 402; defaults to MCP_REGISTER_PAYER_KEY_FILE, else none
|
|
40
|
+
* (relay mode). */
|
|
41
|
+
payer?: PayerBackend;
|
|
42
|
+
/** Where registrations are kept; defaults to MCP_REGISTER_HOME. */
|
|
43
|
+
home?: string;
|
|
44
|
+
sleep?: (ms: number) => Promise<void>;
|
|
45
|
+
};
|
|
46
|
+
export declare function createServer(deps?: Deps): McpServer;
|
|
47
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/node/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAiCjE,eAAO,MAAM,WAAW,2BAA2B,CAAC;AAEpD,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACzE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;wEAEwE;AACxE,MAAM,MAAM,IAAI,GAAG;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mEAAmE;IACnE,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B;wBACoB;IACpB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,CAAC;AAUF,wBAAgB,YAAY,CAAC,IAAI,GAAE,IAAS,GAAG,SAAS,CAwOvD"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createServer(deps?)` — the MCP adapter. No transport: the caller connects
|
|
3
|
+
* one (stdio from `cli.ts`, `InMemoryTransport` from the smoke test), which
|
|
4
|
+
* is also what lets an embedder mount tools on their own transport without
|
|
5
|
+
* the CLI.
|
|
6
|
+
*
|
|
7
|
+
* Tools register from `TOOL_NAMES` and prompts from `PROMPT_NAMES`
|
|
8
|
+
* (`text.ts`), in that order.
|
|
9
|
+
*
|
|
10
|
+
* The signers are seams: pass `deps.signer` (the log key) or
|
|
11
|
+
* `deps.authoritySigner` (the requester's authority key) to use a KMS or a
|
|
12
|
+
* remote signer. Without them, the defaults open local key files
|
|
13
|
+
* (`MCP_REGISTER_KEY_FILE`, else `log-key.jwk` in the home;
|
|
14
|
+
* `MCP_REGISTER_AUTHORITY_KEY_FILE`, else `authority-key.jwk`) at the first
|
|
15
|
+
* tool call that needs each, creating it then if it does not exist.
|
|
16
|
+
*
|
|
17
|
+
* The payer is a seam too: pass `deps.payer` for a hosted wallet. Without
|
|
18
|
+
* it, `MCP_REGISTER_PAYER_KEY_FILE` names a local EVM key file; with
|
|
19
|
+
* neither, register_grant runs in relay mode. The payer is never created.
|
|
20
|
+
*/
|
|
21
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
import { PACKAGE_VERSION } from "../core/index.js";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { GrantStore } from "./grant-store.js";
|
|
26
|
+
import { issueGrantInputShape, makeIssueGrantTool, makeRegisterGrantTool, registerGrantInputShape, } from "./grant-tools.js";
|
|
27
|
+
import { SpendLedger } from "./ledger.js";
|
|
28
|
+
import { openKeyFilePayer } from "./payer.js";
|
|
29
|
+
import { openKeyFileSigner, registerHome, } from "./signer-file.js";
|
|
30
|
+
import { RegistrationStore } from "./store.js";
|
|
31
|
+
import { INSTRUCTIONS, PROMPT_DESCRIPTIONS, PROMPT_NAMES, PROMPT_TITLES, TOOL_DESCRIPTIONS, TOOL_NAMES, TOOL_TITLES, auditPromptText, issueAndPayPromptText, registerPromptText, } from "./text.js";
|
|
32
|
+
import { checkRegistrationInputShape, delegateSealingInputShape, getLogKeyInputShape, makeCheckRegistrationTool, makeDelegateSealingTool, makeGetLogKeyTool, makeRegisterStatementTool, makeVerifyRegistrationTool, registerStatementInputShape, verifyRegistrationInputShape, } from "./tools.js";
|
|
33
|
+
export const SERVER_NAME = "forestrie-mcp-register";
|
|
34
|
+
export { openKeyFileSigner, publicKeyPem } from "./signer-file.js";
|
|
35
|
+
export { evmPayer, openKeyFilePayer, readPaymentCaps } from "./payer.js";
|
|
36
|
+
/** Writes to the operator's log and to the local store; never destroys. */
|
|
37
|
+
const WRITE = {
|
|
38
|
+
readOnlyHint: false,
|
|
39
|
+
destructiveHint: false,
|
|
40
|
+
idempotentHint: false,
|
|
41
|
+
openWorldHint: true,
|
|
42
|
+
};
|
|
43
|
+
export function createServer(deps = {}) {
|
|
44
|
+
const env = deps.env ?? process.env;
|
|
45
|
+
const home = deps.home ?? registerHome(env);
|
|
46
|
+
const keyFile = env["MCP_REGISTER_KEY_FILE"] ?? join(home, "log-key.jwk");
|
|
47
|
+
const authorityKeyFile = env["MCP_REGISTER_AUTHORITY_KEY_FILE"] ?? join(home, "authority-key.jwk");
|
|
48
|
+
const payerKeyFile = env["MCP_REGISTER_PAYER_KEY_FILE"];
|
|
49
|
+
/** A lazily opened key-file signer, unless the embedder passed one. */
|
|
50
|
+
const lazySigner = (path, role, given) => {
|
|
51
|
+
let opened;
|
|
52
|
+
const open = (create) => openKeyFileSigner(path, { create, role }).catch((err) => {
|
|
53
|
+
opened = undefined;
|
|
54
|
+
throw err;
|
|
55
|
+
});
|
|
56
|
+
return (options) => {
|
|
57
|
+
if (given !== undefined)
|
|
58
|
+
return Promise.resolve(given);
|
|
59
|
+
if (opened !== undefined)
|
|
60
|
+
return opened;
|
|
61
|
+
// A no-create open is not remembered: a later call may create.
|
|
62
|
+
if (options?.create === false)
|
|
63
|
+
return open(false);
|
|
64
|
+
return (opened = open(true));
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
let payer = deps.payer;
|
|
68
|
+
const resolved = {
|
|
69
|
+
fetchImpl: deps.fetchImpl ?? ((...a) => globalThis.fetch(...a)),
|
|
70
|
+
env,
|
|
71
|
+
now: deps.now ?? (() => new Date()),
|
|
72
|
+
signer: lazySigner(keyFile, "log", deps.signer),
|
|
73
|
+
authoritySigner: lazySigner(authorityKeyFile, "authority", deps.authoritySigner),
|
|
74
|
+
payer: () => {
|
|
75
|
+
if (payer === undefined && payerKeyFile)
|
|
76
|
+
payer = openKeyFilePayer(payerKeyFile);
|
|
77
|
+
return payer;
|
|
78
|
+
},
|
|
79
|
+
protectedPaths: [
|
|
80
|
+
keyFile,
|
|
81
|
+
authorityKeyFile,
|
|
82
|
+
...(payerKeyFile ? [payerKeyFile] : []),
|
|
83
|
+
home,
|
|
84
|
+
],
|
|
85
|
+
store: new RegistrationStore(home),
|
|
86
|
+
grants: new GrantStore(home),
|
|
87
|
+
ledger: new SpendLedger(home),
|
|
88
|
+
...(deps.sleep !== undefined ? { sleep: deps.sleep } : {}),
|
|
89
|
+
};
|
|
90
|
+
const server = new McpServer({ name: SERVER_NAME, version: PACKAGE_VERSION }, { instructions: INSTRUCTIONS });
|
|
91
|
+
server.registerTool(TOOL_NAMES[0], {
|
|
92
|
+
title: TOOL_TITLES.get_log_key,
|
|
93
|
+
description: TOOL_DESCRIPTIONS.get_log_key,
|
|
94
|
+
inputSchema: getLogKeyInputShape,
|
|
95
|
+
// May create the key file on first use; otherwise reads it.
|
|
96
|
+
annotations: {
|
|
97
|
+
readOnlyHint: false,
|
|
98
|
+
destructiveHint: false,
|
|
99
|
+
idempotentHint: true,
|
|
100
|
+
openWorldHint: false,
|
|
101
|
+
},
|
|
102
|
+
}, makeGetLogKeyTool(resolved));
|
|
103
|
+
server.registerTool(TOOL_NAMES[1], {
|
|
104
|
+
title: TOOL_TITLES.register_statement,
|
|
105
|
+
description: TOOL_DESCRIPTIONS.register_statement,
|
|
106
|
+
inputSchema: registerStatementInputShape,
|
|
107
|
+
annotations: WRITE,
|
|
108
|
+
}, makeRegisterStatementTool(resolved));
|
|
109
|
+
server.registerTool(TOOL_NAMES[2], {
|
|
110
|
+
title: TOOL_TITLES.check_registration,
|
|
111
|
+
description: TOOL_DESCRIPTIONS.check_registration,
|
|
112
|
+
inputSchema: checkRegistrationInputShape,
|
|
113
|
+
// Reads canopy; saves the receipt locally once sealed.
|
|
114
|
+
annotations: {
|
|
115
|
+
readOnlyHint: false,
|
|
116
|
+
destructiveHint: false,
|
|
117
|
+
idempotentHint: true,
|
|
118
|
+
openWorldHint: true,
|
|
119
|
+
},
|
|
120
|
+
}, makeCheckRegistrationTool(resolved));
|
|
121
|
+
server.registerTool(TOOL_NAMES[3], {
|
|
122
|
+
title: TOOL_TITLES.verify_registration,
|
|
123
|
+
description: TOOL_DESCRIPTIONS.verify_registration,
|
|
124
|
+
inputSchema: verifyRegistrationInputShape,
|
|
125
|
+
// Offline: reads the local store and the key's public half only; never
|
|
126
|
+
// creates a key.
|
|
127
|
+
annotations: {
|
|
128
|
+
readOnlyHint: true,
|
|
129
|
+
destructiveHint: false,
|
|
130
|
+
idempotentHint: true,
|
|
131
|
+
openWorldHint: false,
|
|
132
|
+
},
|
|
133
|
+
}, makeVerifyRegistrationTool(resolved));
|
|
134
|
+
server.registerTool(TOOL_NAMES[4], {
|
|
135
|
+
title: TOOL_TITLES.delegate_sealing,
|
|
136
|
+
description: TOOL_DESCRIPTIONS.delegate_sealing,
|
|
137
|
+
inputSchema: delegateSealingInputShape,
|
|
138
|
+
annotations: WRITE,
|
|
139
|
+
}, makeDelegateSealingTool(resolved));
|
|
140
|
+
server.registerTool(TOOL_NAMES[5], {
|
|
141
|
+
title: TOOL_TITLES.issue_grant,
|
|
142
|
+
description: TOOL_DESCRIPTIONS.issue_grant,
|
|
143
|
+
inputSchema: issueGrantInputShape,
|
|
144
|
+
// Signs and saves locally; sends nothing.
|
|
145
|
+
annotations: {
|
|
146
|
+
readOnlyHint: false,
|
|
147
|
+
destructiveHint: false,
|
|
148
|
+
idempotentHint: false,
|
|
149
|
+
openWorldHint: false,
|
|
150
|
+
},
|
|
151
|
+
}, makeIssueGrantTool(resolved));
|
|
152
|
+
server.registerTool(TOOL_NAMES[6], {
|
|
153
|
+
title: TOOL_TITLES.register_grant,
|
|
154
|
+
description: TOOL_DESCRIPTIONS.register_grant,
|
|
155
|
+
inputSchema: registerGrantInputShape,
|
|
156
|
+
// Writes to the operator's log, and may spend from the payer.
|
|
157
|
+
annotations: WRITE,
|
|
158
|
+
}, makeRegisterGrantTool(resolved));
|
|
159
|
+
server.registerPrompt(PROMPT_NAMES[0], {
|
|
160
|
+
title: PROMPT_TITLES.register,
|
|
161
|
+
description: PROMPT_DESCRIPTIONS.register,
|
|
162
|
+
argsSchema: {
|
|
163
|
+
what: z
|
|
164
|
+
.string()
|
|
165
|
+
.optional()
|
|
166
|
+
.describe("what to register, in a few words (optional)"),
|
|
167
|
+
},
|
|
168
|
+
}, (args) => ({
|
|
169
|
+
messages: [
|
|
170
|
+
{
|
|
171
|
+
role: "user",
|
|
172
|
+
content: { type: "text", text: registerPromptText(args) },
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
}));
|
|
176
|
+
server.registerPrompt(PROMPT_NAMES[1], {
|
|
177
|
+
title: PROMPT_TITLES.audit,
|
|
178
|
+
description: PROMPT_DESCRIPTIONS.audit,
|
|
179
|
+
argsSchema: {
|
|
180
|
+
contentHash: z
|
|
181
|
+
.string()
|
|
182
|
+
.optional()
|
|
183
|
+
.describe("the registration to audit (optional)"),
|
|
184
|
+
},
|
|
185
|
+
}, (args) => ({
|
|
186
|
+
messages: [
|
|
187
|
+
{
|
|
188
|
+
role: "user",
|
|
189
|
+
content: { type: "text", text: auditPromptText(args) },
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
}));
|
|
193
|
+
server.registerPrompt(PROMPT_NAMES[2], {
|
|
194
|
+
title: PROMPT_TITLES["issue-and-pay"],
|
|
195
|
+
description: PROMPT_DESCRIPTIONS["issue-and-pay"],
|
|
196
|
+
argsSchema: {
|
|
197
|
+
purpose: z
|
|
198
|
+
.string()
|
|
199
|
+
.optional()
|
|
200
|
+
.describe("what the grant is for, in a few words (optional)"),
|
|
201
|
+
},
|
|
202
|
+
}, (args) => ({
|
|
203
|
+
messages: [
|
|
204
|
+
{
|
|
205
|
+
role: "user",
|
|
206
|
+
content: { type: "text", text: issueAndPayPromptText(args) },
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
}));
|
|
210
|
+
return server;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/node/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Es256Signer } from "../core/signer.js";
|
|
2
|
+
export declare class KeyFileError extends Error {
|
|
3
|
+
readonly code: "signer_unavailable";
|
|
4
|
+
}
|
|
5
|
+
/** `$MCP_REGISTER_HOME`, else `$XDG_CONFIG_HOME/forestrie/mcp-register`, else `~/.config/…`. */
|
|
6
|
+
export declare function registerHome(env: Record<string, string | undefined>): string;
|
|
7
|
+
/**
|
|
8
|
+
* The two ES256 keys this server can hold. The log key owns the performer's
|
|
9
|
+
* data log; the authority key owns the requester's authority log and signs
|
|
10
|
+
* the child grants issue_grant makes. They are separate files so that
|
|
11
|
+
* whoever can write to one log cannot issue grants under the other.
|
|
12
|
+
*/
|
|
13
|
+
export type KeyRole = "log" | "authority";
|
|
14
|
+
export interface KeyFileSigner extends Es256Signer {
|
|
15
|
+
readonly path: string;
|
|
16
|
+
/** True when this process created the key file. */
|
|
17
|
+
readonly created: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load the key at `path`. Lazy by design: the server opens the signer at the
|
|
21
|
+
* first tool call that needs it, so starting the server never writes a
|
|
22
|
+
* file. With `create` (the default) a missing key is created; without it a
|
|
23
|
+
* missing key is a `KeyFileError`, so a read-only tool never makes one.
|
|
24
|
+
*
|
|
25
|
+
* Refused, like ssh refuses a loose private key: a key path that is a
|
|
26
|
+
* symlink, or a key file readable or writable by group or others.
|
|
27
|
+
*/
|
|
28
|
+
export declare function openKeyFileSigner(path: string, options?: {
|
|
29
|
+
create?: boolean;
|
|
30
|
+
role?: KeyRole;
|
|
31
|
+
}): Promise<KeyFileSigner>;
|
|
32
|
+
/** The public key as an SPKI PEM, the form forestrie-cli's `--signer-pem` reads. */
|
|
33
|
+
export declare function publicKeyPem(xy: Uint8Array): string;
|
|
34
|
+
//# sourceMappingURL=signer-file.d.ts.map
|