@rickydata/agent0-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/.env.example +17 -0
- package/Dockerfile +25 -0
- package/README.md +85 -0
- package/dist/auth/sdk-client.d.ts +47 -0
- package/dist/auth/sdk-client.js +142 -0
- package/dist/auth/sdk-client.js.map +1 -0
- package/dist/auth/token.d.ts +5 -0
- package/dist/auth/token.js +6 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/auth/wallet-derivation.d.ts +26 -0
- package/dist/auth/wallet-derivation.js +76 -0
- package/dist/auth/wallet-derivation.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +140 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/a2a.d.ts +3 -0
- package/dist/tools/a2a.js +170 -0
- package/dist/tools/a2a.js.map +1 -0
- package/dist/tools/discovery.d.ts +3 -0
- package/dist/tools/discovery.js +465 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +38 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/payments.d.ts +3 -0
- package/dist/tools/payments.js +124 -0
- package/dist/tools/payments.js.map +1 -0
- package/dist/tools/registration.d.ts +3 -0
- package/dist/tools/registration.js +324 -0
- package/dist/tools/registration.js.map +1 -0
- package/dist/tools/reputation.d.ts +3 -0
- package/dist/tools/reputation.js +147 -0
- package/dist/tools/reputation.js.map +1 -0
- package/dist/utils/chains.d.ts +10 -0
- package/dist/utils/chains.js +33 -0
- package/dist/utils/chains.js.map +1 -0
- package/dist/utils/trust-labels.d.ts +10 -0
- package/dist/utils/trust-labels.js +48 -0
- package/dist/utils/trust-labels.js.map +1 -0
- package/dist/utils/validation.d.ts +12 -0
- package/dist/utils/validation.js +19 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +32 -0
- package/src/auth/sdk-client.ts +171 -0
- package/src/auth/token.ts +19 -0
- package/src/auth/wallet-derivation.ts +91 -0
- package/src/index.ts +184 -0
- package/src/tools/a2a.ts +205 -0
- package/src/tools/discovery.ts +517 -0
- package/src/tools/index.ts +45 -0
- package/src/tools/payments.ts +146 -0
- package/src/tools/registration.ts +389 -0
- package/src/tools/reputation.ts +183 -0
- package/src/utils/chains.ts +42 -0
- package/src/utils/trust-labels.ts +53 -0
- package/src/utils/validation.ts +20 -0
- package/tests/a2a.test.ts +234 -0
- package/tests/chains.test.ts +57 -0
- package/tests/discovery.test.ts +455 -0
- package/tests/e2e.test.ts +234 -0
- package/tests/payments.test.ts +148 -0
- package/tests/registration.test.ts +313 -0
- package/tests/reputation.test.ts +231 -0
- package/tests/sdk-client.test.ts +143 -0
- package/tests/tool-router.test.ts +28 -0
- package/tests/trust-labels.test.ts +229 -0
- package/tests/validation.test.ts +132 -0
- package/tests/wallet-derivation.test.ts +109 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +8 -0
package/.env.example
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Transport: stdio (default) or http
|
|
2
|
+
TRANSPORT=stdio
|
|
3
|
+
PORT=8080
|
|
4
|
+
|
|
5
|
+
# Agent0 / ERC-8004 configuration
|
|
6
|
+
AGENT0_CHAIN_ID=11155111
|
|
7
|
+
# AGENT0_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
|
|
8
|
+
|
|
9
|
+
# Wallet for write operations (choose one)
|
|
10
|
+
# ERC8004_PRIVATE_KEY=0x...
|
|
11
|
+
# ERC8004_DERIVED_KEY=0x...
|
|
12
|
+
|
|
13
|
+
# IPFS for agent registration
|
|
14
|
+
# PINATA_JWT=your_pinata_jwt
|
|
15
|
+
|
|
16
|
+
# Response capping
|
|
17
|
+
RESPONSE_MAX_LENGTH=200000
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
FROM node:22-alpine AS builder
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY package*.json tsconfig.json ./
|
|
4
|
+
RUN npm ci
|
|
5
|
+
COPY src ./src
|
|
6
|
+
RUN npm run build
|
|
7
|
+
|
|
8
|
+
FROM node:22-alpine
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY package*.json ./
|
|
11
|
+
RUN npm ci --only=production
|
|
12
|
+
COPY --from=builder /app/dist ./dist
|
|
13
|
+
|
|
14
|
+
ENV NODE_ENV=production
|
|
15
|
+
ENV PORT=8080
|
|
16
|
+
|
|
17
|
+
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
|
|
18
|
+
USER nodejs
|
|
19
|
+
|
|
20
|
+
EXPOSE 8080
|
|
21
|
+
|
|
22
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
23
|
+
CMD node -e "require('http').get('http://localhost:8080/health', (r) => {r.statusCode === 200 ? process.exit(0) : process.exit(1)})"
|
|
24
|
+
|
|
25
|
+
CMD ["node", "dist/index.js"]
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @rickydata/agent0-mcp
|
|
2
|
+
|
|
3
|
+
MCP server wrapping the [Agent0 SDK](https://www.npmjs.com/package/agent0-sdk) for ERC-8004 trustless agent operations. Discover, register, and interact with on-chain AI agents across Ethereum, Base, and Polygon.
|
|
4
|
+
|
|
5
|
+
## Quick Connect
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# stdio (Claude Code / Claude Desktop)
|
|
9
|
+
cd packages/agent0-mcp && npm run dev -- --stdio
|
|
10
|
+
|
|
11
|
+
# HTTP (Streamable HTTP transport)
|
|
12
|
+
cd packages/agent0-mcp && PORT=8080 npm run dev
|
|
13
|
+
|
|
14
|
+
# Claude Code
|
|
15
|
+
claude mcp add --transport http agent0-mcp http://localhost:8080/mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Tools (17)
|
|
19
|
+
|
|
20
|
+
### Discovery (read-only, no auth needed)
|
|
21
|
+
| Tool | Description |
|
|
22
|
+
|------|-------------|
|
|
23
|
+
| `search_agents` | Multi-chain agent search with filters (name, tools, skills, reputation) |
|
|
24
|
+
| `get_agent` | Get agent details by chainId:tokenId |
|
|
25
|
+
| `get_supported_chains` | List ERC-8004 supported chains and registry addresses |
|
|
26
|
+
| `get_platform_stats` | Aggregate stats (total agents, MCP/A2A/x402 counts) |
|
|
27
|
+
| `get_reputation_summary` | Reputation count + average + trust label for an agent |
|
|
28
|
+
| `search_feedback` | Search feedback by agent, reviewer, tags, value range |
|
|
29
|
+
|
|
30
|
+
### Authentication
|
|
31
|
+
| Tool | Description |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| `configure_wallet` | Set up wallet for write ops (private key or signature derivation) |
|
|
34
|
+
| `get_derivation_message` | Get the message to sign for key derivation |
|
|
35
|
+
| `get_auth_status` | Check wallet configuration status |
|
|
36
|
+
|
|
37
|
+
### Registration (requires wallet)
|
|
38
|
+
| Tool | Description |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| `register_agent` | Register new agent on-chain (IPFS or data URI) |
|
|
41
|
+
| `update_agent` | Update existing agent properties |
|
|
42
|
+
|
|
43
|
+
### Reputation (requires wallet)
|
|
44
|
+
| Tool | Description |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `give_feedback` | Submit on-chain feedback/review (0-100 score) |
|
|
47
|
+
| `revoke_feedback` | Revoke previously submitted feedback |
|
|
48
|
+
|
|
49
|
+
### Payments
|
|
50
|
+
| Tool | Description |
|
|
51
|
+
|------|-------------|
|
|
52
|
+
| `x402_request` | HTTP request with x402 payment handling |
|
|
53
|
+
|
|
54
|
+
### A2A (requires wallet)
|
|
55
|
+
| Tool | Description |
|
|
56
|
+
|------|-------------|
|
|
57
|
+
| `a2a_send_message` | Send message to agent via A2A protocol |
|
|
58
|
+
| `a2a_list_tasks` | List A2A tasks/conversations |
|
|
59
|
+
| `a2a_get_task` | Get A2A task details |
|
|
60
|
+
|
|
61
|
+
## Environment Variables
|
|
62
|
+
|
|
63
|
+
| Variable | Required | Description |
|
|
64
|
+
|----------|----------|-------------|
|
|
65
|
+
| `AGENT0_CHAIN_ID` | No | Default chain (default: 11155111 Sepolia) |
|
|
66
|
+
| `AGENT0_RPC_URL` | No | Custom RPC URL |
|
|
67
|
+
| `ERC8004_PRIVATE_KEY` | No | Direct private key for write ops |
|
|
68
|
+
| `ERC8004_DERIVED_KEY` | No | Previously derived key |
|
|
69
|
+
| `PINATA_JWT` | No | Pinata JWT for IPFS registration |
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install
|
|
75
|
+
npm run dev # stdio mode
|
|
76
|
+
npm run build # compile TypeScript
|
|
77
|
+
npm test # run vitest
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Docker
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
docker build -t agent0-mcp .
|
|
84
|
+
docker run -p 8080:8080 agent0-mcp
|
|
85
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent0 SDK client management.
|
|
3
|
+
*
|
|
4
|
+
* Provides read-only and authenticated SDK instances for ERC-8004 operations.
|
|
5
|
+
*
|
|
6
|
+
* Key resolution order:
|
|
7
|
+
* 1. ERC8004_PRIVATE_KEY env var (explicit private key)
|
|
8
|
+
* 2. ERC8004_DERIVED_KEY env var (previously derived key from wallet signature)
|
|
9
|
+
* 3. Prompt user for wallet signature derivation (via configure_wallet tool)
|
|
10
|
+
*/
|
|
11
|
+
import { SDK } from "agent0-sdk";
|
|
12
|
+
/**
|
|
13
|
+
* Get a read-only SDK instance (no signing capabilities).
|
|
14
|
+
* Suitable for search, discovery, and reputation queries.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getReadOnlySDK(chainId?: number): SDK;
|
|
17
|
+
/**
|
|
18
|
+
* Get an authenticated SDK instance (with signing capabilities).
|
|
19
|
+
* Returns null if no private key is available.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getAuthenticatedSDK(chainId?: number): SDK | null;
|
|
22
|
+
/**
|
|
23
|
+
* Set the derived private key (from wallet signature derivation).
|
|
24
|
+
* Clears cached authenticated SDK so it's rebuilt on next access.
|
|
25
|
+
*/
|
|
26
|
+
export declare function setDerivedKey(privateKey: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if an authenticated SDK is available (has a private key).
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasAuthentication(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Get the current chain ID.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getCurrentChainId(): number;
|
|
35
|
+
/**
|
|
36
|
+
* Set the current chain ID (clears cached SDK instances).
|
|
37
|
+
*/
|
|
38
|
+
export declare function setChainId(chainId: number): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get authentication status information.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getAuthStatus(): {
|
|
43
|
+
hasKey: boolean;
|
|
44
|
+
chainId: number;
|
|
45
|
+
source: "env:ERC8004_PRIVATE_KEY" | "env:ERC8004_DERIVED_KEY" | "derived" | "none";
|
|
46
|
+
isReadOnly: boolean;
|
|
47
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent0 SDK client management.
|
|
3
|
+
*
|
|
4
|
+
* Provides read-only and authenticated SDK instances for ERC-8004 operations.
|
|
5
|
+
*
|
|
6
|
+
* Key resolution order:
|
|
7
|
+
* 1. ERC8004_PRIVATE_KEY env var (explicit private key)
|
|
8
|
+
* 2. ERC8004_DERIVED_KEY env var (previously derived key from wallet signature)
|
|
9
|
+
* 3. Prompt user for wallet signature derivation (via configure_wallet tool)
|
|
10
|
+
*/
|
|
11
|
+
import { SDK } from "agent0-sdk";
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// CONFIGURATION
|
|
14
|
+
// ============================================================================
|
|
15
|
+
const DEFAULT_CHAIN_ID = parseInt(process.env.AGENT0_CHAIN_ID || "11155111", 10);
|
|
16
|
+
const RPC_URL = process.env.AGENT0_RPC_URL;
|
|
17
|
+
const IPFS_PROVIDER = (process.env.AGENT0_IPFS_PROVIDER || "pinata");
|
|
18
|
+
const PINATA_JWT = process.env.PINATA_JWT;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// SDK CLIENT STATE
|
|
21
|
+
// ============================================================================
|
|
22
|
+
let _readOnlySDK = null;
|
|
23
|
+
let _authenticatedSDK = null;
|
|
24
|
+
let _currentPrivateKey = null;
|
|
25
|
+
let _currentChainId = DEFAULT_CHAIN_ID;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the private key from environment or stored state.
|
|
28
|
+
* Returns null if no key is available (read-only mode).
|
|
29
|
+
*/
|
|
30
|
+
function resolvePrivateKey() {
|
|
31
|
+
// 1. Explicit private key
|
|
32
|
+
if (process.env.ERC8004_PRIVATE_KEY) {
|
|
33
|
+
return process.env.ERC8004_PRIVATE_KEY;
|
|
34
|
+
}
|
|
35
|
+
// 2. Previously derived key
|
|
36
|
+
if (process.env.ERC8004_DERIVED_KEY) {
|
|
37
|
+
return process.env.ERC8004_DERIVED_KEY;
|
|
38
|
+
}
|
|
39
|
+
// 3. Stored from configure_wallet
|
|
40
|
+
return _currentPrivateKey;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build SDK config for the given chain.
|
|
44
|
+
*/
|
|
45
|
+
function buildConfig(chainId, privateKey) {
|
|
46
|
+
const config = { chainId };
|
|
47
|
+
if (RPC_URL)
|
|
48
|
+
config.rpcUrl = RPC_URL;
|
|
49
|
+
if (privateKey)
|
|
50
|
+
config.privateKey = privateKey;
|
|
51
|
+
// Only configure IPFS when we have a private key (write operations need IPFS)
|
|
52
|
+
if (privateKey && PINATA_JWT) {
|
|
53
|
+
config.ipfs = IPFS_PROVIDER;
|
|
54
|
+
config.pinataJwt = PINATA_JWT;
|
|
55
|
+
}
|
|
56
|
+
return config;
|
|
57
|
+
}
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// PUBLIC API
|
|
60
|
+
// ============================================================================
|
|
61
|
+
/**
|
|
62
|
+
* Get a read-only SDK instance (no signing capabilities).
|
|
63
|
+
* Suitable for search, discovery, and reputation queries.
|
|
64
|
+
*/
|
|
65
|
+
export function getReadOnlySDK(chainId) {
|
|
66
|
+
const targetChain = chainId ?? _currentChainId;
|
|
67
|
+
// Cache the read-only SDK for the current chain
|
|
68
|
+
if (_readOnlySDK && targetChain === _currentChainId) {
|
|
69
|
+
return _readOnlySDK;
|
|
70
|
+
}
|
|
71
|
+
_readOnlySDK = new SDK(buildConfig(targetChain));
|
|
72
|
+
if (!chainId)
|
|
73
|
+
_currentChainId = targetChain;
|
|
74
|
+
return _readOnlySDK;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get an authenticated SDK instance (with signing capabilities).
|
|
78
|
+
* Returns null if no private key is available.
|
|
79
|
+
*/
|
|
80
|
+
export function getAuthenticatedSDK(chainId) {
|
|
81
|
+
const privateKey = resolvePrivateKey();
|
|
82
|
+
if (!privateKey)
|
|
83
|
+
return null;
|
|
84
|
+
const targetChain = chainId ?? _currentChainId;
|
|
85
|
+
// Return cached if key and chain haven't changed
|
|
86
|
+
if (_authenticatedSDK &&
|
|
87
|
+
_currentPrivateKey === privateKey &&
|
|
88
|
+
targetChain === _currentChainId) {
|
|
89
|
+
return _authenticatedSDK;
|
|
90
|
+
}
|
|
91
|
+
_authenticatedSDK = new SDK(buildConfig(targetChain, privateKey));
|
|
92
|
+
_currentPrivateKey = privateKey;
|
|
93
|
+
_currentChainId = targetChain;
|
|
94
|
+
return _authenticatedSDK;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Set the derived private key (from wallet signature derivation).
|
|
98
|
+
* Clears cached authenticated SDK so it's rebuilt on next access.
|
|
99
|
+
*/
|
|
100
|
+
export function setDerivedKey(privateKey) {
|
|
101
|
+
_currentPrivateKey = privateKey;
|
|
102
|
+
_authenticatedSDK = null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if an authenticated SDK is available (has a private key).
|
|
106
|
+
*/
|
|
107
|
+
export function hasAuthentication() {
|
|
108
|
+
return resolvePrivateKey() !== null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the current chain ID.
|
|
112
|
+
*/
|
|
113
|
+
export function getCurrentChainId() {
|
|
114
|
+
return _currentChainId;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Set the current chain ID (clears cached SDK instances).
|
|
118
|
+
*/
|
|
119
|
+
export function setChainId(chainId) {
|
|
120
|
+
_currentChainId = chainId;
|
|
121
|
+
_readOnlySDK = null;
|
|
122
|
+
_authenticatedSDK = null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get authentication status information.
|
|
126
|
+
*/
|
|
127
|
+
export function getAuthStatus() {
|
|
128
|
+
let source = "none";
|
|
129
|
+
if (process.env.ERC8004_PRIVATE_KEY)
|
|
130
|
+
source = "env:ERC8004_PRIVATE_KEY";
|
|
131
|
+
else if (process.env.ERC8004_DERIVED_KEY)
|
|
132
|
+
source = "env:ERC8004_DERIVED_KEY";
|
|
133
|
+
else if (_currentPrivateKey)
|
|
134
|
+
source = "derived";
|
|
135
|
+
return {
|
|
136
|
+
hasKey: source !== "none",
|
|
137
|
+
chainId: _currentChainId,
|
|
138
|
+
source,
|
|
139
|
+
isReadOnly: source === "none",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=sdk-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-client.js","sourceRoot":"","sources":["../../src/auth/sdk-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports for auth module.
|
|
3
|
+
*/
|
|
4
|
+
export { getDerivationMessage, deriveWalletFromSignature, verifyDerivationSignature, type DerivedWallet, } from "./wallet-derivation.js";
|
|
5
|
+
export { getReadOnlySDK, getAuthenticatedSDK, setDerivedKey, hasAuthentication, getCurrentChainId, setChainId, getAuthStatus, } from "./sdk-client.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports for auth module.
|
|
3
|
+
*/
|
|
4
|
+
export { getDerivationMessage, deriveWalletFromSignature, verifyDerivationSignature, } from "./wallet-derivation.js";
|
|
5
|
+
export { getReadOnlySDK, getAuthenticatedSDK, setDerivedKey, hasAuthentication, getCurrentChainId, setChainId, getAuthStatus, } from "./sdk-client.js";
|
|
6
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,GAE1B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,aAAa,GACd,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface DerivedWallet {
|
|
2
|
+
address: string;
|
|
3
|
+
privateKey: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Get the deterministic message that must be signed for key derivation.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getDerivationMessage(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Derive an ERC-8004 agent wallet from a wallet signature.
|
|
11
|
+
*
|
|
12
|
+
* Uses HKDF-SHA256 to extract a 32-byte private key from the signature.
|
|
13
|
+
* The same signature always produces the same derived key.
|
|
14
|
+
*
|
|
15
|
+
* @param signature - The wallet's signature of DERIVATION_MESSAGE (hex string with 0x prefix)
|
|
16
|
+
* @returns DerivedWallet with address and privateKey
|
|
17
|
+
*/
|
|
18
|
+
export declare function deriveWalletFromSignature(signature: string): DerivedWallet;
|
|
19
|
+
/**
|
|
20
|
+
* Verify that a signature was produced by the expected wallet address.
|
|
21
|
+
*
|
|
22
|
+
* @param signature - The signature to verify
|
|
23
|
+
* @param expectedAddress - The wallet address that should have signed
|
|
24
|
+
* @returns true if the signature was produced by expectedAddress
|
|
25
|
+
*/
|
|
26
|
+
export declare function verifyDerivationSignature(signature: string, expectedAddress: string): boolean;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet derivation for ERC-8004 agent identity.
|
|
3
|
+
*
|
|
4
|
+
* Derives a deterministic private key from a wallet signature using HKDF.
|
|
5
|
+
* This allows an MCP client user to derive a signing key for ERC-8004 operations
|
|
6
|
+
* without exposing their main wallet private key.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. User signs a deterministic message with their wallet
|
|
10
|
+
* 2. HKDF extracts a derived key from the signature
|
|
11
|
+
* 3. The derived key is used for agent0-sdk write operations
|
|
12
|
+
*/
|
|
13
|
+
import { hkdf } from "@noble/hashes/hkdf";
|
|
14
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
15
|
+
import { ethers } from "ethers";
|
|
16
|
+
/**
|
|
17
|
+
* The deterministic signing message used to derive the key.
|
|
18
|
+
* CRITICAL: This MUST NOT contain nonces, timestamps, or any variable data.
|
|
19
|
+
* If changed, all previously derived keys become invalid.
|
|
20
|
+
*/
|
|
21
|
+
const DERIVATION_MESSAGE = "Sign this message to derive your ERC-8004 agent key.\n\n" +
|
|
22
|
+
"This signature will be used to create a deterministic private key " +
|
|
23
|
+
"for managing your on-chain agent identity. " +
|
|
24
|
+
"This does not grant access to your funds.";
|
|
25
|
+
/**
|
|
26
|
+
* Get the deterministic message that must be signed for key derivation.
|
|
27
|
+
*/
|
|
28
|
+
export function getDerivationMessage() {
|
|
29
|
+
return DERIVATION_MESSAGE;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Derive an ERC-8004 agent wallet from a wallet signature.
|
|
33
|
+
*
|
|
34
|
+
* Uses HKDF-SHA256 to extract a 32-byte private key from the signature.
|
|
35
|
+
* The same signature always produces the same derived key.
|
|
36
|
+
*
|
|
37
|
+
* @param signature - The wallet's signature of DERIVATION_MESSAGE (hex string with 0x prefix)
|
|
38
|
+
* @returns DerivedWallet with address and privateKey
|
|
39
|
+
*/
|
|
40
|
+
export function deriveWalletFromSignature(signature) {
|
|
41
|
+
if (!signature || !signature.startsWith("0x")) {
|
|
42
|
+
throw new Error("Invalid signature: must be a hex string starting with 0x");
|
|
43
|
+
}
|
|
44
|
+
// Convert signature to bytes
|
|
45
|
+
const sigBytes = ethers.getBytes(signature);
|
|
46
|
+
// HKDF: extract + expand
|
|
47
|
+
// salt: "agent0-erc8004" (domain separation)
|
|
48
|
+
// info: "agent-key-v1" (versioned derivation context)
|
|
49
|
+
const salt = new TextEncoder().encode("agent0-erc8004");
|
|
50
|
+
const info = new TextEncoder().encode("agent-key-v1");
|
|
51
|
+
const derivedKeyBytes = hkdf(sha256, sigBytes, salt, info, 32);
|
|
52
|
+
// Create ethers wallet from derived key
|
|
53
|
+
const privateKey = ethers.hexlify(derivedKeyBytes);
|
|
54
|
+
const wallet = new ethers.Wallet(privateKey);
|
|
55
|
+
return {
|
|
56
|
+
address: wallet.address,
|
|
57
|
+
privateKey,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Verify that a signature was produced by the expected wallet address.
|
|
62
|
+
*
|
|
63
|
+
* @param signature - The signature to verify
|
|
64
|
+
* @param expectedAddress - The wallet address that should have signed
|
|
65
|
+
* @returns true if the signature was produced by expectedAddress
|
|
66
|
+
*/
|
|
67
|
+
export function verifyDerivationSignature(signature, expectedAddress) {
|
|
68
|
+
try {
|
|
69
|
+
const recovered = ethers.verifyMessage(DERIVATION_MESSAGE, signature);
|
|
70
|
+
return recovered.toLowerCase() === expectedAddress.toLowerCase();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=wallet-derivation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet-derivation.js","sourceRoot":"","sources":["../../src/auth/wallet-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAOhC;;;;GAIG;AACH,MAAM,kBAAkB,GACtB,0DAA0D;IAC1D,oEAAoE;IACpE,6CAA6C;IAC7C,2CAA2C,CAAC;AAE9C;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAiB;IACzD,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAE5C,yBAAyB;IACzB,6CAA6C;IAC7C,sDAAsD;IACtD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACtD,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAE/D,wCAAwC;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAE7C,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAiB,EACjB,eAAuB;IAEvB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;QACtE,OAAO,SAAS,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,WAAW,EAAE,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import express from "express";
|
|
7
|
+
import cors from "cors";
|
|
8
|
+
import { TOOLS, handleToolCall } from "./tools/index.js";
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// CONFIGURATION
|
|
11
|
+
// ============================================================================
|
|
12
|
+
const RESPONSE_MAX_LENGTH = parseInt(process.env.RESPONSE_MAX_LENGTH || "200000", 10);
|
|
13
|
+
const SERVER_NAME = "agent0-mcp";
|
|
14
|
+
const SERVER_VERSION = "0.1.0";
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// RESPONSE CAPPING
|
|
17
|
+
// ============================================================================
|
|
18
|
+
function truncateResponse(result) {
|
|
19
|
+
const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
20
|
+
if (text.length <= RESPONSE_MAX_LENGTH)
|
|
21
|
+
return text;
|
|
22
|
+
return (text.slice(0, RESPONSE_MAX_LENGTH) +
|
|
23
|
+
`\n\n--- Response truncated (${text.length} chars, limit ${RESPONSE_MAX_LENGTH}) ---`);
|
|
24
|
+
}
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// MCP HANDLER SETUP
|
|
27
|
+
// ============================================================================
|
|
28
|
+
function setupMCPHandlers(server) {
|
|
29
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
30
|
+
tools: TOOLS,
|
|
31
|
+
}));
|
|
32
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
33
|
+
const { name, arguments: args = {} } = request.params;
|
|
34
|
+
let result;
|
|
35
|
+
try {
|
|
36
|
+
result = await handleToolCall(name, args);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
40
|
+
result = { success: false, error: message };
|
|
41
|
+
}
|
|
42
|
+
const content = truncateResponse(result);
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: "text", text: content }],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const sessions = new Map();
|
|
49
|
+
// Clean up sessions older than 2 hours
|
|
50
|
+
setInterval(() => {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
for (const [id, session] of sessions) {
|
|
53
|
+
if (now - session.createdAt > 2 * 60 * 60 * 1000) {
|
|
54
|
+
session.transport.close();
|
|
55
|
+
session.server.close();
|
|
56
|
+
sessions.delete(id);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}, 5 * 60 * 1000);
|
|
60
|
+
function createServer() {
|
|
61
|
+
return new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: { listChanged: false } } });
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// TRANSPORT: HTTP (StreamableHTTP)
|
|
65
|
+
// ============================================================================
|
|
66
|
+
const app = express();
|
|
67
|
+
app.use(cors());
|
|
68
|
+
app.use(express.json());
|
|
69
|
+
app.get("/health", (_req, res) => {
|
|
70
|
+
res.json({
|
|
71
|
+
status: "ok",
|
|
72
|
+
server: SERVER_NAME,
|
|
73
|
+
version: SERVER_VERSION,
|
|
74
|
+
tools: TOOLS.length,
|
|
75
|
+
sessions: sessions.size,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
app.post("/mcp", async (req, res) => {
|
|
79
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
80
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
81
|
+
const session = sessions.get(sessionId);
|
|
82
|
+
await session.transport.handleRequest(req, res, req.body);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const newId = randomUUID();
|
|
86
|
+
const server = createServer();
|
|
87
|
+
setupMCPHandlers(server);
|
|
88
|
+
const transport = new StreamableHTTPServerTransport({
|
|
89
|
+
sessionIdGenerator: () => newId,
|
|
90
|
+
});
|
|
91
|
+
await server.connect(transport);
|
|
92
|
+
sessions.set(newId, { server, transport, createdAt: Date.now() });
|
|
93
|
+
await transport.handleRequest(req, res, req.body);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
app.get("/mcp", async (req, res) => {
|
|
97
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
98
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
99
|
+
res.status(400).json({
|
|
100
|
+
error: "Invalid or missing session. Send initialize first via POST.",
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await sessions.get(sessionId).transport.handleRequest(req, res);
|
|
105
|
+
});
|
|
106
|
+
app.delete("/mcp", async (req, res) => {
|
|
107
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
108
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
109
|
+
const session = sessions.get(sessionId);
|
|
110
|
+
await session.transport.handleRequest(req, res);
|
|
111
|
+
session.transport.close();
|
|
112
|
+
session.server.close();
|
|
113
|
+
sessions.delete(sessionId);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
res.status(400).json({ error: "Invalid or missing session." });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// START
|
|
121
|
+
// ============================================================================
|
|
122
|
+
const isStdio = process.argv.includes("--stdio");
|
|
123
|
+
if (isStdio) {
|
|
124
|
+
// In stdio mode, redirect console.log to stderr so it doesn't pollute the MCP JSON stream
|
|
125
|
+
console.log = console.error;
|
|
126
|
+
const server = createServer();
|
|
127
|
+
setupMCPHandlers(server);
|
|
128
|
+
const transport = new StdioServerTransport();
|
|
129
|
+
await server.connect(transport);
|
|
130
|
+
console.error(`${SERVER_NAME} running on stdio (${TOOLS.length} tools)`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const port = parseInt(process.env.PORT || "8080", 10);
|
|
134
|
+
app.listen(port, () => {
|
|
135
|
+
console.log(`${SERVER_NAME} running on port ${port}`);
|
|
136
|
+
console.log(`Tools: ${TOOLS.length}`);
|
|
137
|
+
console.log(`Endpoints: /health /mcp`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|