@shroud-fi/mcp-server 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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/cjs/auth.d.ts +41 -0
- package/dist/cjs/auth.d.ts.map +1 -0
- package/dist/cjs/auth.js +103 -0
- package/dist/cjs/auth.js.map +1 -0
- package/dist/cjs/bin/http.d.ts +7 -0
- package/dist/cjs/bin/http.d.ts.map +1 -0
- package/dist/cjs/bin/http.js +24 -0
- package/dist/cjs/bin/http.js.map +1 -0
- package/dist/cjs/bin/stdio.d.ts +9 -0
- package/dist/cjs/bin/stdio.d.ts.map +1 -0
- package/dist/cjs/bin/stdio.js +15 -0
- package/dist/cjs/bin/stdio.js.map +1 -0
- package/dist/cjs/config.d.ts +22 -0
- package/dist/cjs/config.d.ts.map +1 -0
- package/dist/cjs/config.js +180 -0
- package/dist/cjs/config.js.map +1 -0
- package/dist/cjs/constants.d.ts +29 -0
- package/dist/cjs/constants.d.ts.map +1 -0
- package/dist/cjs/constants.js +35 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/errors.d.ts +43 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +72 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/http.d.ts +32 -0
- package/dist/cjs/http.d.ts.map +1 -0
- package/dist/cjs/http.js +180 -0
- package/dist/cjs/http.js.map +1 -0
- package/dist/cjs/index.d.ts +19 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +40 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/server.d.ts +14 -0
- package/dist/cjs/server.d.ts.map +1 -0
- package/dist/cjs/server.js +92 -0
- package/dist/cjs/server.js.map +1 -0
- package/dist/cjs/stdio.d.ts +9 -0
- package/dist/cjs/stdio.d.ts.map +1 -0
- package/dist/cjs/stdio.js +21 -0
- package/dist/cjs/stdio.js.map +1 -0
- package/dist/cjs/tools/balance.d.ts +9 -0
- package/dist/cjs/tools/balance.d.ts.map +1 -0
- package/dist/cjs/tools/balance.js +61 -0
- package/dist/cjs/tools/balance.js.map +1 -0
- package/dist/cjs/tools/index.d.ts +17 -0
- package/dist/cjs/tools/index.d.ts.map +1 -0
- package/dist/cjs/tools/index.js +40 -0
- package/dist/cjs/tools/index.js.map +1 -0
- package/dist/cjs/tools/receive.d.ts +13 -0
- package/dist/cjs/tools/receive.d.ts.map +1 -0
- package/dist/cjs/tools/receive.js +100 -0
- package/dist/cjs/tools/receive.js.map +1 -0
- package/dist/cjs/tools/register.d.ts +9 -0
- package/dist/cjs/tools/register.d.ts.map +1 -0
- package/dist/cjs/tools/register.js +36 -0
- package/dist/cjs/tools/register.js.map +1 -0
- package/dist/cjs/tools/schema.d.ts +18 -0
- package/dist/cjs/tools/schema.d.ts.map +1 -0
- package/dist/cjs/tools/schema.js +25 -0
- package/dist/cjs/tools/schema.js.map +1 -0
- package/dist/cjs/tools/send-to-wallet.d.ts +9 -0
- package/dist/cjs/tools/send-to-wallet.d.ts.map +1 -0
- package/dist/cjs/tools/send-to-wallet.js +60 -0
- package/dist/cjs/tools/send-to-wallet.js.map +1 -0
- package/dist/cjs/tools/send.d.ts +9 -0
- package/dist/cjs/tools/send.d.ts.map +1 -0
- package/dist/cjs/tools/send.js +68 -0
- package/dist/cjs/tools/send.js.map +1 -0
- package/dist/cjs/tools/status.d.ts +6 -0
- package/dist/cjs/tools/status.d.ts.map +1 -0
- package/dist/cjs/tools/status.js +39 -0
- package/dist/cjs/tools/status.js.map +1 -0
- package/dist/cjs/tools/sweep.d.ts +14 -0
- package/dist/cjs/tools/sweep.d.ts.map +1 -0
- package/dist/cjs/tools/sweep.js +119 -0
- package/dist/cjs/tools/sweep.js.map +1 -0
- package/dist/cjs/tools/x402-pay.d.ts +14 -0
- package/dist/cjs/tools/x402-pay.d.ts.map +1 -0
- package/dist/cjs/tools/x402-pay.js +78 -0
- package/dist/cjs/tools/x402-pay.js.map +1 -0
- package/dist/cjs/tools/x402-serve.d.ts +17 -0
- package/dist/cjs/tools/x402-serve.d.ts.map +1 -0
- package/dist/cjs/tools/x402-serve.js +133 -0
- package/dist/cjs/tools/x402-serve.js.map +1 -0
- package/dist/cjs/types.d.ts +66 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +6 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/auth.d.ts +41 -0
- package/dist/esm/auth.d.ts.map +1 -0
- package/dist/esm/auth.js +99 -0
- package/dist/esm/auth.js.map +1 -0
- package/dist/esm/bin/http.d.ts +7 -0
- package/dist/esm/bin/http.d.ts.map +1 -0
- package/dist/esm/bin/http.js +22 -0
- package/dist/esm/bin/http.js.map +1 -0
- package/dist/esm/bin/stdio.d.ts +9 -0
- package/dist/esm/bin/stdio.d.ts.map +1 -0
- package/dist/esm/bin/stdio.js +13 -0
- package/dist/esm/bin/stdio.js.map +1 -0
- package/dist/esm/config.d.ts +22 -0
- package/dist/esm/config.d.ts.map +1 -0
- package/dist/esm/config.js +175 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/constants.d.ts +29 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +32 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/errors.d.ts +43 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +61 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/http.d.ts +32 -0
- package/dist/esm/http.d.ts.map +1 -0
- package/dist/esm/http.js +177 -0
- package/dist/esm/http.js.map +1 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/server.d.ts +14 -0
- package/dist/esm/server.d.ts.map +1 -0
- package/dist/esm/server.js +89 -0
- package/dist/esm/server.js.map +1 -0
- package/dist/esm/stdio.d.ts +9 -0
- package/dist/esm/stdio.d.ts.map +1 -0
- package/dist/esm/stdio.js +18 -0
- package/dist/esm/stdio.js.map +1 -0
- package/dist/esm/tools/balance.d.ts +9 -0
- package/dist/esm/tools/balance.d.ts.map +1 -0
- package/dist/esm/tools/balance.js +58 -0
- package/dist/esm/tools/balance.js.map +1 -0
- package/dist/esm/tools/index.d.ts +17 -0
- package/dist/esm/tools/index.d.ts.map +1 -0
- package/dist/esm/tools/index.js +28 -0
- package/dist/esm/tools/index.js.map +1 -0
- package/dist/esm/tools/receive.d.ts +13 -0
- package/dist/esm/tools/receive.d.ts.map +1 -0
- package/dist/esm/tools/receive.js +97 -0
- package/dist/esm/tools/receive.js.map +1 -0
- package/dist/esm/tools/register.d.ts +9 -0
- package/dist/esm/tools/register.d.ts.map +1 -0
- package/dist/esm/tools/register.js +33 -0
- package/dist/esm/tools/register.js.map +1 -0
- package/dist/esm/tools/schema.d.ts +18 -0
- package/dist/esm/tools/schema.d.ts.map +1 -0
- package/dist/esm/tools/schema.js +21 -0
- package/dist/esm/tools/schema.js.map +1 -0
- package/dist/esm/tools/send-to-wallet.d.ts +9 -0
- package/dist/esm/tools/send-to-wallet.d.ts.map +1 -0
- package/dist/esm/tools/send-to-wallet.js +57 -0
- package/dist/esm/tools/send-to-wallet.js.map +1 -0
- package/dist/esm/tools/send.d.ts +9 -0
- package/dist/esm/tools/send.d.ts.map +1 -0
- package/dist/esm/tools/send.js +65 -0
- package/dist/esm/tools/send.js.map +1 -0
- package/dist/esm/tools/status.d.ts +6 -0
- package/dist/esm/tools/status.d.ts.map +1 -0
- package/dist/esm/tools/status.js +36 -0
- package/dist/esm/tools/status.js.map +1 -0
- package/dist/esm/tools/sweep.d.ts +14 -0
- package/dist/esm/tools/sweep.d.ts.map +1 -0
- package/dist/esm/tools/sweep.js +116 -0
- package/dist/esm/tools/sweep.js.map +1 -0
- package/dist/esm/tools/x402-pay.d.ts +14 -0
- package/dist/esm/tools/x402-pay.d.ts.map +1 -0
- package/dist/esm/tools/x402-pay.js +75 -0
- package/dist/esm/tools/x402-pay.js.map +1 -0
- package/dist/esm/tools/x402-serve.d.ts +17 -0
- package/dist/esm/tools/x402-serve.d.ts.map +1 -0
- package/dist/esm/tools/x402-serve.js +130 -0
- package/dist/esm/tools/x402-serve.js.map +1 -0
- package/dist/esm/types.d.ts +66 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +5 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ShroudFi contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @shroud-fi/mcp-server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol server exposing the ShroudFi privacy SDK as tools any MCP-aware agent runtime can call — Claude Code, Cursor, Windsurf, Zed AI, custom hosts.
|
|
4
|
+
|
|
5
|
+
Two transports:
|
|
6
|
+
|
|
7
|
+
| Transport | Use when | Auth |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| **stdio** | An agent runtime spawns the server as a child process from a local `.mcp.json` | None — process boundary IS the trust boundary; local key file or env var |
|
|
10
|
+
| **HTTP** | Multiple agents share one server, or the server lives on a different host | EIP-191 challenge-response — every call signs a fresh server-issued nonce |
|
|
11
|
+
|
|
12
|
+
## Tools
|
|
13
|
+
|
|
14
|
+
| Tool | Args | Purpose |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `shroud_register` | none | Publish the agent's stealth meta-address into ERC-6538. Idempotent. |
|
|
17
|
+
| `shroud_send` | `to`, `asset`, `amount` | Send to an encoded meta-address (`st:base:0x…`) — privacy-native shape. |
|
|
18
|
+
| `shroud_send_to_wallet` | `recipient`, `asset`, `amount` | Send to a plain wallet — SDK looks up its meta-address. |
|
|
19
|
+
| `shroud_receive` | `fromBlock`, `toBlock?`, `finality?`, `limit?` | Scan a block range for incoming stealth payments. |
|
|
20
|
+
| `shroud_sweep` | `detection`, `destination`, `token?`, `viaRelayer?` | Sweep a detected stealth address (direct or gasless). |
|
|
21
|
+
| `shroud_balance` | `address?`, `token?` | Read ETH + optional ERC-20 balance. |
|
|
22
|
+
| `shroud_status` | none | Operator wallet, meta-address, chain, latest block, registration state. |
|
|
23
|
+
| `shroud_x402_pay` | `url`, `method?`, `body?`, `headers?`, `maxPriceUsdcAtomic?` | One-shot HTTP 402 client. SDK auto-signs USDC if a 402 lands. |
|
|
24
|
+
| `shroud_x402_serve` | `op: 'challenge'\|'verify'`, … | Server-side x402 primitives — mint 402 challenges or verify payment payloads. |
|
|
25
|
+
|
|
26
|
+
## Environment
|
|
27
|
+
|
|
28
|
+
| Var | Required | Default | Notes |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| `SHROUDFI_PRIVATE_KEY` | one of the two | — | 0x… 32-byte hex. Operator EOA. |
|
|
31
|
+
| `SHROUDFI_PRIVATE_KEY_FILE` | one of the two | — | Path to a file containing the hex private key (one line, trimmed). |
|
|
32
|
+
| `SHROUDFI_CHAIN` | no | `base` | `base`, `sepolia`, `8453`, or `84532`. |
|
|
33
|
+
| `SHROUDFI_RPC_URL` | no | public default | Alchemy or QuickNode endpoint. |
|
|
34
|
+
| `SHROUDFI_START_BLOCK` | no | `0` | Where `shroud_receive` defaults its scan cursor. |
|
|
35
|
+
| `SHROUDFI_MCP_HTTP_PORT` | HTTP only | `7070` | Listening port. |
|
|
36
|
+
| `SHROUDFI_MCP_HTTP_ALLOWED_WALLETS` | HTTP recommended | empty (any) | Comma-separated EOA allow-list. **Set this in production.** |
|
|
37
|
+
| `SHROUDFI_MASTER_SEED` | multi-surface | random per boot | 0x… 32-byte hex. Required when MCP + REST + UI must resolve the same stealth meta-address for the same operator. Skip = different surfaces will disagree on the recipient identity. |
|
|
38
|
+
| `SHROUDFI_MASTER_SEED_FILE` | alt to above | — | Path to a file containing the 0x… seed (one line, trimmed). Use this in production so the seed never ends up in shell history. |
|
|
39
|
+
|
|
40
|
+
## Claude Code — `.mcp.json` (per-project)
|
|
41
|
+
|
|
42
|
+
Drop this at the repo root or `~/.config/claude/.mcp.json` (global):
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"shroudfi": {
|
|
48
|
+
"command": "node",
|
|
49
|
+
"args": ["node_modules/@shroud-fi/mcp-server/dist/esm/bin/stdio.js"],
|
|
50
|
+
"env": {
|
|
51
|
+
"SHROUDFI_PRIVATE_KEY_FILE": "/absolute/path/to/agent-eoa.key.txt",
|
|
52
|
+
"SHROUDFI_CHAIN": "base",
|
|
53
|
+
"SHROUDFI_RPC_URL": "https://base-mainnet.g.alchemy.com/v2/<your-key>"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
After install, Claude Code surfaces every `shroud_*` tool as a callable from within any session inside that repo.
|
|
61
|
+
|
|
62
|
+
## Cursor / Windsurf / Zed AI
|
|
63
|
+
|
|
64
|
+
Same JSON shape under the editor's own MCP config path:
|
|
65
|
+
|
|
66
|
+
| Editor | Config path |
|
|
67
|
+
|---|---|
|
|
68
|
+
| Cursor | Settings → Features → MCP Servers (UI) OR `~/.cursor/mcp.json` |
|
|
69
|
+
| Windsurf | Settings → MCP Servers OR `~/.codeium/windsurf/mcp_config.json` |
|
|
70
|
+
| Zed AI | `~/.config/zed/settings.json` → `assistant.context_servers` |
|
|
71
|
+
|
|
72
|
+
## HTTP transport (deployed server)
|
|
73
|
+
|
|
74
|
+
For shared-infra mode (one server, many agents):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
SHROUDFI_PRIVATE_KEY_FILE=/secrets/relayer.key.txt \
|
|
78
|
+
SHROUDFI_CHAIN=base \
|
|
79
|
+
SHROUDFI_MCP_HTTP_PORT=7070 \
|
|
80
|
+
SHROUDFI_MCP_HTTP_ALLOWED_WALLETS=0xAgent1,0xAgent2 \
|
|
81
|
+
node node_modules/@shroud-fi/mcp-server/dist/esm/bin/http.js
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Auth flow for HTTP clients
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// 1. Get a challenge.
|
|
88
|
+
const challenge = await fetch('http://localhost:7070/auth/challenge', {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: { 'content-type': 'application/json' },
|
|
91
|
+
body: JSON.stringify({ wallet: '0xYourAgentEOA' }),
|
|
92
|
+
}).then((r) => r.json());
|
|
93
|
+
|
|
94
|
+
// 2. Sign the message string (EIP-191 personal_sign).
|
|
95
|
+
const signature = await walletClient.signMessage({ message: challenge.message });
|
|
96
|
+
|
|
97
|
+
// 3. Call any MCP method with the three auth headers.
|
|
98
|
+
const response = await fetch('http://localhost:7070/mcp', {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: {
|
|
101
|
+
'content-type': 'application/json',
|
|
102
|
+
'x-shroudfi-wallet': '0xYourAgentEOA',
|
|
103
|
+
'x-shroudfi-nonce': challenge.nonce,
|
|
104
|
+
'x-shroudfi-signature': signature,
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
method: 'tools/call',
|
|
108
|
+
params: {
|
|
109
|
+
name: 'shroud_send_to_wallet',
|
|
110
|
+
arguments: {
|
|
111
|
+
recipient: '0xRecipientWallet',
|
|
112
|
+
asset: { token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
|
|
113
|
+
amount: '10000',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
}),
|
|
117
|
+
}).then((r) => r.json());
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Challenges are single-use (consumed on success) and expire after 5 minutes.
|
|
121
|
+
|
|
122
|
+
## Privacy
|
|
123
|
+
|
|
124
|
+
- Stdio bin writes errors only to stderr — stdout is reserved for the MCP wire protocol.
|
|
125
|
+
- HTTP transport returns short tag strings on auth failure (`unauthorized`, `auth_signature_invalid`, `auth_challenge_expired`); no signature bytes, no key bytes, no amount values are echoed in responses or in error fields.
|
|
126
|
+
- The MCP tool envelope deliberately does not log tool arguments — `shroud_receive` returns stealth private keys to the caller, and those must never end up in a server log.
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT — same as the rest of the ShroudFi workspace.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EIP-191 challenge-response auth for the HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Client POSTs /auth/challenge with their wallet address.
|
|
6
|
+
* 2. Server returns a fresh nonce + a canonical message string.
|
|
7
|
+
* 3. Client signs message with personal_sign / signMessage (EIP-191).
|
|
8
|
+
* 4. Client sends every subsequent request with:
|
|
9
|
+
* X-Shroudfi-Wallet: <0x… EOA>
|
|
10
|
+
* X-Shroudfi-Nonce: <nonce returned at step 2>
|
|
11
|
+
* X-Shroudfi-Signature: <0x… hex sig>
|
|
12
|
+
* 5. Server recovers, checks address match + expiry + allow-list.
|
|
13
|
+
*
|
|
14
|
+
* The signature is single-shot — once verifyAndConsume returns true for a
|
|
15
|
+
* nonce, the cache entry is deleted, so the same signed challenge cannot be
|
|
16
|
+
* replayed.
|
|
17
|
+
*
|
|
18
|
+
* Privacy: signature bytes never appear in logs, error messages, or response
|
|
19
|
+
* bodies. Errors are short tags only.
|
|
20
|
+
*/
|
|
21
|
+
import type { Hex } from 'viem';
|
|
22
|
+
import type { Eip191Challenge } from './types.js';
|
|
23
|
+
export interface AuthCache {
|
|
24
|
+
issue(wallet: `0x${string}`): Eip191Challenge;
|
|
25
|
+
verifyAndConsume(args: {
|
|
26
|
+
wallet: `0x${string}`;
|
|
27
|
+
nonce: Hex;
|
|
28
|
+
signature: Hex;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
/** Test seam — drop expired entries. Called automatically on every op. */
|
|
31
|
+
prune(now?: number): void;
|
|
32
|
+
/** Returns the current size — test seam. */
|
|
33
|
+
size(): number;
|
|
34
|
+
}
|
|
35
|
+
declare function buildMessage(wallet: `0x${string}`, nonce: Hex, issuedAtMs: number): string;
|
|
36
|
+
export declare function createAuthCache(ttlMs?: number): AuthCache;
|
|
37
|
+
export declare const __auth_test__: {
|
|
38
|
+
buildMessage: typeof buildMessage;
|
|
39
|
+
};
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAOhC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,GAAG,eAAe,CAAC;IAC9C,gBAAgB,CAAC,IAAI,EAAE;QACrB,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;QACtB,KAAK,EAAE,GAAG,CAAC;QACX,SAAS,EAAE,GAAG,CAAC;KAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,0EAA0E;IAC1E,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,IAAI,IAAI,MAAM,CAAC;CAChB;AAID,iBAAS,YAAY,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAWnF;AAED,wBAAgB,eAAe,CAC7B,KAAK,GAAE,MAAgC,GACtC,SAAS,CAgEX;AAED,eAAO,MAAM,aAAa;;CAAmB,CAAC"}
|
package/dist/cjs/auth.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* EIP-191 challenge-response auth for the HTTP transport.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Client POSTs /auth/challenge with their wallet address.
|
|
7
|
+
* 2. Server returns a fresh nonce + a canonical message string.
|
|
8
|
+
* 3. Client signs message with personal_sign / signMessage (EIP-191).
|
|
9
|
+
* 4. Client sends every subsequent request with:
|
|
10
|
+
* X-Shroudfi-Wallet: <0x… EOA>
|
|
11
|
+
* X-Shroudfi-Nonce: <nonce returned at step 2>
|
|
12
|
+
* X-Shroudfi-Signature: <0x… hex sig>
|
|
13
|
+
* 5. Server recovers, checks address match + expiry + allow-list.
|
|
14
|
+
*
|
|
15
|
+
* The signature is single-shot — once verifyAndConsume returns true for a
|
|
16
|
+
* nonce, the cache entry is deleted, so the same signed challenge cannot be
|
|
17
|
+
* replayed.
|
|
18
|
+
*
|
|
19
|
+
* Privacy: signature bytes never appear in logs, error messages, or response
|
|
20
|
+
* bodies. Errors are short tags only.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.__auth_test__ = void 0;
|
|
24
|
+
exports.createAuthCache = createAuthCache;
|
|
25
|
+
const node_crypto_1 = require("node:crypto");
|
|
26
|
+
const viem_1 = require("viem");
|
|
27
|
+
const constants_js_1 = require("./constants.js");
|
|
28
|
+
const errors_js_1 = require("./errors.js");
|
|
29
|
+
const SHROUDFI_DOMAIN_MARK = 'ShroudFi MCP HTTP authentication';
|
|
30
|
+
function buildMessage(wallet, nonce, issuedAtMs) {
|
|
31
|
+
// The message is deliberately self-describing so a wallet UI shows the
|
|
32
|
+
// user the exact intent before signing. The nonce is the only random
|
|
33
|
+
// piece — it ties the signature to a single server-issued challenge.
|
|
34
|
+
return [
|
|
35
|
+
SHROUDFI_DOMAIN_MARK,
|
|
36
|
+
`Wallet: ${wallet}`,
|
|
37
|
+
`Nonce: ${nonce}`,
|
|
38
|
+
`Issued: ${new Date(issuedAtMs).toISOString()}`,
|
|
39
|
+
'Sign to prove control of this wallet.',
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
function createAuthCache(ttlMs = constants_js_1.EIP191_CHALLENGE_TTL_MS) {
|
|
43
|
+
const store = new Map();
|
|
44
|
+
function makeKey(wallet, nonce) {
|
|
45
|
+
return `${wallet.toLowerCase()}|${nonce.toLowerCase()}`;
|
|
46
|
+
}
|
|
47
|
+
function prune(now = Date.now()) {
|
|
48
|
+
for (const [k, v] of store) {
|
|
49
|
+
if (v.expiresAtMs <= now)
|
|
50
|
+
store.delete(k);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
issue(wallet) {
|
|
55
|
+
prune();
|
|
56
|
+
const nonceBytes = (0, node_crypto_1.randomBytes)(16);
|
|
57
|
+
const nonce = `0x${nonceBytes.toString('hex')}`;
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
const ch = {
|
|
60
|
+
wallet: wallet.toLowerCase(),
|
|
61
|
+
nonce,
|
|
62
|
+
issuedAtMs: now,
|
|
63
|
+
expiresAtMs: now + ttlMs,
|
|
64
|
+
message: buildMessage(wallet.toLowerCase(), nonce, now),
|
|
65
|
+
};
|
|
66
|
+
store.set(makeKey(ch.wallet, ch.nonce), ch);
|
|
67
|
+
return ch;
|
|
68
|
+
},
|
|
69
|
+
async verifyAndConsume({ wallet, nonce, signature }) {
|
|
70
|
+
// Don't prune before lookup — we want callers to see the explicit
|
|
71
|
+
// `auth_challenge_expired` code when their nonce has timed out, instead
|
|
72
|
+
// of an indistinguishable `unauthorized`.
|
|
73
|
+
const key = makeKey(wallet, nonce);
|
|
74
|
+
const ch = store.get(key);
|
|
75
|
+
if (ch === undefined) {
|
|
76
|
+
throw new errors_js_1.McpUnauthorizedError();
|
|
77
|
+
}
|
|
78
|
+
if (ch.expiresAtMs <= Date.now()) {
|
|
79
|
+
store.delete(key);
|
|
80
|
+
throw new errors_js_1.McpEip191ChallengeExpiredError();
|
|
81
|
+
}
|
|
82
|
+
let recovered;
|
|
83
|
+
try {
|
|
84
|
+
recovered = await (0, viem_1.recoverMessageAddress)({
|
|
85
|
+
message: ch.message,
|
|
86
|
+
signature,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new errors_js_1.McpEip191SignatureInvalidError();
|
|
91
|
+
}
|
|
92
|
+
if (recovered.toLowerCase() !== ch.wallet.toLowerCase()) {
|
|
93
|
+
throw new errors_js_1.McpEip191SignatureInvalidError();
|
|
94
|
+
}
|
|
95
|
+
// Single-use: consume the nonce so the signed challenge can't be replayed.
|
|
96
|
+
store.delete(key);
|
|
97
|
+
},
|
|
98
|
+
prune,
|
|
99
|
+
size: () => store.size,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
exports.__auth_test__ = { buildMessage };
|
|
103
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/bin/http.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* HTTP transport bin. Reads SHROUDFI_MCP_HTTP_PORT (default 7070) via the
|
|
5
|
+
* config module, boots the HTTP server, parks until SIGINT/SIGTERM.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const http_js_1 = require("../http.js");
|
|
9
|
+
const config_js_1 = require("../config.js");
|
|
10
|
+
const port = (0, config_js_1.readHttpPortFromEnv)();
|
|
11
|
+
(0, http_js_1.runHttpServer)({ port })
|
|
12
|
+
.then((handle) => {
|
|
13
|
+
process.stderr.write(`shroudfi-mcp-http listening on :${port}\n`);
|
|
14
|
+
const shutdown = () => {
|
|
15
|
+
void handle.close().then(() => process.exit(0));
|
|
16
|
+
};
|
|
17
|
+
process.on('SIGINT', shutdown);
|
|
18
|
+
process.on('SIGTERM', shutdown);
|
|
19
|
+
})
|
|
20
|
+
.catch((err) => {
|
|
21
|
+
process.stderr.write(`shroudfi-mcp-http failed: ${err.code ?? 'unknown'}\n`);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/bin/http.ts"],"names":[],"mappings":";;AACA;;;GAGG;;AAEH,wCAA2C;AAC3C,4CAAmD;AAEnD,MAAM,IAAI,GAAG,IAAA,+BAAmB,GAAE,CAAC;AAEnC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,CAAC;KACpB,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;IACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,IAAI,IAAI,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA8B,GAAyB,CAAC,IAAI,IAAI,SAAS,IAAI,CAC9E,CAAC;IACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stdio bin — invoked by MCP clients via `.mcp.json` `command` field.
|
|
4
|
+
*
|
|
5
|
+
* Errors go to stderr only — stdout is reserved for the JSON-RPC framed
|
|
6
|
+
* MCP protocol stream. Any stdout pollution corrupts the client.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=stdio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../src/bin/stdio.ts"],"names":[],"mappings":";AACA;;;;;GAKG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Stdio bin — invoked by MCP clients via `.mcp.json` `command` field.
|
|
5
|
+
*
|
|
6
|
+
* Errors go to stderr only — stdout is reserved for the JSON-RPC framed
|
|
7
|
+
* MCP protocol stream. Any stdout pollution corrupts the client.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const stdio_js_1 = require("../stdio.js");
|
|
11
|
+
(0, stdio_js_1.runStdioServer)().catch((err) => {
|
|
12
|
+
process.stderr.write(`shroudfi-mcp stdio failed: ${err.code ?? 'unknown'}\n`);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../src/bin/stdio.ts"],"names":[],"mappings":";;AACA;;;;;GAKG;;AAEH,0CAA6C;AAE7C,IAAA,yBAAc,GAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8BAA+B,GAAyB,CAAC,IAAI,IAAI,SAAS,IAAI,CAC/E,CAAC;IACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve operator config from env (or explicit args) into a runtime context.
|
|
3
|
+
*
|
|
4
|
+
* Env reads concentrated here per verify-privacy R5. Allowed to use process.env.
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServerBootstrapConfig, McpServerContext } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Read the HTTP port the operator wants the MCP server to listen on. Defaults
|
|
9
|
+
* to 7070. Strict integer between 1 and 65535.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readHttpPortFromEnv(defaultPort?: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve env into a fully-formed bootstrap config.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadBootstrapConfigFromEnv(): McpServerBootstrapConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Build a McpServerContext from a resolved bootstrap config.
|
|
18
|
+
*
|
|
19
|
+
* Throws McpConfigError if the chain is unknown to @shroud-fi/transport.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildContextFromConfig(config: McpServerBootstrapConfig): McpServerContext;
|
|
22
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoBH,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AA2FpB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAO,GAAG,MAAM,CAQ9D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,wBAAwB,CAkBrE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,wBAAwB,GAC/B,gBAAgB,CAmClB"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve operator config from env (or explicit args) into a runtime context.
|
|
4
|
+
*
|
|
5
|
+
* Env reads concentrated here per verify-privacy R5. Allowed to use process.env.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.readHttpPortFromEnv = readHttpPortFromEnv;
|
|
9
|
+
exports.loadBootstrapConfigFromEnv = loadBootstrapConfigFromEnv;
|
|
10
|
+
exports.buildContextFromConfig = buildContextFromConfig;
|
|
11
|
+
const node_fs_1 = require("node:fs");
|
|
12
|
+
const viem_1 = require("viem");
|
|
13
|
+
const transport_1 = require("@shroud-fi/transport");
|
|
14
|
+
const agent_runtime_1 = require("@shroud-fi/agent-runtime");
|
|
15
|
+
const constants_js_1 = require("./constants.js");
|
|
16
|
+
const errors_js_1 = require("./errors.js");
|
|
17
|
+
function readChain() {
|
|
18
|
+
const v = process.env[constants_js_1.ENV_CHAIN];
|
|
19
|
+
if (v === undefined || v === '')
|
|
20
|
+
return 8453;
|
|
21
|
+
if (v === 'base' || v === 'mainnet' || v === '8453')
|
|
22
|
+
return 8453;
|
|
23
|
+
if (v === 'sepolia' || v === 'base-sepolia' || v === '84532')
|
|
24
|
+
return 84532;
|
|
25
|
+
throw new errors_js_1.McpConfigError('Unknown chain — set SHROUDFI_CHAIN to base|sepolia');
|
|
26
|
+
}
|
|
27
|
+
function readPrivateKey() {
|
|
28
|
+
const inline = process.env[constants_js_1.ENV_PRIVATE_KEY];
|
|
29
|
+
if (typeof inline === 'string' && inline.length > 0) {
|
|
30
|
+
if (!(0, viem_1.isHex)(inline) || inline.length !== 66) {
|
|
31
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_PRIVATE_KEY is not a 0x… 32-byte hex');
|
|
32
|
+
}
|
|
33
|
+
return inline;
|
|
34
|
+
}
|
|
35
|
+
const file = process.env[constants_js_1.ENV_PRIVATE_KEY_FILE];
|
|
36
|
+
if (typeof file === 'string' && file.length > 0) {
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = (0, node_fs_1.readFileSync)(file, 'utf-8').trim();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_PRIVATE_KEY_FILE could not be read');
|
|
43
|
+
}
|
|
44
|
+
if (!(0, viem_1.isHex)(raw) || raw.length !== 66) {
|
|
45
|
+
throw new errors_js_1.McpConfigError('Private key file contents are not a 0x… 32-byte hex');
|
|
46
|
+
}
|
|
47
|
+
return raw;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the optional 32-byte master seed from env. Accepts either an inline
|
|
53
|
+
* 0x… hex string or a path to a file containing one (same shape as the
|
|
54
|
+
* private-key resolution). Returns `undefined` when neither is set so callers
|
|
55
|
+
* can fall back to per-process random seeds.
|
|
56
|
+
*/
|
|
57
|
+
function readMasterSeed() {
|
|
58
|
+
const inline = process.env[constants_js_1.ENV_MASTER_SEED];
|
|
59
|
+
if (typeof inline === 'string' && inline.length > 0) {
|
|
60
|
+
if (!(0, viem_1.isHex)(inline) || inline.length !== 66) {
|
|
61
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_MASTER_SEED is not a 0x… 32-byte hex');
|
|
62
|
+
}
|
|
63
|
+
return (0, viem_1.hexToBytes)(inline);
|
|
64
|
+
}
|
|
65
|
+
const file = process.env[constants_js_1.ENV_MASTER_SEED_FILE];
|
|
66
|
+
if (typeof file === 'string' && file.length > 0) {
|
|
67
|
+
let raw;
|
|
68
|
+
try {
|
|
69
|
+
raw = (0, node_fs_1.readFileSync)(file, 'utf-8').trim();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_MASTER_SEED_FILE could not be read');
|
|
73
|
+
}
|
|
74
|
+
if (!(0, viem_1.isHex)(raw) || raw.length !== 66) {
|
|
75
|
+
throw new errors_js_1.McpConfigError('Master seed file contents are not a 0x… 32-byte hex');
|
|
76
|
+
}
|
|
77
|
+
return (0, viem_1.hexToBytes)(raw);
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
function readStartBlock() {
|
|
82
|
+
const v = process.env[constants_js_1.ENV_START_BLOCK];
|
|
83
|
+
if (v === undefined || v === '')
|
|
84
|
+
return 0n;
|
|
85
|
+
try {
|
|
86
|
+
return BigInt(v);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_START_BLOCK must be a non-negative integer');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function readHttpAllowedWallets() {
|
|
93
|
+
const v = process.env[constants_js_1.ENV_HTTP_ALLOWED_WALLETS];
|
|
94
|
+
if (v === undefined || v === '')
|
|
95
|
+
return new Set();
|
|
96
|
+
const out = new Set();
|
|
97
|
+
for (const piece of v.split(',')) {
|
|
98
|
+
const trimmed = piece.trim().toLowerCase();
|
|
99
|
+
if (trimmed.length === 0)
|
|
100
|
+
continue;
|
|
101
|
+
if (!(0, viem_1.isHex)(trimmed) || trimmed.length !== 42) {
|
|
102
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_MCP_HTTP_ALLOWED_WALLETS contains a non-address entry');
|
|
103
|
+
}
|
|
104
|
+
out.add(trimmed);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read the HTTP port the operator wants the MCP server to listen on. Defaults
|
|
110
|
+
* to 7070. Strict integer between 1 and 65535.
|
|
111
|
+
*/
|
|
112
|
+
function readHttpPortFromEnv(defaultPort = 7070) {
|
|
113
|
+
const raw = process.env[constants_js_1.ENV_HTTP_PORT];
|
|
114
|
+
if (raw === undefined || raw.length === 0)
|
|
115
|
+
return defaultPort;
|
|
116
|
+
const port = Number(raw);
|
|
117
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
|
118
|
+
throw new errors_js_1.McpConfigError('SHROUDFI_MCP_HTTP_PORT must be 1..65535');
|
|
119
|
+
}
|
|
120
|
+
return port;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolve env into a fully-formed bootstrap config.
|
|
124
|
+
*/
|
|
125
|
+
function loadBootstrapConfigFromEnv() {
|
|
126
|
+
const chainId = readChain();
|
|
127
|
+
const rpcOverride = process.env[constants_js_1.ENV_RPC_URL];
|
|
128
|
+
const rpcUrl = rpcOverride !== undefined && rpcOverride.length > 0
|
|
129
|
+
? rpcOverride
|
|
130
|
+
: constants_js_1.DEFAULT_RPC_URL_BY_CHAIN[chainId];
|
|
131
|
+
if (rpcUrl === undefined) {
|
|
132
|
+
throw new errors_js_1.McpConfigError('No RPC URL resolved for the requested chain');
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
chainId,
|
|
136
|
+
rpcUrl,
|
|
137
|
+
privateKey: readPrivateKey(),
|
|
138
|
+
startBlock: readStartBlock(),
|
|
139
|
+
masterSeed: readMasterSeed(),
|
|
140
|
+
httpAllowedWallets: readHttpAllowedWallets(),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build a McpServerContext from a resolved bootstrap config.
|
|
145
|
+
*
|
|
146
|
+
* Throws McpConfigError if the chain is unknown to @shroud-fi/transport.
|
|
147
|
+
*/
|
|
148
|
+
function buildContextFromConfig(config) {
|
|
149
|
+
const chainName = config.chainId === 8453 ? 'base' : 'baseSepolia';
|
|
150
|
+
const transport = (0, transport_1.createTransport)({
|
|
151
|
+
chain: chainName,
|
|
152
|
+
rpcUrl: config.rpcUrl,
|
|
153
|
+
...(config.privateKey !== undefined
|
|
154
|
+
? { privateKey: config.privateKey }
|
|
155
|
+
: {}),
|
|
156
|
+
});
|
|
157
|
+
// Will throw UnknownChainError if no manifest entry — surface as config error.
|
|
158
|
+
let stealthContract;
|
|
159
|
+
try {
|
|
160
|
+
stealthContract = (0, transport_1.getShroudFiStealth)(config.chainId);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
throw new errors_js_1.McpConfigError('No ShroudFiStealth deployment for the chain');
|
|
164
|
+
}
|
|
165
|
+
const agent = (0, agent_runtime_1.createShroudAgent)({
|
|
166
|
+
transport,
|
|
167
|
+
startBlock: config.startBlock,
|
|
168
|
+
stealthContract,
|
|
169
|
+
autoRegister: true,
|
|
170
|
+
...(config.masterSeed !== undefined ? { masterSeed: config.masterSeed } : {}),
|
|
171
|
+
});
|
|
172
|
+
const walletAddress = transport.walletClient?.account?.address;
|
|
173
|
+
return {
|
|
174
|
+
agent,
|
|
175
|
+
transport,
|
|
176
|
+
chainId: config.chainId,
|
|
177
|
+
walletAddress,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static constants. All env reads live here per verify-privacy R5.
|
|
3
|
+
*/
|
|
4
|
+
export declare const SHROUDFI_MCP_SERVER_NAME: "shroudfi-mcp";
|
|
5
|
+
export declare const SHROUDFI_MCP_SERVER_VERSION: "0.0.1";
|
|
6
|
+
/** EIP-191 challenge TTL — short window so a replayed challenge dies fast. */
|
|
7
|
+
export declare const EIP191_CHALLENGE_TTL_MS: number;
|
|
8
|
+
/** Default RPC URLs by chain id. Operator override via SHROUDFI_RPC_URL. */
|
|
9
|
+
export declare const DEFAULT_RPC_URL_BY_CHAIN: Record<number, string>;
|
|
10
|
+
/** Env knobs the bootstrap surface reads. */
|
|
11
|
+
export declare const ENV_PRIVATE_KEY: "SHROUDFI_PRIVATE_KEY";
|
|
12
|
+
export declare const ENV_PRIVATE_KEY_FILE: "SHROUDFI_PRIVATE_KEY_FILE";
|
|
13
|
+
export declare const ENV_RPC_URL: "SHROUDFI_RPC_URL";
|
|
14
|
+
export declare const ENV_CHAIN: "SHROUDFI_CHAIN";
|
|
15
|
+
export declare const ENV_START_BLOCK: "SHROUDFI_START_BLOCK";
|
|
16
|
+
export declare const ENV_HTTP_PORT: "SHROUDFI_MCP_HTTP_PORT";
|
|
17
|
+
export declare const ENV_HTTP_ALLOWED_WALLETS: "SHROUDFI_MCP_HTTP_ALLOWED_WALLETS";
|
|
18
|
+
/**
|
|
19
|
+
* Deterministic 32-byte master seed (0x… 64 hex chars). When set, every
|
|
20
|
+
* boot of the MCP server, REST API, or any other surface using
|
|
21
|
+
* `loadBootstrapConfigFromEnv` resolves the SAME stealth meta-address for
|
|
22
|
+
* the same operator EOA. When unset, a fresh random seed is generated per
|
|
23
|
+
* process — fine for local testing, BAD for production multi-surface
|
|
24
|
+
* deployments (caller's main wallet ↔ stealth meta-address binding would
|
|
25
|
+
* desync between MCP, REST, and UI processes).
|
|
26
|
+
*/
|
|
27
|
+
export declare const ENV_MASTER_SEED: "SHROUDFI_MASTER_SEED";
|
|
28
|
+
export declare const ENV_MASTER_SEED_FILE: "SHROUDFI_MASTER_SEED_FILE";
|
|
29
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,wBAAwB,EAAG,cAAuB,CAAC;AAChE,eAAO,MAAM,2BAA2B,EAAG,OAAgB,CAAC;AAE5D,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,QAAgB,CAAC;AAErD,4EAA4E;AAC5E,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG3D,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,eAAe,EAAG,sBAA+B,CAAC;AAC/D,eAAO,MAAM,oBAAoB,EAAG,2BAAoC,CAAC;AACzE,eAAO,MAAM,WAAW,EAAG,kBAA2B,CAAC;AACvD,eAAO,MAAM,SAAS,EAAG,gBAAyB,CAAC;AACnD,eAAO,MAAM,eAAe,EAAG,sBAA+B,CAAC;AAC/D,eAAO,MAAM,aAAa,EAAG,wBAAiC,CAAC;AAC/D,eAAO,MAAM,wBAAwB,EAAG,mCAA4C,CAAC;AACrF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAG,sBAA+B,CAAC;AAC/D,eAAO,MAAM,oBAAoB,EAAG,2BAAoC,CAAC"}
|