@veyanet/mcp 1.0.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/index.js ADDED
@@ -0,0 +1,444 @@
1
+ // src/config.ts
2
+ var MCP_SERVICE_NAME = "@veyanet/mcp";
3
+ var MCP_SERVICE_VERSION = "1.0.0";
4
+ function loadConfig() {
5
+ const mcpApiKey = process.env.MCP_API_KEY?.trim() || null;
6
+ const relayer = process.env.VEYA_RELAYER_PRIVATE_KEY?.trim() || process.env.VEYA_DEPLOYER_PRIVATE_KEY?.trim() || null;
7
+ return {
8
+ port: Number(process.env.PORT || 8788),
9
+ host: process.env.HOST || "0.0.0.0",
10
+ publicMcpUrl: (process.env.PUBLIC_MCP_URL || "https://mcp.veyanet.tech/mcp").replace(/\/$/, ""),
11
+ chainId: Number(process.env.ROBINHOOD_CHAIN_ID || 46630),
12
+ rpcUrl: process.env.ROBINHOOD_RPC_URL || "https://rpc.testnet.chain.robinhood.com",
13
+ explorerUrl: process.env.ROBINHOOD_EXPLORER_URL || "https://explorer.testnet.chain.robinhood.com",
14
+ contractAddress: process.env.VEYA_CONTRACT_ADDRESS || "0x1a1Dc3c55550FCE9F70ef6cDEeF967c0b72a5d84",
15
+ apiUrl: (process.env.VEYA_API_URL || "https://api.veyanet.tech").replace(/\/$/, ""),
16
+ mcpApiKey,
17
+ relayerPrivateKey: relayer,
18
+ corsOrigins: (process.env.CORS_ORIGIN || "").split(",").map((s) => s.trim()).filter(Boolean),
19
+ nodeEnv: process.env.NODE_ENV || "development"
20
+ };
21
+ }
22
+ function writesEnabled(cfg) {
23
+ return Boolean(cfg.mcpApiKey && cfg.relayerPrivateKey);
24
+ }
25
+
26
+ // src/server.ts
27
+ import { AsyncLocalStorage } from "async_hooks";
28
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
29
+
30
+ // src/tools/public.ts
31
+ import { z } from "zod";
32
+
33
+ // src/sdk.ts
34
+ import { VeyaClient } from "@veyanet/sdk";
35
+ function createReadClient(cfg) {
36
+ return new VeyaClient({
37
+ rpcUrl: cfg.rpcUrl,
38
+ contractAddress: cfg.contractAddress,
39
+ chainId: cfg.chainId,
40
+ explorerUrl: cfg.explorerUrl
41
+ });
42
+ }
43
+ function createWriteClient(cfg) {
44
+ if (!cfg.relayerPrivateKey) {
45
+ throw new Error("VEYA_RELAYER_PRIVATE_KEY required for write tools");
46
+ }
47
+ return new VeyaClient({
48
+ rpcUrl: cfg.rpcUrl,
49
+ contractAddress: cfg.contractAddress,
50
+ chainId: cfg.chainId,
51
+ explorerUrl: cfg.explorerUrl,
52
+ payerPrivateKey: cfg.relayerPrivateKey
53
+ });
54
+ }
55
+ function parseHexBytes(value, expectedLen) {
56
+ const hex = value.trim().replace(/^0x/, "");
57
+ if (!/^[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) {
58
+ throw new Error("Expected even-length hex string");
59
+ }
60
+ const buf = Buffer.from(hex, "hex");
61
+ if (expectedLen !== void 0 && buf.length !== expectedLen) {
62
+ throw new Error(`Expected ${expectedLen} bytes, got ${buf.length}`);
63
+ }
64
+ return Uint8Array.from(buf);
65
+ }
66
+
67
+ // src/tools/public.ts
68
+ function registerPublicTools(server, cfg) {
69
+ server.tool(
70
+ "veya_describe",
71
+ "Honesty card: what this MCP is (AES sealed elsewhere, testnet 46630, not FHE/mainnet)",
72
+ {},
73
+ async () => ({
74
+ content: [
75
+ {
76
+ type: "text",
77
+ text: JSON.stringify(
78
+ {
79
+ name: MCP_SERVICE_NAME,
80
+ version: MCP_SERVICE_VERSION,
81
+ publicUrl: cfg.publicMcpUrl,
82
+ settlement: {
83
+ network: "Robinhood Chain testnet",
84
+ chainId: cfg.chainId,
85
+ contract: cfg.contractAddress,
86
+ explorer: cfg.explorerUrl,
87
+ rpc: cfg.rpcUrl
88
+ },
89
+ productApi: cfg.apiUrl,
90
+ sealed: "AES-256-GCM sealed-node (not FHE; TFHE is Phase 4)",
91
+ mainnet: "Not a VEYA settlement claim (Phase 3)",
92
+ writes: "Require Authorization: Bearer <MCP_API_KEY> when enabled on the server",
93
+ stdioSibling: "Local stdio MCP remains at veya-anchor/packages/mcp (operators)"
94
+ },
95
+ null,
96
+ 2
97
+ )
98
+ }
99
+ ]
100
+ })
101
+ );
102
+ server.tool(
103
+ "veya_ping_chain",
104
+ "Ping Robinhood Chain RPC \u2014 chain id, block, contract",
105
+ {},
106
+ async () => {
107
+ const client = createReadClient(cfg);
108
+ const ping = await client.pingChain();
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: JSON.stringify(
114
+ {
115
+ ...ping,
116
+ chainId: ping.chainId.toString(),
117
+ expectedChainId: cfg.chainId,
118
+ config: client.describe()
119
+ },
120
+ null,
121
+ 2
122
+ )
123
+ }
124
+ ]
125
+ };
126
+ }
127
+ );
128
+ server.tool(
129
+ "veya_hash_blake3",
130
+ "Compute BLAKE3 hex digest (commitment helper)",
131
+ { data: z.string().min(1) },
132
+ async ({ data }) => {
133
+ const client = createReadClient(cfg);
134
+ const hash = await client.hashBlake3(data);
135
+ return {
136
+ content: [{ type: "text", text: JSON.stringify({ hash }, null, 2) }]
137
+ };
138
+ }
139
+ );
140
+ server.tool(
141
+ "veya_verify_transaction",
142
+ "Verify a Veya.sol commitment from a Robinhood Chain tx hash",
143
+ { txHash: z.string().min(66) },
144
+ async ({ txHash }) => {
145
+ const client = createReadClient(cfg);
146
+ const result = await client.verifyTransaction(txHash);
147
+ return {
148
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
149
+ };
150
+ }
151
+ );
152
+ server.tool(
153
+ "veya_api_health",
154
+ "Probe the public VEYA API /health (api.veyanet.tech) \u2014 honest degraded when fleet down",
155
+ {},
156
+ async () => {
157
+ const res = await fetch(`${cfg.apiUrl}/health`, {
158
+ signal: AbortSignal.timeout(8e3)
159
+ });
160
+ const body = await res.json().catch(() => ({}));
161
+ return {
162
+ content: [
163
+ {
164
+ type: "text",
165
+ text: JSON.stringify({ httpStatus: res.status, body }, null, 2)
166
+ }
167
+ ]
168
+ };
169
+ }
170
+ );
171
+ }
172
+
173
+ // src/tools/write.ts
174
+ import { z as z2 } from "zod";
175
+
176
+ // src/auth.ts
177
+ function extractBearer(req) {
178
+ const h = req.header("authorization") || req.header("Authorization");
179
+ if (!h) return null;
180
+ const m = /^Bearer\s+(.+)$/i.exec(h.trim());
181
+ return m?.[1]?.trim() || null;
182
+ }
183
+ function assertWriteAuthorized(cfg, bearer) {
184
+ if (!cfg.mcpApiKey) {
185
+ throw new Error(
186
+ "Write tools disabled on this server (set MCP_API_KEY + VEYA_RELAYER_PRIVATE_KEY)"
187
+ );
188
+ }
189
+ if (!bearer || bearer !== cfg.mcpApiKey) {
190
+ throw new Error("Unauthorized: provide Authorization: Bearer <MCP_API_KEY>");
191
+ }
192
+ if (!cfg.relayerPrivateKey) {
193
+ throw new Error("Write tools disabled: VEYA_RELAYER_PRIVATE_KEY not configured");
194
+ }
195
+ }
196
+
197
+ // src/tools/write.ts
198
+ function registerWriteTools(server, cfg, getBearer) {
199
+ if (!writesEnabled(cfg)) {
200
+ server.tool(
201
+ "veya_writes_status",
202
+ "Reports that on-chain write tools are disabled on this server",
203
+ {},
204
+ async () => ({
205
+ content: [
206
+ {
207
+ type: "text",
208
+ text: JSON.stringify({
209
+ writesEnabled: false,
210
+ reason: "Set MCP_API_KEY and VEYA_RELAYER_PRIVATE_KEY to enable write tools"
211
+ })
212
+ }
213
+ ]
214
+ })
215
+ );
216
+ return;
217
+ }
218
+ function requireAuth() {
219
+ assertWriteAuthorized(cfg, getBearer());
220
+ }
221
+ server.tool(
222
+ "veya_store_commitment",
223
+ "Write a 32-byte commitment to Veya.sol (requires MCP_API_KEY). Use content Use stamps prefer product-site + API.",
224
+ {
225
+ environmentUuidHex: z2.string().min(32),
226
+ commitmentHex: z2.string().min(64)
227
+ },
228
+ async ({ environmentUuidHex, commitmentHex }) => {
229
+ requireAuth();
230
+ const client = createWriteClient(cfg);
231
+ const txHash = await client.requireEvm().storeCommitment(
232
+ parseHexBytes(environmentUuidHex, 16),
233
+ parseHexBytes(commitmentHex, 32)
234
+ );
235
+ return {
236
+ content: [
237
+ {
238
+ type: "text",
239
+ text: JSON.stringify({ txHash, explorer: client.explorerFor(txHash) }, null, 2)
240
+ }
241
+ ]
242
+ };
243
+ }
244
+ );
245
+ server.tool(
246
+ "veya_attest_execution",
247
+ "attestExecution on Veya.sol with ML-DSA bytes (requires MCP_API_KEY)",
248
+ {
249
+ environmentUuidHex: z2.string().min(32),
250
+ blake3HashHex: z2.string().min(64),
251
+ mldsaSigHex: z2.string().min(8)
252
+ },
253
+ async ({ environmentUuidHex, blake3HashHex, mldsaSigHex }) => {
254
+ requireAuth();
255
+ const client = createWriteClient(cfg);
256
+ const txHash = await client.requireEvm().attestExecution(
257
+ parseHexBytes(environmentUuidHex, 16),
258
+ parseHexBytes(blake3HashHex, 32),
259
+ parseHexBytes(mldsaSigHex)
260
+ );
261
+ return {
262
+ content: [
263
+ {
264
+ type: "text",
265
+ text: JSON.stringify({ txHash, explorer: client.explorerFor(txHash) }, null, 2)
266
+ }
267
+ ]
268
+ };
269
+ }
270
+ );
271
+ server.tool(
272
+ "veya_register_environment",
273
+ "registerEnvironment on Veya.sol (requires MCP_API_KEY)",
274
+ {
275
+ environmentUuidHex: z2.string().min(32),
276
+ pqPubkeyHashHex: z2.string().min(64),
277
+ envType: z2.number().int().min(0).max(10).default(0)
278
+ },
279
+ async ({ environmentUuidHex, pqPubkeyHashHex, envType }) => {
280
+ requireAuth();
281
+ const client = createWriteClient(cfg);
282
+ const txHash = await client.requireEvm().registerEnvironment(
283
+ parseHexBytes(environmentUuidHex, 16),
284
+ parseHexBytes(pqPubkeyHashHex, 32),
285
+ envType
286
+ );
287
+ return {
288
+ content: [
289
+ {
290
+ type: "text",
291
+ text: JSON.stringify({ txHash, explorer: client.explorerFor(txHash) }, null, 2)
292
+ }
293
+ ]
294
+ };
295
+ }
296
+ );
297
+ }
298
+
299
+ // src/server.ts
300
+ var requestAuth = new AsyncLocalStorage();
301
+ function createMcpServer(cfg) {
302
+ const server = new McpServer({
303
+ name: MCP_SERVICE_NAME,
304
+ version: MCP_SERVICE_VERSION
305
+ });
306
+ registerPublicTools(server, cfg);
307
+ registerWriteTools(server, cfg, () => requestAuth.getStore()?.bearer ?? null);
308
+ return server;
309
+ }
310
+
311
+ // src/http.ts
312
+ import express from "express";
313
+ import cors from "cors";
314
+ import { randomUUID } from "crypto";
315
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
316
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
317
+ function createHttpApp(cfg = loadConfig()) {
318
+ const app = express();
319
+ app.use(express.json({ limit: "1mb" }));
320
+ app.use(
321
+ cors({
322
+ origin(origin, cb) {
323
+ if (!origin) return cb(null, true);
324
+ if (cfg.corsOrigins.length === 0) return cb(null, false);
325
+ if (cfg.corsOrigins.includes(origin)) return cb(null, true);
326
+ return cb(null, false);
327
+ },
328
+ credentials: true
329
+ })
330
+ );
331
+ app.get("/health", (_req, res) => {
332
+ res.json({
333
+ status: "ok",
334
+ service: MCP_SERVICE_NAME,
335
+ version: MCP_SERVICE_VERSION,
336
+ publicMcpUrl: cfg.publicMcpUrl,
337
+ chainId: cfg.chainId,
338
+ contractAddress: cfg.contractAddress,
339
+ writesEnabled: writesEnabled(cfg),
340
+ sealed: "AES-256-GCM (not FHE)",
341
+ settlement: "Robinhood Chain testnet 46630"
342
+ });
343
+ });
344
+ app.post("/mcp", async (req, res) => {
345
+ const bearer = extractBearer(req);
346
+ await requestAuth.run({ bearer }, async () => {
347
+ try {
348
+ const server = createMcpServer(cfg);
349
+ const transport = new StreamableHTTPServerTransport({
350
+ sessionIdGenerator: void 0
351
+ });
352
+ await server.connect(transport);
353
+ await transport.handleRequest(req, res, req.body);
354
+ res.on("close", () => {
355
+ void transport.close();
356
+ void server.close();
357
+ });
358
+ } catch (err) {
359
+ console.error("[veyanet/mcp] POST /mcp", err);
360
+ if (!res.headersSent) {
361
+ res.status(500).json({
362
+ jsonrpc: "2.0",
363
+ error: { code: -32603, message: "Internal server error" },
364
+ id: null
365
+ });
366
+ }
367
+ }
368
+ });
369
+ });
370
+ const sessions = /* @__PURE__ */ new Map();
371
+ app.post("/mcp/session", async (req, res) => {
372
+ const bearer = extractBearer(req);
373
+ await requestAuth.run({ bearer }, async () => {
374
+ try {
375
+ const sessionId = req.header("mcp-session-id") || void 0;
376
+ if (sessionId && sessions.has(sessionId)) {
377
+ await sessions.get(sessionId).handleRequest(req, res, req.body);
378
+ return;
379
+ }
380
+ if (!sessionId && isInitializeRequest(req.body)) {
381
+ const transport = new StreamableHTTPServerTransport({
382
+ sessionIdGenerator: () => randomUUID(),
383
+ onsessioninitialized: (id) => {
384
+ sessions.set(id, transport);
385
+ }
386
+ });
387
+ transport.onclose = () => {
388
+ if (transport.sessionId) sessions.delete(transport.sessionId);
389
+ };
390
+ const server = createMcpServer(cfg);
391
+ await server.connect(transport);
392
+ await transport.handleRequest(req, res, req.body);
393
+ return;
394
+ }
395
+ res.status(400).json({
396
+ jsonrpc: "2.0",
397
+ error: { code: -32e3, message: "No valid MCP session" },
398
+ id: null
399
+ });
400
+ } catch (err) {
401
+ console.error("[veyanet/mcp] POST /mcp/session", err);
402
+ if (!res.headersSent) {
403
+ res.status(500).json({
404
+ jsonrpc: "2.0",
405
+ error: { code: -32603, message: "Internal server error" },
406
+ id: null
407
+ });
408
+ }
409
+ }
410
+ });
411
+ });
412
+ app.get("/", (_req, res) => {
413
+ res.type("html").send(`<!doctype html>
414
+ <html><head><meta charset="utf-8"/><title>VEYA MCP</title></head>
415
+ <body style="font-family:system-ui;max-width:40rem;margin:2rem auto;line-height:1.5">
416
+ <h1>VEYA MCP</h1>
417
+ <p>Paste this URL into Claude / Cursor (Streamable HTTP):</p>
418
+ <pre>${cfg.publicMcpUrl}</pre>
419
+ <p>Local: <code>POST http://127.0.0.1:${cfg.port}/mcp</code></p>
420
+ <p>Health: <a href="/health">/health</a></p>
421
+ <p>Settlement: Robinhood testnet <strong>46630</strong> \xB7 Sealed = AES-256-GCM (not FHE) \xB7 Not mainnet.</p>
422
+ <p>Local stdio MCP for operators: <code>veya-anchor/packages/mcp</code></p>
423
+ </body></html>`);
424
+ });
425
+ return app;
426
+ }
427
+ function startHttpServer(cfg = loadConfig()) {
428
+ const app = createHttpApp(cfg);
429
+ return app.listen(cfg.port, cfg.host, () => {
430
+ console.log(`[veyanet/mcp] listening on http://${cfg.host}:${cfg.port}`);
431
+ console.log(`[veyanet/mcp] MCP POST ${cfg.publicMcpUrl} (local /mcp)`);
432
+ console.log(`[veyanet/mcp] writesEnabled=${writesEnabled(cfg)}`);
433
+ });
434
+ }
435
+ export {
436
+ MCP_SERVICE_NAME,
437
+ MCP_SERVICE_VERSION,
438
+ createHttpApp,
439
+ createMcpServer,
440
+ loadConfig,
441
+ startHttpServer,
442
+ writesEnabled
443
+ };
444
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/server.ts","../src/tools/public.ts","../src/sdk.ts","../src/tools/write.ts","../src/auth.ts","../src/http.ts"],"sourcesContent":["export type McpServiceConfig = {\r\n port: number;\r\n host: string;\r\n publicMcpUrl: string;\r\n chainId: number;\r\n rpcUrl: string;\r\n explorerUrl: string;\r\n contractAddress: string;\r\n apiUrl: string;\r\n mcpApiKey: string | null;\r\n relayerPrivateKey: string | null;\r\n corsOrigins: string[];\r\n nodeEnv: string;\r\n};\r\n\r\nexport const MCP_SERVICE_NAME = \"@veyanet/mcp\";\r\nexport const MCP_SERVICE_VERSION = \"1.0.0\";\r\n\r\nexport function loadConfig(): McpServiceConfig {\r\n const mcpApiKey = process.env.MCP_API_KEY?.trim() || null;\r\n const relayer =\r\n process.env.VEYA_RELAYER_PRIVATE_KEY?.trim() ||\r\n process.env.VEYA_DEPLOYER_PRIVATE_KEY?.trim() ||\r\n null;\r\n\r\n return {\r\n port: Number(process.env.PORT || 8788),\r\n host: process.env.HOST || \"0.0.0.0\",\r\n publicMcpUrl: (process.env.PUBLIC_MCP_URL || \"https://mcp.veyanet.tech/mcp\").replace(/\\/$/, \"\"),\r\n chainId: Number(process.env.ROBINHOOD_CHAIN_ID || 46630),\r\n rpcUrl: process.env.ROBINHOOD_RPC_URL || \"https://rpc.testnet.chain.robinhood.com\",\r\n explorerUrl:\r\n process.env.ROBINHOOD_EXPLORER_URL || \"https://explorer.testnet.chain.robinhood.com\",\r\n contractAddress:\r\n process.env.VEYA_CONTRACT_ADDRESS || \"0x1a1Dc3c55550FCE9F70ef6cDEeF967c0b72a5d84\",\r\n apiUrl: (process.env.VEYA_API_URL || \"https://api.veyanet.tech\").replace(/\\/$/, \"\"),\r\n mcpApiKey,\r\n relayerPrivateKey: relayer,\r\n corsOrigins: (process.env.CORS_ORIGIN || \"\")\r\n .split(\",\")\r\n .map((s) => s.trim())\r\n .filter(Boolean),\r\n nodeEnv: process.env.NODE_ENV || \"development\",\r\n };\r\n}\r\n\r\nexport function writesEnabled(cfg: McpServiceConfig): boolean {\r\n return Boolean(cfg.mcpApiKey && cfg.relayerPrivateKey);\r\n}\r\n","import { AsyncLocalStorage } from \"node:async_hooks\";\r\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport {\r\n MCP_SERVICE_NAME,\r\n MCP_SERVICE_VERSION,\r\n type McpServiceConfig,\r\n} from \"./config.js\";\r\nimport { registerPublicTools } from \"./tools/public.js\";\r\nimport { registerWriteTools } from \"./tools/write.js\";\r\n\r\nexport const requestAuth = new AsyncLocalStorage<{ bearer: string | null }>();\r\n\r\nexport function createMcpServer(cfg: McpServiceConfig): McpServer {\r\n const server = new McpServer({\r\n name: MCP_SERVICE_NAME,\r\n version: MCP_SERVICE_VERSION,\r\n });\r\n\r\n registerPublicTools(server, cfg);\r\n registerWriteTools(server, cfg, () => requestAuth.getStore()?.bearer ?? null);\r\n\r\n return server;\r\n}\r\n","import { z } from \"zod\";\r\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { MCP_SERVICE_NAME, MCP_SERVICE_VERSION, type McpServiceConfig } from \"../config.js\";\r\nimport { createReadClient } from \"../sdk.js\";\r\n\r\nexport function registerPublicTools(server: McpServer, cfg: McpServiceConfig): void {\r\n server.tool(\r\n \"veya_describe\",\r\n \"Honesty card: what this MCP is (AES sealed elsewhere, testnet 46630, not FHE/mainnet)\",\r\n {},\r\n async () => ({\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify(\r\n {\r\n name: MCP_SERVICE_NAME,\r\n version: MCP_SERVICE_VERSION,\r\n publicUrl: cfg.publicMcpUrl,\r\n settlement: {\r\n network: \"Robinhood Chain testnet\",\r\n chainId: cfg.chainId,\r\n contract: cfg.contractAddress,\r\n explorer: cfg.explorerUrl,\r\n rpc: cfg.rpcUrl,\r\n },\r\n productApi: cfg.apiUrl,\r\n sealed: \"AES-256-GCM sealed-node (not FHE; TFHE is Phase 4)\",\r\n mainnet: \"Not a VEYA settlement claim (Phase 3)\",\r\n writes: \"Require Authorization: Bearer <MCP_API_KEY> when enabled on the server\",\r\n stdioSibling: \"Local stdio MCP remains at veya-anchor/packages/mcp (operators)\",\r\n },\r\n null,\r\n 2,\r\n ),\r\n },\r\n ],\r\n }),\r\n );\r\n\r\n server.tool(\r\n \"veya_ping_chain\",\r\n \"Ping Robinhood Chain RPC — chain id, block, contract\",\r\n {},\r\n async () => {\r\n const client = createReadClient(cfg);\r\n const ping = await client.pingChain();\r\n return {\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify(\r\n {\r\n ...ping,\r\n chainId: ping.chainId.toString(),\r\n expectedChainId: cfg.chainId,\r\n config: client.describe(),\r\n },\r\n null,\r\n 2,\r\n ),\r\n },\r\n ],\r\n };\r\n },\r\n );\r\n\r\n server.tool(\r\n \"veya_hash_blake3\",\r\n \"Compute BLAKE3 hex digest (commitment helper)\",\r\n { data: z.string().min(1) },\r\n async ({ data }) => {\r\n const client = createReadClient(cfg);\r\n const hash = await client.hashBlake3(data);\r\n return {\r\n content: [{ type: \"text\", text: JSON.stringify({ hash }, null, 2) }],\r\n };\r\n },\r\n );\r\n\r\n server.tool(\r\n \"veya_verify_transaction\",\r\n \"Verify a Veya.sol commitment from a Robinhood Chain tx hash\",\r\n { txHash: z.string().min(66) },\r\n async ({ txHash }) => {\r\n const client = createReadClient(cfg);\r\n const result = await client.verifyTransaction(txHash);\r\n return {\r\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\r\n };\r\n },\r\n );\r\n\r\n server.tool(\r\n \"veya_api_health\",\r\n \"Probe the public VEYA API /health (api.veyanet.tech) — honest degraded when fleet down\",\r\n {},\r\n async () => {\r\n const res = await fetch(`${cfg.apiUrl}/health`, {\r\n signal: AbortSignal.timeout(8000),\r\n });\r\n const body = await res.json().catch(() => ({}));\r\n return {\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify({ httpStatus: res.status, body }, null, 2),\r\n },\r\n ],\r\n };\r\n },\r\n );\r\n}\r\n","import { VeyaClient } from \"@veyanet/sdk\";\r\nimport type { McpServiceConfig } from \"./config.js\";\r\n\r\nexport function createReadClient(cfg: McpServiceConfig): VeyaClient {\r\n return new VeyaClient({\r\n rpcUrl: cfg.rpcUrl,\r\n contractAddress: cfg.contractAddress,\r\n chainId: cfg.chainId,\r\n explorerUrl: cfg.explorerUrl,\r\n });\r\n}\r\n\r\nexport function createWriteClient(cfg: McpServiceConfig): VeyaClient {\r\n if (!cfg.relayerPrivateKey) {\r\n throw new Error(\"VEYA_RELAYER_PRIVATE_KEY required for write tools\");\r\n }\r\n return new VeyaClient({\r\n rpcUrl: cfg.rpcUrl,\r\n contractAddress: cfg.contractAddress,\r\n chainId: cfg.chainId,\r\n explorerUrl: cfg.explorerUrl,\r\n payerPrivateKey: cfg.relayerPrivateKey,\r\n });\r\n}\r\n\r\nexport function parseHexBytes(value: string, expectedLen?: number): Uint8Array {\r\n const hex = value.trim().replace(/^0x/, \"\");\r\n if (!/^[0-9a-fA-F]*$/.test(hex) || hex.length % 2 !== 0) {\r\n throw new Error(\"Expected even-length hex string\");\r\n }\r\n const buf = Buffer.from(hex, \"hex\");\r\n if (expectedLen !== undefined && buf.length !== expectedLen) {\r\n throw new Error(`Expected ${expectedLen} bytes, got ${buf.length}`);\r\n }\r\n return Uint8Array.from(buf);\r\n}\r\n","import { z } from \"zod\";\r\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport type { McpServiceConfig } from \"../config.js\";\r\nimport { writesEnabled } from \"../config.js\";\r\nimport { assertWriteAuthorized } from \"../auth.js\";\r\nimport { createWriteClient, parseHexBytes } from \"../sdk.js\";\r\n\r\n/**\r\n * Write tools are registered only when MCP_API_KEY + relayer key exist.\r\n * Callers must still pass Authorization: Bearer on the HTTP request;\r\n * bearer is read from request AsyncLocalStorage.\r\n */\r\nexport function registerWriteTools(\r\n server: McpServer,\r\n cfg: McpServiceConfig,\r\n getBearer: () => string | null,\r\n): void {\r\n if (!writesEnabled(cfg)) {\r\n server.tool(\r\n \"veya_writes_status\",\r\n \"Reports that on-chain write tools are disabled on this server\",\r\n {},\r\n async () => ({\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify({\r\n writesEnabled: false,\r\n reason: \"Set MCP_API_KEY and VEYA_RELAYER_PRIVATE_KEY to enable write tools\",\r\n }),\r\n },\r\n ],\r\n }),\r\n );\r\n return;\r\n }\r\n\r\n function requireAuth(): void {\r\n assertWriteAuthorized(cfg, getBearer());\r\n }\r\n\r\n server.tool(\r\n \"veya_store_commitment\",\r\n \"Write a 32-byte commitment to Veya.sol (requires MCP_API_KEY). Use content Use stamps prefer product-site + API.\",\r\n {\r\n environmentUuidHex: z.string().min(32),\r\n commitmentHex: z.string().min(64),\r\n },\r\n async ({ environmentUuidHex, commitmentHex }) => {\r\n requireAuth();\r\n const client = createWriteClient(cfg);\r\n const txHash = await client.requireEvm().storeCommitment(\r\n parseHexBytes(environmentUuidHex, 16),\r\n parseHexBytes(commitmentHex, 32),\r\n );\r\n return {\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify({ txHash, explorer: client.explorerFor(txHash) }, null, 2),\r\n },\r\n ],\r\n };\r\n },\r\n );\r\n\r\n server.tool(\r\n \"veya_attest_execution\",\r\n \"attestExecution on Veya.sol with ML-DSA bytes (requires MCP_API_KEY)\",\r\n {\r\n environmentUuidHex: z.string().min(32),\r\n blake3HashHex: z.string().min(64),\r\n mldsaSigHex: z.string().min(8),\r\n },\r\n async ({ environmentUuidHex, blake3HashHex, mldsaSigHex }) => {\r\n requireAuth();\r\n const client = createWriteClient(cfg);\r\n const txHash = await client.requireEvm().attestExecution(\r\n parseHexBytes(environmentUuidHex, 16),\r\n parseHexBytes(blake3HashHex, 32),\r\n parseHexBytes(mldsaSigHex),\r\n );\r\n return {\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify({ txHash, explorer: client.explorerFor(txHash) }, null, 2),\r\n },\r\n ],\r\n };\r\n },\r\n );\r\n\r\n server.tool(\r\n \"veya_register_environment\",\r\n \"registerEnvironment on Veya.sol (requires MCP_API_KEY)\",\r\n {\r\n environmentUuidHex: z.string().min(32),\r\n pqPubkeyHashHex: z.string().min(64),\r\n envType: z.number().int().min(0).max(10).default(0),\r\n },\r\n async ({ environmentUuidHex, pqPubkeyHashHex, envType }) => {\r\n requireAuth();\r\n const client = createWriteClient(cfg);\r\n const txHash = await client.requireEvm().registerEnvironment(\r\n parseHexBytes(environmentUuidHex, 16),\r\n parseHexBytes(pqPubkeyHashHex, 32),\r\n envType,\r\n );\r\n return {\r\n content: [\r\n {\r\n type: \"text\",\r\n text: JSON.stringify({ txHash, explorer: client.explorerFor(txHash) }, null, 2),\r\n },\r\n ],\r\n };\r\n },\r\n );\r\n}\r\n","import type { Request } from \"express\";\r\nimport type { McpServiceConfig } from \"./config.js\";\r\n\r\nexport function extractBearer(req: Request): string | null {\r\n const h = req.header(\"authorization\") || req.header(\"Authorization\");\r\n if (!h) return null;\r\n const m = /^Bearer\\s+(.+)$/i.exec(h.trim());\r\n return m?.[1]?.trim() || null;\r\n}\r\n\r\n/** Write tools require MCP_API_KEY match. Fail closed if key unset. */\r\nexport function assertWriteAuthorized(cfg: McpServiceConfig, bearer: string | null): void {\r\n if (!cfg.mcpApiKey) {\r\n throw new Error(\r\n \"Write tools disabled on this server (set MCP_API_KEY + VEYA_RELAYER_PRIVATE_KEY)\",\r\n );\r\n }\r\n if (!bearer || bearer !== cfg.mcpApiKey) {\r\n throw new Error(\"Unauthorized: provide Authorization: Bearer <MCP_API_KEY>\");\r\n }\r\n if (!cfg.relayerPrivateKey) {\r\n throw new Error(\"Write tools disabled: VEYA_RELAYER_PRIVATE_KEY not configured\");\r\n }\r\n}\r\n","import express, { type Request, type Response } from \"express\";\r\nimport cors from \"cors\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\r\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\r\nimport {\r\n loadConfig,\r\n writesEnabled,\r\n MCP_SERVICE_NAME,\r\n MCP_SERVICE_VERSION,\r\n type McpServiceConfig,\r\n} from \"./config.js\";\r\nimport { extractBearer } from \"./auth.js\";\r\nimport { createMcpServer, requestAuth } from \"./server.js\";\r\n\r\nexport function createHttpApp(cfg: McpServiceConfig = loadConfig()) {\r\n const app = express();\r\n app.use(express.json({ limit: \"1mb\" }));\r\n\r\n app.use(\r\n cors({\r\n origin(origin, cb) {\r\n if (!origin) return cb(null, true);\r\n if (cfg.corsOrigins.length === 0) return cb(null, false);\r\n if (cfg.corsOrigins.includes(origin)) return cb(null, true);\r\n return cb(null, false);\r\n },\r\n credentials: true,\r\n }),\r\n );\r\n\r\n app.get(\"/health\", (_req, res) => {\r\n res.json({\r\n status: \"ok\",\r\n service: MCP_SERVICE_NAME,\r\n version: MCP_SERVICE_VERSION,\r\n publicMcpUrl: cfg.publicMcpUrl,\r\n chainId: cfg.chainId,\r\n contractAddress: cfg.contractAddress,\r\n writesEnabled: writesEnabled(cfg),\r\n sealed: \"AES-256-GCM (not FHE)\",\r\n settlement: \"Robinhood Chain testnet 46630\",\r\n });\r\n });\r\n\r\n // Stateless Streamable HTTP — paste URL into Claude / Cursor (POST /mcp)\r\n app.post(\"/mcp\", async (req: Request, res: Response) => {\r\n const bearer = extractBearer(req);\r\n await requestAuth.run({ bearer }, async () => {\r\n try {\r\n const server = createMcpServer(cfg);\r\n const transport = new StreamableHTTPServerTransport({\r\n sessionIdGenerator: undefined,\r\n });\r\n await server.connect(transport);\r\n await transport.handleRequest(req, res, req.body);\r\n res.on(\"close\", () => {\r\n void transport.close();\r\n void server.close();\r\n });\r\n } catch (err) {\r\n console.error(\"[veyanet/mcp] POST /mcp\", err);\r\n if (!res.headersSent) {\r\n res.status(500).json({\r\n jsonrpc: \"2.0\",\r\n error: { code: -32603, message: \"Internal server error\" },\r\n id: null,\r\n });\r\n }\r\n }\r\n });\r\n });\r\n\r\n // Optional stateful path for clients that send MCP-Session-Id\r\n const sessions = new Map<string, StreamableHTTPServerTransport>();\r\n\r\n app.post(\"/mcp/session\", async (req: Request, res: Response) => {\r\n const bearer = extractBearer(req);\r\n await requestAuth.run({ bearer }, async () => {\r\n try {\r\n const sessionId = req.header(\"mcp-session-id\") || undefined;\r\n if (sessionId && sessions.has(sessionId)) {\r\n await sessions.get(sessionId)!.handleRequest(req, res, req.body);\r\n return;\r\n }\r\n if (!sessionId && isInitializeRequest(req.body)) {\r\n const transport = new StreamableHTTPServerTransport({\r\n sessionIdGenerator: () => randomUUID(),\r\n onsessioninitialized: (id) => {\r\n sessions.set(id, transport);\r\n },\r\n });\r\n transport.onclose = () => {\r\n if (transport.sessionId) sessions.delete(transport.sessionId);\r\n };\r\n const server = createMcpServer(cfg);\r\n await server.connect(transport);\r\n await transport.handleRequest(req, res, req.body);\r\n return;\r\n }\r\n res.status(400).json({\r\n jsonrpc: \"2.0\",\r\n error: { code: -32000, message: \"No valid MCP session\" },\r\n id: null,\r\n });\r\n } catch (err) {\r\n console.error(\"[veyanet/mcp] POST /mcp/session\", err);\r\n if (!res.headersSent) {\r\n res.status(500).json({\r\n jsonrpc: \"2.0\",\r\n error: { code: -32603, message: \"Internal server error\" },\r\n id: null,\r\n });\r\n }\r\n }\r\n });\r\n });\r\n\r\n app.get(\"/\", (_req, res) => {\r\n res.type(\"html\").send(`<!doctype html>\r\n<html><head><meta charset=\"utf-8\"/><title>VEYA MCP</title></head>\r\n<body style=\"font-family:system-ui;max-width:40rem;margin:2rem auto;line-height:1.5\">\r\n <h1>VEYA MCP</h1>\r\n <p>Paste this URL into Claude / Cursor (Streamable HTTP):</p>\r\n <pre>${cfg.publicMcpUrl}</pre>\r\n <p>Local: <code>POST http://127.0.0.1:${cfg.port}/mcp</code></p>\r\n <p>Health: <a href=\"/health\">/health</a></p>\r\n <p>Settlement: Robinhood testnet <strong>46630</strong> · Sealed = AES-256-GCM (not FHE) · Not mainnet.</p>\r\n <p>Local stdio MCP for operators: <code>veya-anchor/packages/mcp</code></p>\r\n</body></html>`);\r\n });\r\n\r\n return app;\r\n}\r\n\r\nexport function startHttpServer(cfg: McpServiceConfig = loadConfig()) {\r\n const app = createHttpApp(cfg);\r\n return app.listen(cfg.port, cfg.host, () => {\r\n console.log(`[veyanet/mcp] listening on http://${cfg.host}:${cfg.port}`);\r\n console.log(`[veyanet/mcp] MCP POST ${cfg.publicMcpUrl} (local /mcp)`);\r\n console.log(`[veyanet/mcp] writesEnabled=${writesEnabled(cfg)}`);\r\n });\r\n}\r\n"],"mappings":";AAeO,IAAM,mBAAmB;AACzB,IAAM,sBAAsB;AAE5B,SAAS,aAA+B;AAC7C,QAAM,YAAY,QAAQ,IAAI,aAAa,KAAK,KAAK;AACrD,QAAM,UACJ,QAAQ,IAAI,0BAA0B,KAAK,KAC3C,QAAQ,IAAI,2BAA2B,KAAK,KAC5C;AAEF,SAAO;AAAA,IACL,MAAM,OAAO,QAAQ,IAAI,QAAQ,IAAI;AAAA,IACrC,MAAM,QAAQ,IAAI,QAAQ;AAAA,IAC1B,eAAe,QAAQ,IAAI,kBAAkB,gCAAgC,QAAQ,OAAO,EAAE;AAAA,IAC9F,SAAS,OAAO,QAAQ,IAAI,sBAAsB,KAAK;AAAA,IACvD,QAAQ,QAAQ,IAAI,qBAAqB;AAAA,IACzC,aACE,QAAQ,IAAI,0BAA0B;AAAA,IACxC,iBACE,QAAQ,IAAI,yBAAyB;AAAA,IACvC,SAAS,QAAQ,IAAI,gBAAgB,4BAA4B,QAAQ,OAAO,EAAE;AAAA,IAClF;AAAA,IACA,mBAAmB;AAAA,IACnB,cAAc,QAAQ,IAAI,eAAe,IACtC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAAA,IACjB,SAAS,QAAQ,IAAI,YAAY;AAAA,EACnC;AACF;AAEO,SAAS,cAAc,KAAgC;AAC5D,SAAO,QAAQ,IAAI,aAAa,IAAI,iBAAiB;AACvD;;;AChDA,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;;;ACD1B,SAAS,SAAS;;;ACAlB,SAAS,kBAAkB;AAGpB,SAAS,iBAAiB,KAAmC;AAClE,SAAO,IAAI,WAAW;AAAA,IACpB,QAAQ,IAAI;AAAA,IACZ,iBAAiB,IAAI;AAAA,IACrB,SAAS,IAAI;AAAA,IACb,aAAa,IAAI;AAAA,EACnB,CAAC;AACH;AAEO,SAAS,kBAAkB,KAAmC;AACnE,MAAI,CAAC,IAAI,mBAAmB;AAC1B,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,SAAO,IAAI,WAAW;AAAA,IACpB,QAAQ,IAAI;AAAA,IACZ,iBAAiB,IAAI;AAAA,IACrB,SAAS,IAAI;AAAA,IACb,aAAa,IAAI;AAAA,IACjB,iBAAiB,IAAI;AAAA,EACvB,CAAC;AACH;AAEO,SAAS,cAAc,OAAe,aAAkC;AAC7E,QAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,OAAO,EAAE;AAC1C,MAAI,CAAC,iBAAiB,KAAK,GAAG,KAAK,IAAI,SAAS,MAAM,GAAG;AACvD,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,QAAM,MAAM,OAAO,KAAK,KAAK,KAAK;AAClC,MAAI,gBAAgB,UAAa,IAAI,WAAW,aAAa;AAC3D,UAAM,IAAI,MAAM,YAAY,WAAW,eAAe,IAAI,MAAM,EAAE;AAAA,EACpE;AACA,SAAO,WAAW,KAAK,GAAG;AAC5B;;;AD9BO,SAAS,oBAAoB,QAAmB,KAA6B;AAClF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,aAAa;AAAA,MACX,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,KAAK;AAAA,YACT;AAAA,cACE,MAAM;AAAA,cACN,SAAS;AAAA,cACT,WAAW,IAAI;AAAA,cACf,YAAY;AAAA,gBACV,SAAS;AAAA,gBACT,SAAS,IAAI;AAAA,gBACb,UAAU,IAAI;AAAA,gBACd,UAAU,IAAI;AAAA,gBACd,KAAK,IAAI;AAAA,cACX;AAAA,cACA,YAAY,IAAI;AAAA,cAChB,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,cAAc;AAAA,YAChB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY;AACV,YAAM,SAAS,iBAAiB,GAAG;AACnC,YAAM,OAAO,MAAM,OAAO,UAAU;AACpC,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK;AAAA,cACT;AAAA,gBACE,GAAG;AAAA,gBACH,SAAS,KAAK,QAAQ,SAAS;AAAA,gBAC/B,iBAAiB,IAAI;AAAA,gBACrB,QAAQ,OAAO,SAAS;AAAA,cAC1B;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;AAAA,IAC1B,OAAO,EAAE,KAAK,MAAM;AAClB,YAAM,SAAS,iBAAiB,GAAG;AACnC,YAAM,OAAO,MAAM,OAAO,WAAW,IAAI;AACzC,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,EAAE,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;AAAA,IAC7B,OAAO,EAAE,OAAO,MAAM;AACpB,YAAM,SAAS,iBAAiB,GAAG;AACnC,YAAM,SAAS,MAAM,OAAO,kBAAkB,MAAM;AACpD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAC;AAAA,IACD,YAAY;AACV,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,MAAM,WAAW;AAAA,QAC9C,QAAQ,YAAY,QAAQ,GAAI;AAAA,MAClC,CAAC;AACD,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,EAAE,YAAY,IAAI,QAAQ,KAAK,GAAG,MAAM,CAAC;AAAA,UAChE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AEhHA,SAAS,KAAAA,UAAS;;;ACGX,SAAS,cAAc,KAA6B;AACzD,QAAM,IAAI,IAAI,OAAO,eAAe,KAAK,IAAI,OAAO,eAAe;AACnE,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,mBAAmB,KAAK,EAAE,KAAK,CAAC;AAC1C,SAAO,IAAI,CAAC,GAAG,KAAK,KAAK;AAC3B;AAGO,SAAS,sBAAsB,KAAuB,QAA6B;AACxF,MAAI,CAAC,IAAI,WAAW;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,UAAU,WAAW,IAAI,WAAW;AACvC,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,MAAI,CAAC,IAAI,mBAAmB;AAC1B,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACF;;;ADXO,SAAS,mBACd,QACA,KACA,WACM;AACN,MAAI,CAAC,cAAc,GAAG,GAAG;AACvB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,CAAC;AAAA,MACD,aAAa;AAAA,QACX,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU;AAAA,cACnB,eAAe;AAAA,cACf,QAAQ;AAAA,YACV,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAEA,WAAS,cAAoB;AAC3B,0BAAsB,KAAK,UAAU,CAAC;AAAA,EACxC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,oBAAoBC,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,MACrC,eAAeA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,IAClC;AAAA,IACA,OAAO,EAAE,oBAAoB,cAAc,MAAM;AAC/C,kBAAY;AACZ,YAAM,SAAS,kBAAkB,GAAG;AACpC,YAAM,SAAS,MAAM,OAAO,WAAW,EAAE;AAAA,QACvC,cAAc,oBAAoB,EAAE;AAAA,QACpC,cAAc,eAAe,EAAE;AAAA,MACjC;AACA,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,EAAE,QAAQ,UAAU,OAAO,YAAY,MAAM,EAAE,GAAG,MAAM,CAAC;AAAA,UAChF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,oBAAoBA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,MACrC,eAAeA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,MAChC,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC/B;AAAA,IACA,OAAO,EAAE,oBAAoB,eAAe,YAAY,MAAM;AAC5D,kBAAY;AACZ,YAAM,SAAS,kBAAkB,GAAG;AACpC,YAAM,SAAS,MAAM,OAAO,WAAW,EAAE;AAAA,QACvC,cAAc,oBAAoB,EAAE;AAAA,QACpC,cAAc,eAAe,EAAE;AAAA,QAC/B,cAAc,WAAW;AAAA,MAC3B;AACA,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,EAAE,QAAQ,UAAU,OAAO,YAAY,MAAM,EAAE,GAAG,MAAM,CAAC;AAAA,UAChF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,oBAAoBA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,MACrC,iBAAiBA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,MAClC,SAASA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,CAAC;AAAA,IACpD;AAAA,IACA,OAAO,EAAE,oBAAoB,iBAAiB,QAAQ,MAAM;AAC1D,kBAAY;AACZ,YAAM,SAAS,kBAAkB,GAAG;AACpC,YAAM,SAAS,MAAM,OAAO,WAAW,EAAE;AAAA,QACvC,cAAc,oBAAoB,EAAE;AAAA,QACpC,cAAc,iBAAiB,EAAE;AAAA,QACjC;AAAA,MACF;AACA,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,EAAE,QAAQ,UAAU,OAAO,YAAY,MAAM,EAAE,GAAG,MAAM,CAAC;AAAA,UAChF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AH7GO,IAAM,cAAc,IAAI,kBAA6C;AAErE,SAAS,gBAAgB,KAAkC;AAChE,QAAM,SAAS,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAED,sBAAoB,QAAQ,GAAG;AAC/B,qBAAmB,QAAQ,KAAK,MAAM,YAAY,SAAS,GAAG,UAAU,IAAI;AAE5E,SAAO;AACT;;;AKtBA,OAAO,aAA8C;AACrD,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAC3B,SAAS,qCAAqC;AAC9C,SAAS,2BAA2B;AAW7B,SAAS,cAAc,MAAwB,WAAW,GAAG;AAClE,QAAM,MAAM,QAAQ;AACpB,MAAI,IAAI,QAAQ,KAAK,EAAE,OAAO,MAAM,CAAC,CAAC;AAEtC,MAAI;AAAA,IACF,KAAK;AAAA,MACH,OAAO,QAAQ,IAAI;AACjB,YAAI,CAAC,OAAQ,QAAO,GAAG,MAAM,IAAI;AACjC,YAAI,IAAI,YAAY,WAAW,EAAG,QAAO,GAAG,MAAM,KAAK;AACvD,YAAI,IAAI,YAAY,SAAS,MAAM,EAAG,QAAO,GAAG,MAAM,IAAI;AAC1D,eAAO,GAAG,MAAM,KAAK;AAAA,MACvB;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAEA,MAAI,IAAI,WAAW,CAAC,MAAM,QAAQ;AAChC,QAAI,KAAK;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,SAAS;AAAA,MACT,cAAc,IAAI;AAAA,MAClB,SAAS,IAAI;AAAA,MACb,iBAAiB,IAAI;AAAA,MACrB,eAAe,cAAc,GAAG;AAAA,MAChC,QAAQ;AAAA,MACR,YAAY;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AAGD,MAAI,KAAK,QAAQ,OAAO,KAAc,QAAkB;AACtD,UAAM,SAAS,cAAc,GAAG;AAChC,UAAM,YAAY,IAAI,EAAE,OAAO,GAAG,YAAY;AAC5C,UAAI;AACF,cAAM,SAAS,gBAAgB,GAAG;AAClC,cAAM,YAAY,IAAI,8BAA8B;AAAA,UAClD,oBAAoB;AAAA,QACtB,CAAC;AACD,cAAM,OAAO,QAAQ,SAAS;AAC9B,cAAM,UAAU,cAAc,KAAK,KAAK,IAAI,IAAI;AAChD,YAAI,GAAG,SAAS,MAAM;AACpB,eAAK,UAAU,MAAM;AACrB,eAAK,OAAO,MAAM;AAAA,QACpB,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,gBAAQ,MAAM,2BAA2B,GAAG;AAC5C,YAAI,CAAC,IAAI,aAAa;AACpB,cAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YACnB,SAAS;AAAA,YACT,OAAO,EAAE,MAAM,QAAQ,SAAS,wBAAwB;AAAA,YACxD,IAAI;AAAA,UACN,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,WAAW,oBAAI,IAA2C;AAEhE,MAAI,KAAK,gBAAgB,OAAO,KAAc,QAAkB;AAC9D,UAAM,SAAS,cAAc,GAAG;AAChC,UAAM,YAAY,IAAI,EAAE,OAAO,GAAG,YAAY;AAC5C,UAAI;AACF,cAAM,YAAY,IAAI,OAAO,gBAAgB,KAAK;AAClD,YAAI,aAAa,SAAS,IAAI,SAAS,GAAG;AACxC,gBAAM,SAAS,IAAI,SAAS,EAAG,cAAc,KAAK,KAAK,IAAI,IAAI;AAC/D;AAAA,QACF;AACA,YAAI,CAAC,aAAa,oBAAoB,IAAI,IAAI,GAAG;AAC/C,gBAAM,YAAY,IAAI,8BAA8B;AAAA,YAClD,oBAAoB,MAAM,WAAW;AAAA,YACrC,sBAAsB,CAAC,OAAO;AAC5B,uBAAS,IAAI,IAAI,SAAS;AAAA,YAC5B;AAAA,UACF,CAAC;AACD,oBAAU,UAAU,MAAM;AACxB,gBAAI,UAAU,UAAW,UAAS,OAAO,UAAU,SAAS;AAAA,UAC9D;AACA,gBAAM,SAAS,gBAAgB,GAAG;AAClC,gBAAM,OAAO,QAAQ,SAAS;AAC9B,gBAAM,UAAU,cAAc,KAAK,KAAK,IAAI,IAAI;AAChD;AAAA,QACF;AACA,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,SAAS;AAAA,UACT,OAAO,EAAE,MAAM,OAAQ,SAAS,uBAAuB;AAAA,UACvD,IAAI;AAAA,QACN,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,gBAAQ,MAAM,mCAAmC,GAAG;AACpD,YAAI,CAAC,IAAI,aAAa;AACpB,cAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YACnB,SAAS;AAAA,YACT,OAAO,EAAE,MAAM,QAAQ,SAAS,wBAAwB;AAAA,YACxD,IAAI;AAAA,UACN,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,MAAI,IAAI,KAAK,CAAC,MAAM,QAAQ;AAC1B,QAAI,KAAK,MAAM,EAAE,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,SAKjB,IAAI,YAAY;AAAA,0CACiB,IAAI,IAAI;AAAA;AAAA;AAAA;AAAA,eAInC;AAAA,EACb,CAAC;AAED,SAAO;AACT;AAEO,SAAS,gBAAgB,MAAwB,WAAW,GAAG;AACpE,QAAM,MAAM,cAAc,GAAG;AAC7B,SAAO,IAAI,OAAO,IAAI,MAAM,IAAI,MAAM,MAAM;AAC1C,YAAQ,IAAI,qCAAqC,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AACvE,YAAQ,IAAI,0BAA0B,IAAI,YAAY,eAAe;AACrE,YAAQ,IAAI,+BAA+B,cAAc,GAAG,CAAC,EAAE;AAAA,EACjE,CAAC;AACH;","names":["z","z"]}
@@ -0,0 +1,77 @@
1
+ # System Architecture — VEYA MCP
2
+
3
+ ## Purpose
4
+
5
+ `@veyanet/mcp` is a **thin Streamable HTTP gate** in front of `@veyanet/sdk`. It exists so agent clients can paste `https://mcp.veyanet.tech/mcp` and call selected VEYA capabilities without embedding the SDK. It is not a second cryptography stack and not a token service.
6
+
7
+ ## Trust boundaries
8
+
9
+ ```text
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ MCP Client (Claude / Cursor / custom agent) │
12
+ │ Trusts: TLS to mcp.veyanet.tech, tool JSON responses │
13
+ │ Does not receive: relayer private keys, MCP_API_KEY dump │
14
+ └────────────────────────────┬────────────────────────────────┘
15
+ │ HTTPS Streamable HTTP
16
+
17
+ ┌─────────────────────────────────────────────────────────────┐
18
+ │ @veyanet/mcp process │
19
+ │ Owns: POST /mcp, /health, tool registry, Bearer check │
20
+ │ Does not own: PQ primitives, consensus quorum, sealed AES │
21
+ └────────────────────────────┬────────────────────────────────┘
22
+ │ in-process import
23
+
24
+ ┌─────────────────────────────────────────────────────────────┐
25
+ │ @veyanet/sdk │
26
+ │ Owns: BLAKE3, verify receipts, EvmAnchor, chain id guard │
27
+ └────────────────────────────┬────────────────────────────────┘
28
+ │ JSON-RPC
29
+
30
+ ┌─────────────────────────────────────────────────────────────┐
31
+ │ Robinhood Chain testnet 46630 · Veya.sol │
32
+ │ Owns: commitments, environments, attestations (protocol) │
33
+ └─────────────────────────────────────────────────────────────┘
34
+
35
+ Optional side call:
36
+ veya_api_health → https://api.veyanet.tech/health
37
+ (product API fleet honesty; independent of MCP process uptime)
38
+ ```
39
+
40
+ ## Components inside this package
41
+
42
+ | Module | Role |
43
+ |--------|------|
44
+ | `src/http.ts` | Express app: `/`, `/health`, `POST /mcp`, optional session path |
45
+ | `src/server.ts` | `McpServer` construction + tool registration |
46
+ | `src/config.ts` | Env load, defaults, `writesEnabled` |
47
+ | `src/auth.ts` | Bearer extract + write assert |
48
+ | `src/sdk.ts` | Read/write `VeyaClient` factories |
49
+ | `src/tools/public.ts` | Public tools |
50
+ | `src/tools/write.ts` | Authenticated write tools |
51
+ | `scripts/smoke.ts` | Live initialize + tool smoke |
52
+
53
+ ## What this architecture refuses
54
+
55
+ * Inventing 2-of-3 quorum inside MCP without validators
56
+ * Claiming FHE / SGX / Nitro as the sealed product path
57
+ * Claiming mainnet settlement before Phase 3
58
+ * Exposing write tools without both API key and relayer key
59
+ * Treating `Veya.sol` as ERC-20
60
+
61
+ ## Relationship to stdio MCP
62
+
63
+ `veya-anchor/packages/mcp/` is a **local stdio** operator surface. `@veyanet/mcp` is the **HTTP** public surface. Both should stay honest about the same chain pins; they are not nested packages.
64
+
65
+ ## Failure domains
66
+
67
+ | Domain | Symptom | Operator action |
68
+ |--------|---------|-----------------|
69
+ | MCP process down | Connector timeout | Restart Node / systemd |
70
+ | RPC down | `veya_ping_chain` / verify fail | Check Robinhood RPC |
71
+ | Product API degraded | `veya_api_health` body degraded | Fleet / SHIP.md — MCP can still serve describe/ping if RPC works |
72
+ | Wrong Bearer | Write tool error | Rotate / fix `MCP_API_KEY` |
73
+ | Chain mismatch | SDK `CHAIN_MISMATCH` on writes | Fix RPC / chain id env |
74
+
75
+ ## Security notes
76
+
77
+ See [AUTHENTICATION.md](./AUTHENTICATION.md) and [../SECURITY.md](../SECURITY.md). Relayer keys are high value: use a dedicated testnet wallet, minimal ETH, and rotate if leaked.
@@ -0,0 +1,76 @@
1
+ # Authentication & Writes — VEYA MCP
2
+
3
+ ## Model summary
4
+
5
+ | Surface | Auth |
6
+ |---------|------|
7
+ | Public tools | None |
8
+ | `/health`, `/` | None |
9
+ | Write tools | `Authorization: Bearer <MCP_API_KEY>` **and** server-side relayer key |
10
+
11
+ There is no end-user wallet login inside this MCP process. Wallet / guest sessions belong to the product API and product-site.
12
+
13
+ ## Enabling writes
14
+
15
+ Both must be set in the server environment:
16
+
17
+ ```bash
18
+ MCP_API_KEY=... # shared secret for callers
19
+ VEYA_RELAYER_PRIVATE_KEY=0x... # pays gas on Robinhood testnet
20
+ ```
21
+
22
+ `VEYA_DEPLOYER_PRIVATE_KEY` is accepted as an alternate payer env name.
23
+
24
+ If either is missing, `writesEnabled(cfg)` is false:
25
+ * Write tools are **not** registered
26
+ * `veya_writes_status` is registered instead
27
+
28
+ ## Bearer validation
29
+
30
+ `src/auth.ts`:
31
+
32
+ 1. Extract `Authorization: Bearer …`
33
+ 2. Require `cfg.mcpApiKey` to be set
34
+ 3. Require bearer equality (constant-time string compare is not currently used; treat key as high entropy and rotate on suspicion)
35
+ 4. Require relayer private key present
36
+
37
+ Failure throws; the tool call fails closed.
38
+
39
+ ## Client example (conceptual)
40
+
41
+ MCP clients differ in how they attach HTTP headers. Operators testing with raw HTTP must send:
42
+
43
+ ```http
44
+ POST /mcp HTTP/1.1
45
+ Host: mcp.veyanet.tech
46
+ Authorization: Bearer <MCP_API_KEY>
47
+ Content-Type: application/json
48
+ Accept: application/json, text/event-stream
49
+ ```
50
+
51
+ If your Claude/Cursor build cannot attach custom headers, keep writes **disabled** on the public URL and run a separate private MCP instance for authorized writers.
52
+
53
+ ## Key custody
54
+
55
+ | Secret | Where it lives | Never |
56
+ |--------|----------------|-------|
57
+ | `MCP_API_KEY` | Server `.env`, optionally CI secret store | README, git, screenshots |
58
+ | Relayer private key | Server `.env` only | Logs, tool responses, `/health` |
59
+ | User wallet keys | Not used by this MCP | — |
60
+
61
+ ## Recommended production posture
62
+
63
+ 1. Public `mcp.veyanet.tech` → **reads only** (`writesEnabled: false`)
64
+ 2. Private operator instance (VPN / IP allowlist) → writes enabled with dedicated testnet wallet
65
+ 3. Rotate `MCP_API_KEY` if any authorized client is compromised
66
+ 4. Fund relayer with minimal testnet ETH; monitor explorer for unexpected txs
67
+
68
+ ## Abuse considerations
69
+
70
+ Write tools can spend testnet gas and mutate protocol state (commitments, environments, attestations). Treat Bearer distribution like production API key distribution. Rate limiting at nginx is recommended before enabling public writes.
71
+
72
+ ## Related
73
+
74
+ * [TOOLS.md](./TOOLS.md) — write tool argument lists
75
+ * [CONFIGURATION.md](./CONFIGURATION.md) — env names
76
+ * [../SECURITY.md](../SECURITY.md) — disclosure