@smartpolicy/mcp 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/http.js ADDED
@@ -0,0 +1,201 @@
1
+ /**
2
+ * `smartpolicy-mcp serve` — Streamable HTTP transport with optional x402
3
+ * metering. Stateless: each POST /mcp gets a fresh McpServer + transport
4
+ * (Registry/GrantIssuer are shared; grant nonces are random, no state).
5
+ *
6
+ * Payments (x402, https://x402.org): enabled by setting
7
+ * SMARTPOLICY_X402_PAY_TO — USDC receiving address (enables metering)
8
+ * SMARTPOLICY_X402_PRICE_USDC — price per paid call in USDC (default 0.001)
9
+ * SMARTPOLICY_X402_FACILITATOR — default https://x402.org/facilitator
10
+ * (keyless, testnet; use CDP for mainnet)
11
+ * SMARTPOLICY_PUBLIC_URL — public base URL for the `resource` field
12
+ * SMARTPOLICY_PORT — default 3402
13
+ * Without PAY_TO every tool is free (the self-host default). Only
14
+ * grant_issue / policy_create / policy_update are ever metered; reads stay
15
+ * free per ARCHITECTURE.md §4.
16
+ *
17
+ * ponytail: payment is settled BEFORE the tool runs — a tool-level error can
18
+ * consume a payment. Acceptable at $0.001/call; refund-on-error needs an
19
+ * escrow pattern, add if anyone actually complains.
20
+ */
21
+ import { createServer } from "node:http";
22
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
23
+ import { loadConfig } from "./config.js";
24
+ import { GrantIssuer } from "./grants.js";
25
+ import { Registry } from "./registry.js";
26
+ import { buildServer } from "./server.js";
27
+ const PAID_TOOLS = new Set(["grant_issue", "policy_create", "policy_update"]);
28
+ /** x402 `exact` scheme needs the chain's USDC contract + network slug. */
29
+ const X402_NETWORKS = {
30
+ 84532: { network: "base-sepolia", usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" },
31
+ 8453: { network: "base", usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
32
+ };
33
+ function loadX402(config, port) {
34
+ const payTo = process.env.SMARTPOLICY_X402_PAY_TO;
35
+ if (!payTo)
36
+ return undefined;
37
+ const net = X402_NETWORKS[config.chainId];
38
+ if (!net) {
39
+ console.error(`WARNING: x402 metering not supported on chainId ${config.chainId}; running free.`);
40
+ return undefined;
41
+ }
42
+ const priceUsdc = Number(process.env.SMARTPOLICY_X402_PRICE_USDC ?? "0.001");
43
+ if (!Number.isFinite(priceUsdc) || priceUsdc <= 0) {
44
+ throw new Error(`invalid SMARTPOLICY_X402_PRICE_USDC: ${process.env.SMARTPOLICY_X402_PRICE_USDC}`);
45
+ }
46
+ return {
47
+ payTo,
48
+ priceAtomic: String(Math.round(priceUsdc * 1_000_000)),
49
+ facilitator: process.env.SMARTPOLICY_X402_FACILITATOR ?? "https://x402.org/facilitator",
50
+ network: net.network,
51
+ usdc: net.usdc,
52
+ publicUrl: process.env.SMARTPOLICY_PUBLIC_URL ?? `http://localhost:${port}`,
53
+ };
54
+ }
55
+ function paymentRequirements(x, toolName) {
56
+ return {
57
+ scheme: "exact",
58
+ network: x.network,
59
+ maxAmountRequired: x.priceAtomic,
60
+ resource: `${x.publicUrl}/mcp#${toolName}`,
61
+ description: `SmartPolicy MCP tool: ${toolName}`,
62
+ mimeType: "application/json",
63
+ payTo: x.payTo,
64
+ maxTimeoutSeconds: 60,
65
+ asset: x.usdc,
66
+ extra: { name: "USDC", version: "2" }, // EIP-712 domain of USDC transferWithAuthorization
67
+ };
68
+ }
69
+ async function facilitatorPost(x, path, paymentHeader, requirements) {
70
+ let paymentPayload;
71
+ try {
72
+ paymentPayload = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf8"));
73
+ }
74
+ catch {
75
+ return { ok: false, reason: "malformed X-PAYMENT header (expected base64 JSON)" };
76
+ }
77
+ const res = await fetch(`${x.facilitator}${path}`, {
78
+ method: "POST",
79
+ headers: { "content-type": "application/json" },
80
+ body: JSON.stringify({ x402Version: 1, paymentPayload, paymentRequirements: requirements }),
81
+ });
82
+ if (!res.ok)
83
+ return { ok: false, reason: `facilitator ${path} returned HTTP ${res.status}` };
84
+ return { ok: true, body: (await res.json()) };
85
+ }
86
+ function sendJson(res, status, body) {
87
+ res.writeHead(status, { "content-type": "application/json" });
88
+ res.end(JSON.stringify(body));
89
+ }
90
+ function send402(res, x, toolName, error) {
91
+ sendJson(res, 402, { x402Version: 1, error, accepts: [paymentRequirements(x, toolName)] });
92
+ }
93
+ function readBody(req) {
94
+ return new Promise((resolve, reject) => {
95
+ let data = "";
96
+ req.on("data", (chunk) => (data += chunk));
97
+ req.on("end", () => resolve(data));
98
+ req.on("error", reject);
99
+ });
100
+ }
101
+ export async function serve() {
102
+ const config = loadConfig();
103
+ const registry = new Registry(config);
104
+ const issuer = config.issuerKey ? new GrantIssuer(config) : undefined;
105
+ const port = Number(process.env.SMARTPOLICY_PORT ?? 3402);
106
+ const x402 = loadX402(config, port);
107
+ try {
108
+ await registry.assertChainId();
109
+ }
110
+ catch (error) {
111
+ console.error(`WARNING: ${error instanceof Error ? error.message : String(error)}`);
112
+ }
113
+ const httpServer = createServer(async (req, res) => {
114
+ // CORS: the API is public and stateless; browsers may call it directly.
115
+ res.setHeader("access-control-allow-origin", "*");
116
+ res.setHeader("access-control-allow-headers", "content-type, accept, x-payment, mcp-session-id, mcp-protocol-version");
117
+ res.setHeader("access-control-expose-headers", "x-payment-response");
118
+ if (req.method === "OPTIONS") {
119
+ res.writeHead(204).end();
120
+ return;
121
+ }
122
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
123
+ if (req.method === "GET" && url.pathname === "/healthz") {
124
+ sendJson(res, 200, {
125
+ ok: true,
126
+ chainId: config.chainId,
127
+ registry: config.registry,
128
+ verifier: config.verifier,
129
+ issuerEnabled: Boolean(issuer),
130
+ x402: x402 ? { network: x402.network, priceAtomicUsdc: x402.priceAtomic, paidTools: [...PAID_TOOLS] } : null,
131
+ });
132
+ return;
133
+ }
134
+ if (url.pathname !== "/mcp") {
135
+ sendJson(res, 404, { error: "not found; MCP endpoint is POST /mcp" });
136
+ return;
137
+ }
138
+ if (req.method !== "POST") {
139
+ // Stateless mode: no SSE stream, no sessions to delete.
140
+ sendJson(res, 405, { error: "stateless server: use POST /mcp" });
141
+ return;
142
+ }
143
+ try {
144
+ const raw = await readBody(req);
145
+ let body;
146
+ try {
147
+ body = raw ? JSON.parse(raw) : undefined;
148
+ }
149
+ catch {
150
+ sendJson(res, 400, { error: "invalid JSON body" });
151
+ return;
152
+ }
153
+ // x402 gate: meter tools/call on paid tools only.
154
+ const msg = body;
155
+ const toolName = msg?.method === "tools/call" ? msg.params?.name : undefined;
156
+ if (x402 && toolName && PAID_TOOLS.has(toolName)) {
157
+ const header = req.headers["x-payment"];
158
+ if (typeof header !== "string") {
159
+ send402(res, x402, toolName, "X-PAYMENT header is required for this tool");
160
+ return;
161
+ }
162
+ const requirements = paymentRequirements(x402, toolName);
163
+ const verified = await facilitatorPost(x402, "/verify", header, requirements);
164
+ if (!verified.ok || !verified.body?.isValid) {
165
+ const reason = (!verified.ok && "reason" in verified && verified.reason) ||
166
+ String(verified.body?.invalidReason ?? "payment invalid");
167
+ send402(res, x402, toolName, reason);
168
+ return;
169
+ }
170
+ const settled = await facilitatorPost(x402, "/settle", header, requirements);
171
+ if (!settled.ok || settled.body?.success === false) {
172
+ const reason = (!settled.ok && "reason" in settled && settled.reason) ||
173
+ String(settled.body?.errorReason ?? "settlement failed");
174
+ send402(res, x402, toolName, reason);
175
+ return;
176
+ }
177
+ res.setHeader("x-payment-response", Buffer.from(JSON.stringify(settled.body)).toString("base64"));
178
+ }
179
+ // Fresh server + transport per request (official stateless pattern).
180
+ const server = buildServer(config, registry, issuer);
181
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
182
+ res.on("close", () => {
183
+ void transport.close();
184
+ void server.close();
185
+ });
186
+ await server.connect(transport);
187
+ await transport.handleRequest(req, res, body);
188
+ }
189
+ catch (error) {
190
+ console.error("request failed:", error);
191
+ if (!res.headersSent) {
192
+ sendJson(res, 500, { error: "internal server error" });
193
+ }
194
+ }
195
+ });
196
+ httpServer.listen(port, () => {
197
+ console.error(`smartpolicy-mcp listening on :${port} — POST /mcp (chainId ${config.chainId}, ` +
198
+ `issuer ${issuer ? issuer.address : "disabled"}, x402 ${x402 ? `${x402.network} → ${x402.payTo}` : "off (all tools free)"})`);
199
+ });
200
+ }
201
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,UAAU,EAAe,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC;AAE9E,0EAA0E;AAC1E,MAAM,aAAa,GAAsD;IACvE,KAAK,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,4CAA4C,EAAE;IACtF,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,4CAA4C,EAAE;CAC9E,CAAC;AAWF,SAAS,QAAQ,CAAC,MAAc,EAAE,IAAY;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,mDAAmD,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;QAClG,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,OAAO,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,wCAAwC,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,OAAO;QACL,KAAK;QACL,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;QACtD,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,8BAA8B;QACvF,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,oBAAoB,IAAI,EAAE;KAC5E,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,CAAa,EAAE,QAAgB;IAC1D,OAAO;QACL,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,iBAAiB,EAAE,CAAC,CAAC,WAAW;QAChC,QAAQ,EAAE,GAAG,CAAC,CAAC,SAAS,QAAQ,QAAQ,EAAE;QAC1C,WAAW,EAAE,yBAAyB,QAAQ,EAAE;QAChD,QAAQ,EAAE,kBAAkB;QAC5B,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,iBAAiB,EAAE,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,mDAAmD;KAC3F,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,CAAa,EAAE,IAAY,EAAE,aAAqB,EAAE,YAAqB;IACtG,IAAI,cAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mDAAmD,EAAE,CAAC;IACpF,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,GAAG,IAAI,EAAE,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,CAAC;KAC5F,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,IAAI,kBAAkB,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;IAC7F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;IAClE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,OAAO,CAAC,GAAmB,EAAE,CAAa,EAAE,QAAgB,EAAE,KAAa;IAClF,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;AAC7F,CAAC;AAED,SAAS,QAAQ,CAAC,GAAoB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC;QAC3C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,aAAa,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,YAAY,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjD,wEAAwE;QACxE,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;QAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,uEAAuE,CAAC,CAAC;QACvH,GAAG,CAAC,SAAS,CAAC,+BAA+B,EAAE,oBAAoB,CAAC,CAAC;QACrE,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC,CAAC;QAEjF,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;YACxD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;gBACjB,EAAE,EAAE,IAAI;gBACR,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;gBAC9B,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;aAC7G,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YAC5B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC,CAAC;YACtE,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1B,wDAAwD;YACxD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,IAAa,CAAC;YAClB,IAAI,CAAC;gBACH,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3C,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;gBACnD,OAAO;YACT,CAAC;YAED,kDAAkD;YAClD,MAAM,GAAG,GAAG,IAAmE,CAAC;YAChF,MAAM,QAAQ,GAAG,GAAG,EAAE,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YAC7E,IAAI,IAAI,IAAI,QAAQ,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjD,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;gBACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAC/B,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,4CAA4C,CAAC,CAAC;oBAC3E,OAAO;gBACT,CAAC;gBACD,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACzD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;gBAC9E,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAE,QAAQ,CAAC,IAA0C,EAAE,OAAO,EAAE,CAAC;oBACnF,MAAM,MAAM,GACV,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC;wBACzD,MAAM,CAAE,QAAQ,CAAC,IAA4C,EAAE,aAAa,IAAI,iBAAiB,CAAC,CAAC;oBACrG,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;oBACrC,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;gBAC7E,IAAI,CAAC,OAAO,CAAC,EAAE,IAAK,OAAO,CAAC,IAA0C,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;oBAC1F,MAAM,MAAM,GACV,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,QAAQ,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;wBACtD,MAAM,CAAE,OAAO,CAAC,IAA4C,EAAE,WAAW,IAAI,mBAAmB,CAAC,CAAC;oBACpG,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;oBACrC,OAAO;gBACT,CAAC;gBACD,GAAG,CAAC,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YACpG,CAAC;YAED,qEAAqE;YACrE,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YACrD,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC,CAAC;YACvF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;gBACvB,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC3B,OAAO,CAAC,KAAK,CACX,iCAAiC,IAAI,yBAAyB,MAAM,CAAC,OAAO,IAAI;YAC9E,UAAU,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,UAAU,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,sBAAsB,GAAG,CAC/H,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SmartPolicy MCP server.
4
+ *
5
+ * smartpolicy-mcp — stdio transport (local MCP clients)
6
+ * smartpolicy-mcp serve — Streamable HTTP transport + optional x402 metering
7
+ * smartpolicy-mcp deploy — bootstrap Registry+Verifier on any chain
8
+ *
9
+ * Free reads: policy_check, policy_get, policy_fees, grant_verify, grant_issuer_info
10
+ * Issuance: grant_issue (requires SMARTPOLICY_ISSUER_KEY)
11
+ * Writes: policy_create, policy_update — return unsigned calldata for the
12
+ * caller's own wallet; this server never holds user keys.
13
+ */
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+ import { loadConfig } from "./config.js";
16
+ import { deploy } from "./deploy.js";
17
+ import { GrantIssuer } from "./grants.js";
18
+ import { serve } from "./http.js";
19
+ import { Registry } from "./registry.js";
20
+ import { buildServer } from "./server.js";
21
+ // NOTE: no process.exit() anywhere in this package — on Windows it races
22
+ // libuv's handle teardown (UV_HANDLE_CLOSING assertion, exit 0xC0000409)
23
+ // while viem's keep-alive sockets are open. Let the event loop drain instead.
24
+ async function startStdio() {
25
+ const config = loadConfig();
26
+ const registry = new Registry(config);
27
+ const issuer = config.issuerKey ? new GrantIssuer(config) : undefined;
28
+ // Best-effort chainId reconciliation at startup (M2): warn loudly if the
29
+ // configured chainId disagrees with the RPC, since grants would silently fail.
30
+ // Non-fatal — free reads still work even if the RPC is briefly unreachable.
31
+ try {
32
+ await registry.assertChainId();
33
+ }
34
+ catch (error) {
35
+ console.error(`WARNING: ${error instanceof Error ? error.message : String(error)}`);
36
+ }
37
+ const server = buildServer(config, registry, issuer);
38
+ await server.connect(new StdioServerTransport());
39
+ }
40
+ if (process.argv[2] === "deploy") {
41
+ await deploy();
42
+ }
43
+ else if (process.argv[2] === "serve") {
44
+ await serve();
45
+ }
46
+ else {
47
+ await startStdio();
48
+ }
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,yEAAyE;AACzE,yEAAyE;AACzE,8EAA8E;AAC9E,KAAK,UAAU,UAAU;IACvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEtE,yEAAyE;IACzE,+EAA+E;IAC/E,4EAA4E;IAC5E,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,aAAa,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,YAAY,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;IACjC,MAAM,MAAM,EAAE,CAAC;AACjB,CAAC;KAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;IACvC,MAAM,KAAK,EAAE,CAAC;AAChB,CAAC;KAAM,CAAC;IACN,MAAM,UAAU,EAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,104 @@
1
+ import { type Address, type Hex, type PublicClient } from "viem";
2
+ import { type ActionRuleName } from "./abi.js";
3
+ import type { Config } from "./config.js";
4
+ /** Mirrors IGrantVerifier.Grant for the grant_verify tool. */
5
+ export interface GrantTuple {
6
+ policyId: string | number | bigint;
7
+ subject: Address;
8
+ action: Hex;
9
+ issuedAt: string | number | bigint;
10
+ expiresAt: string | number | bigint;
11
+ nonce: string | number | bigint;
12
+ issuer: Address;
13
+ target: Address;
14
+ }
15
+ export interface CheckResult {
16
+ allowed: boolean;
17
+ exists: boolean;
18
+ policyId: string;
19
+ subject: Address;
20
+ action: string;
21
+ actionId: Hex;
22
+ reasons: string[];
23
+ }
24
+ export interface PolicyInfo {
25
+ policyId: string;
26
+ exists: boolean;
27
+ owner: Address;
28
+ expiresAt: string;
29
+ active: boolean;
30
+ locked: boolean;
31
+ openMembership: boolean;
32
+ transferable: boolean;
33
+ memberCount: string;
34
+ metadataURI: string;
35
+ /** keccak256 commitment to the metadata document; 0x00…00 = none. Verify fetched metadata against this. */
36
+ metadataHash: Hex;
37
+ }
38
+ /** Unsigned transaction returned to the caller to sign with their own wallet. */
39
+ export interface UnsignedTx {
40
+ to: Address;
41
+ value: string;
42
+ data: Hex;
43
+ description: string;
44
+ }
45
+ export declare class Registry {
46
+ private readonly config;
47
+ readonly client: PublicClient;
48
+ constructor(config: Config);
49
+ private read;
50
+ /** policy_check: the canonical question, with human/agent-readable reasons.
51
+ * All four reads are pinned to one block so the cosmetic reasons[] can never
52
+ * disagree with `allowed` under a sub-second race (N2). */
53
+ check(policyId: bigint, subject: Address, action: string): Promise<CheckResult>;
54
+ getPolicy(policyId: bigint): Promise<PolicyInfo>;
55
+ fees(): Promise<{
56
+ creationFee: bigint;
57
+ updateFee: bigint;
58
+ }>;
59
+ isAuthorizedIssuer(policyId: bigint, issuer: Address): Promise<boolean>;
60
+ /** M2: confirm the configured chainId matches the chain the RPC actually
61
+ * serves — a mismatch silently makes every issued grant's EIP-712 signature
62
+ * recover to the wrong address and revert on-chain. */
63
+ assertChainId(): Promise<void>;
64
+ /** grant_verify (idea #2): off-chain pre-flight of a grant before broadcast. */
65
+ verifyGrant(grant: GrantTuple, signature: Hex): Promise<{
66
+ valid: boolean;
67
+ nonceUsed: boolean;
68
+ }>;
69
+ /** policy_create: build the unsigned transaction; the caller signs it. */
70
+ buildCreatePolicy(opts: {
71
+ openMembership?: boolean;
72
+ transferable?: boolean;
73
+ expiresAt?: number;
74
+ metadataURI?: string;
75
+ metadataHash?: Hex;
76
+ }): Promise<UnsignedTx>;
77
+ /** policy_update: build the unsigned transaction for a rule change. */
78
+ buildUpdate(policyId: bigint, op: {
79
+ kind: "addMembers";
80
+ members: Address[];
81
+ } | {
82
+ kind: "removeMembers";
83
+ members: Address[];
84
+ } | {
85
+ kind: "setActionRule";
86
+ action: string;
87
+ rule: ActionRuleName;
88
+ } | {
89
+ kind: "addIssuer";
90
+ issuer: Address;
91
+ } | {
92
+ kind: "removeIssuer";
93
+ issuer: Address;
94
+ } | {
95
+ kind: "addAdmin";
96
+ admin: Address;
97
+ } | {
98
+ kind: "removeAdmin";
99
+ admin: Address;
100
+ } | {
101
+ kind: "setExpiry";
102
+ expiresAt: number;
103
+ }): Promise<UnsignedTx>;
104
+ }
@@ -0,0 +1,208 @@
1
+ import { createPublicClient, encodeFunctionData, http, } from "viem";
2
+ import { actionRuleNames, registryAbi, verifierAbi } from "./abi.js";
3
+ import { toActionId } from "./actions.js";
4
+ export class Registry {
5
+ config;
6
+ client;
7
+ constructor(config) {
8
+ this.config = config;
9
+ this.client = createPublicClient({ transport: http(config.rpcUrl) });
10
+ }
11
+ read(functionName, args = []) {
12
+ return this.client.readContract({
13
+ address: this.config.registry,
14
+ abi: registryAbi,
15
+ functionName: functionName,
16
+ args: args,
17
+ });
18
+ }
19
+ /** policy_check: the canonical question, with human/agent-readable reasons.
20
+ * All four reads are pinned to one block so the cosmetic reasons[] can never
21
+ * disagree with `allowed` under a sub-second race (N2). */
22
+ async check(policyId, subject, action) {
23
+ const actionId = toActionId(action);
24
+ // cacheTime: 0 — viem caches getBlockNumber for ~pollingInterval, which would
25
+ // pin reads to a stale block and miss a just-mined update (worse than the
26
+ // cosmetic race N2 set out to fix).
27
+ const blockNumber = await this.client.getBlockNumber({ cacheTime: 0 });
28
+ const pinned = (functionName, args) => this.client.readContract({
29
+ address: this.config.registry,
30
+ abi: registryAbi,
31
+ functionName: functionName,
32
+ args: args,
33
+ blockNumber,
34
+ });
35
+ const [allowed, active, exists, member, ruleIdx] = (await Promise.all([
36
+ pinned("isAllowed", [policyId, subject, actionId]),
37
+ pinned("isPolicyActive", [policyId]),
38
+ pinned("policyCount", []).then((count) => policyId >= 1n && policyId <= count),
39
+ pinned("isMember", [policyId, subject]),
40
+ pinned("getActionRule", [policyId, actionId]),
41
+ ]));
42
+ const rule = actionRuleNames[ruleIdx] ?? "UNSET";
43
+ const reasons = [];
44
+ if (!exists) {
45
+ reasons.push("policy does not exist");
46
+ }
47
+ else if (!active) {
48
+ reasons.push("policy is expired");
49
+ }
50
+ else {
51
+ reasons.push(`action rule is ${rule}`);
52
+ if (rule === "ANYONE")
53
+ reasons.push("rule ANYONE: every address is allowed");
54
+ else if (rule === "NOBODY")
55
+ reasons.push("rule NOBODY: the action is disabled for everyone");
56
+ else
57
+ reasons.push(member ? "subject is a member" : "subject is not a member");
58
+ }
59
+ return { allowed, exists, policyId: policyId.toString(), subject, action, actionId, reasons };
60
+ }
61
+ async getPolicy(policyId) {
62
+ const count = await this.read("policyCount");
63
+ if (policyId < 1n || policyId > count) {
64
+ return {
65
+ policyId: policyId.toString(),
66
+ exists: false,
67
+ owner: `0x${"0".repeat(40)}`,
68
+ expiresAt: "never",
69
+ active: false,
70
+ locked: false,
71
+ openMembership: false,
72
+ transferable: false,
73
+ memberCount: "0",
74
+ metadataURI: "",
75
+ metadataHash: `0x${"0".repeat(64)}`,
76
+ };
77
+ }
78
+ const [policy, active, members] = await Promise.all([
79
+ this.read("getPolicy", [policyId]),
80
+ this.read("isPolicyActive", [policyId]),
81
+ this.read("memberCount", [policyId]),
82
+ ]);
83
+ return {
84
+ policyId: policyId.toString(),
85
+ exists: true,
86
+ owner: policy.owner,
87
+ expiresAt: policy.expiresAt === 0n ? "never" : new Date(Number(policy.expiresAt) * 1000).toISOString(),
88
+ active,
89
+ locked: policy.locked,
90
+ openMembership: (policy.flags & 1) !== 0,
91
+ transferable: (policy.flags & 2) !== 0,
92
+ memberCount: members.toString(),
93
+ metadataURI: policy.metadataURI,
94
+ metadataHash: policy.metadataHash,
95
+ };
96
+ }
97
+ async fees() {
98
+ const [creationFee, updateFee] = await Promise.all([
99
+ this.read("creationFee"),
100
+ this.read("updateFee"),
101
+ ]);
102
+ return { creationFee, updateFee };
103
+ }
104
+ async isAuthorizedIssuer(policyId, issuer) {
105
+ return this.read("isAuthorizedIssuer", [policyId, issuer]);
106
+ }
107
+ /** M2: confirm the configured chainId matches the chain the RPC actually
108
+ * serves — a mismatch silently makes every issued grant's EIP-712 signature
109
+ * recover to the wrong address and revert on-chain. */
110
+ async assertChainId() {
111
+ const live = await this.client.getChainId();
112
+ if (live !== this.config.chainId) {
113
+ throw new Error(`chainId mismatch: configured ${this.config.chainId} but RPC ${this.config.rpcUrl} serves ${live}. ` +
114
+ "Fix SMARTPOLICY_CHAIN_ID or SMARTPOLICY_RPC_URL — grants would otherwise be silently unverifiable.");
115
+ }
116
+ }
117
+ /** grant_verify (idea #2): off-chain pre-flight of a grant before broadcast. */
118
+ async verifyGrant(grant, signature) {
119
+ const tuple = {
120
+ policyId: BigInt(grant.policyId),
121
+ subject: grant.subject,
122
+ action: grant.action,
123
+ issuedAt: BigInt(grant.issuedAt),
124
+ expiresAt: BigInt(grant.expiresAt),
125
+ nonce: BigInt(grant.nonce),
126
+ issuer: grant.issuer,
127
+ target: grant.target,
128
+ };
129
+ const [valid, nonceUsed] = await Promise.all([
130
+ this.client.readContract({
131
+ address: this.config.verifier,
132
+ abi: verifierAbi,
133
+ functionName: "isGrantValid",
134
+ args: [tuple, signature],
135
+ }),
136
+ this.client.readContract({
137
+ address: this.config.verifier,
138
+ abi: verifierAbi,
139
+ functionName: "isNonceUsed",
140
+ args: [grant.issuer, BigInt(grant.nonce)],
141
+ }),
142
+ ]);
143
+ return { valid, nonceUsed };
144
+ }
145
+ /** policy_create: build the unsigned transaction; the caller signs it. */
146
+ async buildCreatePolicy(opts) {
147
+ const { creationFee } = await this.fees();
148
+ const flags = (opts.openMembership ? 1 : 0) | (opts.transferable ? 2 : 0);
149
+ const zeroHash = `0x${"0".repeat(64)}`;
150
+ return {
151
+ to: this.config.registry,
152
+ value: creationFee.toString(),
153
+ data: encodeFunctionData({
154
+ abi: registryAbi,
155
+ functionName: "createPolicy",
156
+ args: [flags, BigInt(opts.expiresAt ?? 0), opts.metadataURI ?? "", opts.metadataHash ?? zeroHash],
157
+ }),
158
+ description: "createPolicy — submit from the wallet that should OWN the policy. " +
159
+ "The new policyId is emitted in the PolicyCreated event (also: policyCount() after inclusion).",
160
+ };
161
+ }
162
+ /** policy_update: build the unsigned transaction for a rule change. */
163
+ async buildUpdate(policyId, op) {
164
+ const { updateFee } = await this.fees();
165
+ let data;
166
+ switch (op.kind) {
167
+ case "addMembers":
168
+ data = encodeFunctionData({ abi: registryAbi, functionName: "addMembers", args: [policyId, op.members] });
169
+ break;
170
+ case "removeMembers":
171
+ data = encodeFunctionData({ abi: registryAbi, functionName: "removeMembers", args: [policyId, op.members] });
172
+ break;
173
+ case "setActionRule":
174
+ data = encodeFunctionData({
175
+ abi: registryAbi,
176
+ functionName: "setActionRule",
177
+ args: [policyId, toActionId(op.action), actionRuleNames.indexOf(op.rule)],
178
+ });
179
+ break;
180
+ case "addIssuer":
181
+ data = encodeFunctionData({ abi: registryAbi, functionName: "addIssuer", args: [policyId, op.issuer] });
182
+ break;
183
+ case "removeIssuer":
184
+ data = encodeFunctionData({ abi: registryAbi, functionName: "removeIssuer", args: [policyId, op.issuer] });
185
+ break;
186
+ case "addAdmin":
187
+ data = encodeFunctionData({ abi: registryAbi, functionName: "addAdmin", args: [policyId, op.admin] });
188
+ break;
189
+ case "removeAdmin":
190
+ data = encodeFunctionData({ abi: registryAbi, functionName: "removeAdmin", args: [policyId, op.admin] });
191
+ break;
192
+ case "setExpiry":
193
+ data = encodeFunctionData({
194
+ abi: registryAbi,
195
+ functionName: "setExpiry",
196
+ args: [policyId, BigInt(op.expiresAt)],
197
+ });
198
+ break;
199
+ }
200
+ return {
201
+ to: this.config.registry,
202
+ value: updateFee.toString(),
203
+ data,
204
+ description: `${op.kind} on policy ${policyId} — submit from the policy owner or an admin wallet.`,
205
+ };
206
+ }
207
+ }
208
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * SmartPolicy MCP server construction — all tool registrations live here.
3
+ * Transports (stdio in index.ts, Streamable HTTP in http.ts) call buildServer
4
+ * per connection/request; Registry and GrantIssuer are shared singletons
5
+ * (grant nonces are random and stateless, so sharing is safe).
6
+ */
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import type { Config } from "./config.js";
9
+ import type { GrantIssuer } from "./grants.js";
10
+ import type { Registry } from "./registry.js";
11
+ export declare function buildServer(config: Config, registry: Registry, issuer: GrantIssuer | undefined): McpServer;