@andrewkimjoseph/celina 0.3.1 → 0.3.3
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/README.md +65 -49
- package/build/clients/ens-client.d.ts +10 -0
- package/build/clients/ens-client.js +23 -0
- package/build/clients/ens-client.js.map +1 -0
- package/build/config/env.d.ts +1 -0
- package/build/config/env.js +1 -0
- package/build/config/env.js.map +1 -1
- package/build/context/app-context.d.ts +4 -1
- package/build/context/app-context.js +3 -1
- package/build/context/app-context.js.map +1 -1
- package/build/schemas/common.d.ts +2 -0
- package/build/schemas/common.js +8 -0
- package/build/schemas/common.js.map +1 -1
- package/build/server/create-server.js +3 -1
- package/build/server/create-server.js.map +1 -1
- package/build/server/instructions.js +2 -1
- package/build/server/instructions.js.map +1 -1
- package/build/services/ens.service.d.ts +30 -0
- package/build/services/ens.service.js +78 -0
- package/build/services/ens.service.js.map +1 -0
- package/build/tools/ens.tools.d.ts +2 -0
- package/build/tools/ens.tools.js +27 -0
- package/build/tools/ens.tools.js.map +1 -0
- package/build/tools/index.js +2 -0
- package/build/tools/index.js.map +1 -1
- package/build/tools/transaction.tools.js +11 -7
- package/build/tools/transaction.tools.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/andrewkimjoseph/celina/main/assets/logo.png" alt="Celina logo — yellow C with profile silhouette on black" width="160" />
|
|
2
|
+
<img src="https://raw.githubusercontent.com/andrewkimjoseph/celina/main/assets/logo-yellow.png#gh-dark-mode-only" alt="Celina logo — yellow C with profile silhouette on black" width="160" />
|
|
3
|
+
<img src="https://raw.githubusercontent.com/andrewkimjoseph/celina/main/assets/logo-black.png#gh-light-mode-only" alt="Celina logo — black C with profile silhouette" width="160" />
|
|
3
4
|
</p>
|
|
4
5
|
|
|
5
6
|
<h1 align="center">Celina — Celo MCP Server</h1>
|
|
@@ -24,9 +25,9 @@ npm: [@andrewkimjoseph/celina](https://www.npmjs.com/package/@andrewkimjoseph/ce
|
|
|
24
25
|
|
|
25
26
|
## Quick start
|
|
26
27
|
|
|
27
|
-
Celina is not meant to be run manually in a terminal for normal use. Your MCP client (Cursor, Claude Desktop, LM Studio, etc.) spawns it as a child process and talks to it over stdio.
|
|
28
|
+
Celina is not meant to be run manually in a terminal for normal use. Your MCP client (Cursor, Claude Desktop, LM Studio, etc.) spawns it as a child process and talks to it over stdio.
|
|
28
29
|
|
|
29
|
-
**
|
|
30
|
+
**Recommended:** install from npm, then add Celina to your MCP config (see [MCP setup](#mcp-setup)).
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
33
|
npm i @andrewkimjoseph/celina
|
|
@@ -40,30 +41,39 @@ npm run build
|
|
|
40
41
|
npm start
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
##
|
|
44
|
+
## MCP setup
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
Pick your client, install the package, paste the config, restart. Celina shows up as MCP tools your LLM can call.
|
|
46
47
|
|
|
47
|
-
###
|
|
48
|
+
### Local stdio (recommended)
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
|
|
51
|
-
openssl rsa -pubout -in private.pem -out public.pem
|
|
52
|
-
```
|
|
50
|
+
Install the package, then point your MCP client at the built entry. Works in any stdio client (Cursor, Claude Desktop, LM Studio, Continue, MCP Inspector). Requires Node.js ≥ 20.
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
1. Run `npm i @andrewkimjoseph/celina`
|
|
53
|
+
2. Open your MCP config (e.g. `claude_desktop_config.json`, Cursor **Settings → MCP**) and merge the snippet below into `mcpServers`
|
|
54
|
+
3. Replace the path with your absolute path to `node_modules/@andrewkimjoseph/celina/build/index.js`, then restart the client
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"celina": {
|
|
60
|
+
"type": "stdio",
|
|
61
|
+
"command": "node",
|
|
62
|
+
"args": ["/absolute/path/to/node_modules/@andrewkimjoseph/celina/build/index.js"],
|
|
63
|
+
"env": {
|
|
64
|
+
"CELO_PRIVATE_KEY": "0x...",
|
|
65
|
+
"SELF_AGENT_PRIVATE_KEY": "0x..."
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
Keep `CELO_PRIVATE_KEY` and `SELF_AGENT_PRIVATE_KEY` out of source control — they stay on your machine. Omit both for read-only chain queries.
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
### Cursor — remote (streamable HTTP)
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
No local install — paste and go. Write tools use the [hosted encryption flow](#write-tools-hosted-mode) (no plaintext keys in config).
|
|
67
77
|
|
|
68
78
|
```json
|
|
69
79
|
{
|
|
@@ -101,43 +111,46 @@ Fully quit and relaunch Claude Desktop after editing the config (closing the win
|
|
|
101
111
|
|
|
102
112
|
> **Pro / Max / Team / Enterprise:** you can skip `mcp-remote` and add `https://mcp.celina.andrewkimjoseph.com/mcp` under **Settings → Integrations** instead.
|
|
103
113
|
|
|
104
|
-
### Local stdio (
|
|
114
|
+
### Local stdio (from source)
|
|
105
115
|
|
|
106
|
-
|
|
116
|
+
For development from a cloned repo, point at your local `build/index.js`:
|
|
107
117
|
|
|
108
118
|
```json
|
|
109
119
|
{
|
|
110
120
|
"mcpServers": {
|
|
111
121
|
"celina": {
|
|
122
|
+
"type": "stdio",
|
|
112
123
|
"command": "node",
|
|
113
|
-
"args": ["/absolute/path/to/
|
|
124
|
+
"args": ["/absolute/path/to/celina/build/index.js"],
|
|
125
|
+
"env": {
|
|
126
|
+
"CELO_PRIVATE_KEY": "0x...",
|
|
127
|
+
"SELF_AGENT_PRIVATE_KEY": "0x..."
|
|
128
|
+
}
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
131
|
}
|
|
117
132
|
```
|
|
118
133
|
|
|
119
|
-
|
|
134
|
+
## Deploy to Render
|
|
120
135
|
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
"mcpServers": {
|
|
124
|
-
"celina": {
|
|
125
|
-
"command": "node",
|
|
126
|
-
"args": ["/absolute/path/to/celina/build/index.js"]
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
136
|
+
This project includes a [Render Blueprint](render.yaml) for one-click deployment as a public Streamable HTTP MCP server.
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
### 1. Generate an RSA key pair
|
|
133
139
|
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
140
|
+
```bash
|
|
141
|
+
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
|
|
142
|
+
openssl rsa -pubout -in private.pem -out public.pem
|
|
138
143
|
```
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
### 2. Deploy
|
|
146
|
+
|
|
147
|
+
1. Push this repo to GitHub
|
|
148
|
+
2. Render Dashboard → **New → Blueprint** → connect the repo
|
|
149
|
+
3. Set `WALLET_ENCRYPTION_PRIVATE_KEY` in the Render Environment tab (paste contents of `private.pem`)
|
|
150
|
+
4. (Optional) Add a custom domain in Render and set `ALLOWED_HOSTS` to that hostname (comma-separated if multiple)
|
|
151
|
+
5. Your MCP endpoint will be at your Render URL + `/mcp`
|
|
152
|
+
|
|
153
|
+
> **Note:** Free Render services spin down after ~15 minutes of inactivity. Cold starts can take 30–60 seconds and may cause MCP client timeouts. Use a Starter plan for always-on hosting.
|
|
141
154
|
|
|
142
155
|
## Local LLM integration
|
|
143
156
|
|
|
@@ -147,10 +160,10 @@ Read-only tools (balances, blocks, GoodDollar status, etc.) work out of the box.
|
|
|
147
160
|
|
|
148
161
|
### LM Studio (0.3.17+)
|
|
149
162
|
|
|
150
|
-
LM Studio can host MCP servers directly via `mcp.json
|
|
163
|
+
LM Studio can host MCP servers directly via `mcp.json`. After `npm i @andrewkimjoseph/celina`:
|
|
151
164
|
|
|
152
165
|
1. Open LM Studio → **Program** → **Install** → **Edit mcp.json**
|
|
153
|
-
2. Add Celina under `mcpServers`
|
|
166
|
+
2. Add Celina under `mcpServers` (same JSON as [Local stdio (recommended)](#local-stdio-recommended))
|
|
154
167
|
3. In **Server Settings**, enable **Allow calling servers from mcp.json**
|
|
155
168
|
4. Chat with a tool-capable model (e.g. Qwen 2.5, Llama 3.1+)
|
|
156
169
|
|
|
@@ -158,17 +171,19 @@ LM Studio can host MCP servers directly via `mcp.json` (same format as Cursor).
|
|
|
158
171
|
{
|
|
159
172
|
"mcpServers": {
|
|
160
173
|
"celina": {
|
|
174
|
+
"type": "stdio",
|
|
161
175
|
"command": "node",
|
|
162
176
|
"args": ["/absolute/path/to/node_modules/@andrewkimjoseph/celina/build/index.js"],
|
|
163
177
|
"env": {
|
|
164
|
-
"CELO_PRIVATE_KEY": "0x..."
|
|
178
|
+
"CELO_PRIVATE_KEY": "0x...",
|
|
179
|
+
"SELF_AGENT_PRIVATE_KEY": "0x..."
|
|
165
180
|
}
|
|
166
181
|
}
|
|
167
182
|
}
|
|
168
183
|
}
|
|
169
184
|
```
|
|
170
185
|
|
|
171
|
-
Omit
|
|
186
|
+
Omit both env vars for read-only chain queries.
|
|
172
187
|
|
|
173
188
|
### Open WebUI + Ollama
|
|
174
189
|
|
|
@@ -197,7 +212,9 @@ For write tools over HTTP, set `WALLET_ENCRYPTION_PRIVATE_KEY` in `.env` (see [D
|
|
|
197
212
|
|
|
198
213
|
[Continue](https://docs.continue.dev/customize/deep-dives/mcp) works with local models (Ollama, LM Studio, etc.) in **agent mode**.
|
|
199
214
|
|
|
200
|
-
|
|
215
|
+
1. Run `npm i @andrewkimjoseph/celina`
|
|
216
|
+
2. Create `.continue/mcpServers/celina.yaml` in your workspace
|
|
217
|
+
3. Switch Continue to agent mode and prompt
|
|
201
218
|
|
|
202
219
|
```yaml
|
|
203
220
|
name: Celina
|
|
@@ -206,14 +223,13 @@ schema: v1
|
|
|
206
223
|
mcpServers:
|
|
207
224
|
- name: celina
|
|
208
225
|
type: stdio
|
|
209
|
-
command:
|
|
226
|
+
command: npx
|
|
210
227
|
args:
|
|
211
|
-
- "
|
|
212
|
-
|
|
213
|
-
CELO_PRIVATE_KEY: "0x..."
|
|
228
|
+
- "-y"
|
|
229
|
+
- "@andrewkimjoseph/celina"
|
|
214
230
|
```
|
|
215
231
|
|
|
216
|
-
Alternatively, copy the [local stdio JSON](#local-stdio-
|
|
232
|
+
Alternatively, copy the [local stdio JSON](#local-stdio-recommended) into `.continue/mcpServers/mcp.json` — Continue picks up Claude/Cursor-style configs automatically.
|
|
217
233
|
|
|
218
234
|
### Test without an LLM
|
|
219
235
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type PublicClient } from "viem";
|
|
2
|
+
import type { AppConfig } from "../config/env.js";
|
|
3
|
+
export declare const DEFAULT_ETH_RPC_URL = "https://ethereum.publicnode.com";
|
|
4
|
+
export declare const ENS_CCIP_GATEWAY = "https://ccip.ens.xyz";
|
|
5
|
+
export declare class EnsClientFactory {
|
|
6
|
+
private readonly config;
|
|
7
|
+
private client;
|
|
8
|
+
constructor(config: AppConfig);
|
|
9
|
+
getClient(): PublicClient;
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createPublicClient, http } from "viem";
|
|
2
|
+
import { mainnet } from "viem/chains";
|
|
3
|
+
export const DEFAULT_ETH_RPC_URL = "https://ethereum.publicnode.com";
|
|
4
|
+
export const ENS_CCIP_GATEWAY = "https://ccip.ens.xyz";
|
|
5
|
+
export class EnsClientFactory {
|
|
6
|
+
config;
|
|
7
|
+
client = null;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
getClient() {
|
|
12
|
+
if (this.client) {
|
|
13
|
+
return this.client;
|
|
14
|
+
}
|
|
15
|
+
const rpcUrl = this.config.ethRpcUrl ?? DEFAULT_ETH_RPC_URL;
|
|
16
|
+
this.client = createPublicClient({
|
|
17
|
+
chain: mainnet,
|
|
18
|
+
transport: http(rpcUrl, { timeout: 15_000 }),
|
|
19
|
+
});
|
|
20
|
+
return this.client;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=ens-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ens-client.js","sourceRoot":"","sources":["../../src/clients/ens-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAqB,MAAM,MAAM,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC,MAAM,CAAC,MAAM,mBAAmB,GAAG,iCAAiC,CAAC;AAErE,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEvD,MAAM,OAAO,gBAAgB;IAGE;IAFrB,MAAM,GAAwB,IAAI,CAAC;IAE3C,YAA6B,MAAiB;QAAjB,WAAM,GAAN,MAAM,CAAW;IAAG,CAAC;IAElD,SAAS;QACP,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,mBAAmB,CAAC;QAC5D,IAAI,CAAC,MAAM,GAAG,kBAAkB,CAAC;YAC/B,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;SAC7C,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF"}
|
package/build/config/env.d.ts
CHANGED
package/build/config/env.js
CHANGED
package/build/config/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,wBAAwB;QACpE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC1C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,gBAA6C;QACrE,mBAAmB,EAAE,OAAO,CAAC,GAAG,CAAC,sBAEpB;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CeloClientFactory } from "../clients/celo-client.js";
|
|
2
|
+
import type { EnsClientFactory } from "../clients/ens-client.js";
|
|
2
3
|
import { BlockchainService } from "../services/blockchain.service.js";
|
|
3
4
|
import { AccountService } from "../services/account.service.js";
|
|
4
5
|
import { TokenService } from "../services/token.service.js";
|
|
@@ -7,6 +8,7 @@ import { MentoFxService } from "../services/mento-fx.service.js";
|
|
|
7
8
|
import { GoodDollarService } from "../services/gooddollar.service.js";
|
|
8
9
|
import { AaveService } from "../services/aave.service.js";
|
|
9
10
|
import { SelfService } from "../services/self.service.js";
|
|
11
|
+
import { EnsService } from "../services/ens.service.js";
|
|
10
12
|
export interface AppContext {
|
|
11
13
|
config: {
|
|
12
14
|
hasWallet: boolean;
|
|
@@ -21,5 +23,6 @@ export interface AppContext {
|
|
|
21
23
|
gooddollar: GoodDollarService;
|
|
22
24
|
aave: AaveService;
|
|
23
25
|
self: SelfService;
|
|
26
|
+
ens: EnsService;
|
|
24
27
|
}
|
|
25
|
-
export declare function createAppContext(clientFactory: CeloClientFactory, walletAddress?: `0x${string}`, selfAgentPrivateKey?: `0x${string}`): AppContext;
|
|
28
|
+
export declare function createAppContext(clientFactory: CeloClientFactory, ensClientFactory: EnsClientFactory, walletAddress?: `0x${string}`, selfAgentPrivateKey?: `0x${string}`): AppContext;
|
|
@@ -6,7 +6,8 @@ import { MentoFxService } from "../services/mento-fx.service.js";
|
|
|
6
6
|
import { GoodDollarService } from "../services/gooddollar.service.js";
|
|
7
7
|
import { AaveService } from "../services/aave.service.js";
|
|
8
8
|
import { SelfService } from "../services/self.service.js";
|
|
9
|
-
|
|
9
|
+
import { EnsService } from "../services/ens.service.js";
|
|
10
|
+
export function createAppContext(clientFactory, ensClientFactory, walletAddress, selfAgentPrivateKey) {
|
|
10
11
|
return {
|
|
11
12
|
config: {
|
|
12
13
|
hasWallet: Boolean(walletAddress),
|
|
@@ -21,6 +22,7 @@ export function createAppContext(clientFactory, walletAddress, selfAgentPrivateK
|
|
|
21
22
|
gooddollar: new GoodDollarService(clientFactory),
|
|
22
23
|
aave: new AaveService(clientFactory),
|
|
23
24
|
self: new SelfService(clientFactory, selfAgentPrivateKey),
|
|
25
|
+
ens: new EnsService(ensClientFactory),
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
//# sourceMappingURL=app-context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-context.js","sourceRoot":"","sources":["../../src/context/app-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-context.js","sourceRoot":"","sources":["../../src/context/app-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAmBxD,MAAM,UAAU,gBAAgB,CAC9B,aAAgC,EAChC,gBAAkC,EAClC,aAA6B,EAC7B,mBAAmC;IAEnC,OAAO;QACL,MAAM,EAAE;YACN,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC;YACjC,aAAa;YACb,eAAe,EAAE,OAAO,CAAC,mBAAmB,CAAC;SAC9C;QACD,UAAU,EAAE,IAAI,iBAAiB,CAAC,aAAa,CAAC;QAChD,OAAO,EAAE,IAAI,cAAc,CAAC,aAAa,CAAC;QAC1C,KAAK,EAAE,IAAI,YAAY,CAAC,aAAa,CAAC;QACtC,WAAW,EAAE,IAAI,kBAAkB,CAAC,aAAa,CAAC;QAClD,OAAO,EAAE,IAAI,cAAc,CAAC,aAAa,CAAC;QAC1C,UAAU,EAAE,IAAI,iBAAiB,CAAC,aAAa,CAAC;QAChD,IAAI,EAAE,IAAI,WAAW,CAAC,aAAa,CAAC;QACpC,IAAI,EAAE,IAAI,WAAW,CAAC,aAAa,EAAE,mBAAmB,CAAC;QACzD,GAAG,EAAE,IAAI,UAAU,CAAC,gBAAgB,CAAC;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const addressSchema: z.ZodString;
|
|
3
|
+
export declare const addressOrEnsSchema: z.ZodString;
|
|
3
4
|
export declare const blockIdSchema: z.ZodUnion<[z.ZodNumber, z.ZodString, z.ZodLiteral<"latest">, z.ZodLiteral<"pending">]>;
|
|
4
5
|
export declare const tokenSymbolSchema: z.ZodString;
|
|
6
|
+
export declare const ensNameSchema: z.ZodString;
|
package/build/schemas/common.js
CHANGED
|
@@ -2,6 +2,10 @@ import { z } from "zod";
|
|
|
2
2
|
export const addressSchema = z
|
|
3
3
|
.string()
|
|
4
4
|
.regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address");
|
|
5
|
+
export const addressOrEnsSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.min(3)
|
|
8
|
+
.describe("Recipient 0x address or ENS name (e.g. andrewkimjoseph.celo.eth, celina.eth)");
|
|
5
9
|
export const blockIdSchema = z
|
|
6
10
|
.union([
|
|
7
11
|
z.number().int().nonnegative(),
|
|
@@ -13,4 +17,8 @@ export const blockIdSchema = z
|
|
|
13
17
|
export const tokenSymbolSchema = z
|
|
14
18
|
.string()
|
|
15
19
|
.describe("Token symbol (e.g. CELO, USDm, G$, GoodDollar) or 0x contract address");
|
|
20
|
+
export const ensNameSchema = z
|
|
21
|
+
.string()
|
|
22
|
+
.min(3)
|
|
23
|
+
.describe("ENS name, e.g. celina.eth or vitalik.eth");
|
|
16
24
|
//# sourceMappingURL=common.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/schemas/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,MAAM,EAAE;KACR,KAAK,CAAC,qBAAqB,EAAE,0BAA0B,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,KAAK,CAAC;IACL,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC9B,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;IAC1D,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACnB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;CACrB,CAAC;KACD,QAAQ,CAAC,uCAAuC,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,QAAQ,CAAC,uEAAuE,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/schemas/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,MAAM,EAAE;KACR,KAAK,CAAC,qBAAqB,EAAE,0BAA0B,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC;KAChC,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,CAAC;KACN,QAAQ,CACP,8EAA8E,CAC/E,CAAC;AAEJ,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,KAAK,CAAC;IACL,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC9B,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;IAC1D,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACnB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;CACrB,CAAC;KACD,QAAQ,CAAC,uCAAuC,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,QAAQ,CAAC,uEAAuE,CAAC,CAAC;AAErF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,CAAC;KACN,QAAQ,CAAC,0CAA0C,CAAC,CAAC"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { loadConfig } from "../config/env.js";
|
|
3
3
|
import { CeloClientFactory } from "../clients/celo-client.js";
|
|
4
|
+
import { EnsClientFactory } from "../clients/ens-client.js";
|
|
4
5
|
import { createAppContext } from "../context/app-context.js";
|
|
5
6
|
import { registerAllTools } from "../tools/index.js";
|
|
6
7
|
import { SERVER_INSTRUCTIONS } from "./instructions.js";
|
|
7
8
|
export function createServer() {
|
|
8
9
|
const config = loadConfig();
|
|
9
10
|
const clientFactory = new CeloClientFactory(config);
|
|
11
|
+
const ensClientFactory = new EnsClientFactory(config);
|
|
10
12
|
const clients = clientFactory.getClients();
|
|
11
13
|
const server = new McpServer({ name: "celina", version: "0.2.0" }, {
|
|
12
14
|
instructions: SERVER_INSTRUCTIONS,
|
|
@@ -15,7 +17,7 @@ export function createServer() {
|
|
|
15
17
|
logging: {},
|
|
16
18
|
},
|
|
17
19
|
});
|
|
18
|
-
registerAllTools(server, createAppContext(clientFactory, clients.accountAddress, config.selfAgentPrivateKey));
|
|
20
|
+
registerAllTools(server, createAppContext(clientFactory, ensClientFactory, clients.accountAddress, config.selfAgentPrivateKey));
|
|
19
21
|
return server;
|
|
20
22
|
}
|
|
21
23
|
//# sourceMappingURL=create-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-server.js","sourceRoot":"","sources":["../../src/server/create-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EACpC;QACE,YAAY,EAAE,mBAAmB;QACjC,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAC5B,OAAO,EAAE,EAAE;SACZ;KACF,CACF,CAAC;IAEF,gBAAgB,CACd,MAAM,EACN,gBAAgB,CACd,aAAa,EACb,OAAO,CAAC,cAAc,EACtB,MAAM,CAAC,mBAAmB,CAC3B,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"create-server.js","sourceRoot":"","sources":["../../src/server/create-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,aAAa,GAAG,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EACpC;QACE,YAAY,EAAE,mBAAmB;QACjC,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAC5B,OAAO,EAAE,EAAE;SACZ;KACF,CACF,CAAC;IAEF,gBAAgB,CACd,MAAM,EACN,gBAAgB,CACd,aAAa,EACb,gBAAgB,EAChB,OAAO,CAAC,cAAc,EACtB,MAAM,CAAC,mBAAmB,CAC3B,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -2,7 +2,8 @@ export const SERVER_INSTRUCTIONS = `
|
|
|
2
2
|
You are connected to Celina, the Celo MCP server (mainnet only).
|
|
3
3
|
|
|
4
4
|
Guidelines:
|
|
5
|
-
- Prefer read-only tools (get_
|
|
5
|
+
- Prefer read-only tools (get_*, resolve_ens) before any write operation.
|
|
6
|
+
- send_token and estimate_send accept ENS names (e.g. andrewkimjoseph.celo.eth) directly; use resolve_ens for standalone lookups.
|
|
6
7
|
- Always call estimate_send before send_token when possible.
|
|
7
8
|
- For Mento FX conversions, call get_mento_fx_quote and estimate_mento_fx before execute_mento_fx.
|
|
8
9
|
- Write tools require CELO_PRIVATE_KEY in the server environment.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/server/instructions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/server/instructions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;CAoBlC,CAAC,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type EnsClientFactory } from "../clients/ens-client.js";
|
|
2
|
+
export type EnsResolveChain = "celo" | "ethereum";
|
|
3
|
+
export type ResolvedRecipient = {
|
|
4
|
+
address: `0x${string}`;
|
|
5
|
+
ens?: {
|
|
6
|
+
name: string;
|
|
7
|
+
normalizedName: string;
|
|
8
|
+
resolvedVia?: "celo" | "ethereum";
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export declare class EnsService {
|
|
12
|
+
private readonly ensClientFactory;
|
|
13
|
+
constructor(ensClientFactory: EnsClientFactory);
|
|
14
|
+
resolveEns(name: string, chain?: EnsResolveChain): Promise<{
|
|
15
|
+
name: string;
|
|
16
|
+
normalizedName: string;
|
|
17
|
+
address: `0x${string}`;
|
|
18
|
+
coinType: string;
|
|
19
|
+
chain: "ethereum";
|
|
20
|
+
resolvedVia?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
name: string;
|
|
23
|
+
normalizedName: string;
|
|
24
|
+
address: `0x${string}`;
|
|
25
|
+
coinType: string;
|
|
26
|
+
chain: "celo";
|
|
27
|
+
resolvedVia: "celo" | "ethereum";
|
|
28
|
+
}>;
|
|
29
|
+
resolveAddressOrEns(input: string): Promise<ResolvedRecipient>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { toCoinType } from "viem";
|
|
2
|
+
import { celo } from "viem/chains";
|
|
3
|
+
import { getEnsAddress, normalize } from "viem/ens";
|
|
4
|
+
import { ENS_CCIP_GATEWAY, } from "../clients/ens-client.js";
|
|
5
|
+
const ADDRESS_PATTERN = /^0x[a-fA-F0-9]{40}$/;
|
|
6
|
+
export class EnsService {
|
|
7
|
+
ensClientFactory;
|
|
8
|
+
constructor(ensClientFactory) {
|
|
9
|
+
this.ensClientFactory = ensClientFactory;
|
|
10
|
+
}
|
|
11
|
+
async resolveEns(name, chain = "celo") {
|
|
12
|
+
const trimmedName = name.trim();
|
|
13
|
+
const normalizedName = normalize(trimmedName);
|
|
14
|
+
const client = this.ensClientFactory.getClient();
|
|
15
|
+
const gatewayUrls = [ENS_CCIP_GATEWAY];
|
|
16
|
+
if (chain === "ethereum") {
|
|
17
|
+
const address = await getEnsAddress(client, {
|
|
18
|
+
name: normalizedName,
|
|
19
|
+
gatewayUrls,
|
|
20
|
+
});
|
|
21
|
+
if (!address) {
|
|
22
|
+
throw new Error(`ENS name "${trimmedName}" has no Ethereum address record`);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
name: trimmedName,
|
|
26
|
+
normalizedName,
|
|
27
|
+
address,
|
|
28
|
+
coinType: "60",
|
|
29
|
+
chain: "ethereum",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const celoCoinType = toCoinType(celo.id);
|
|
33
|
+
let address = await getEnsAddress(client, {
|
|
34
|
+
name: normalizedName,
|
|
35
|
+
coinType: celoCoinType,
|
|
36
|
+
gatewayUrls,
|
|
37
|
+
});
|
|
38
|
+
let coinType = celoCoinType.toString();
|
|
39
|
+
let resolvedVia = "celo";
|
|
40
|
+
if (!address) {
|
|
41
|
+
address = await getEnsAddress(client, {
|
|
42
|
+
name: normalizedName,
|
|
43
|
+
gatewayUrls,
|
|
44
|
+
});
|
|
45
|
+
coinType = "60";
|
|
46
|
+
resolvedVia = "ethereum";
|
|
47
|
+
}
|
|
48
|
+
if (!address) {
|
|
49
|
+
throw new Error(`ENS name "${trimmedName}" could not be resolved to an address`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
name: trimmedName,
|
|
53
|
+
normalizedName,
|
|
54
|
+
address,
|
|
55
|
+
coinType,
|
|
56
|
+
chain: "celo",
|
|
57
|
+
resolvedVia,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async resolveAddressOrEns(input) {
|
|
61
|
+
const trimmed = input.trim();
|
|
62
|
+
if (ADDRESS_PATTERN.test(trimmed)) {
|
|
63
|
+
return { address: trimmed };
|
|
64
|
+
}
|
|
65
|
+
const resolved = await this.resolveEns(trimmed);
|
|
66
|
+
return {
|
|
67
|
+
address: resolved.address,
|
|
68
|
+
ens: {
|
|
69
|
+
name: resolved.name,
|
|
70
|
+
normalizedName: resolved.normalizedName,
|
|
71
|
+
...(resolved.chain === "celo"
|
|
72
|
+
? { resolvedVia: resolved.resolvedVia }
|
|
73
|
+
: {}),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=ens.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ens.service.js","sourceRoot":"","sources":["../../src/services/ens.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ensNameSchema } from "../schemas/common.js";
|
|
3
|
+
import { err, ok } from "./helpers.js";
|
|
4
|
+
export const ensTools = {
|
|
5
|
+
register(server, ctx) {
|
|
6
|
+
server.registerTool("resolve_ens", {
|
|
7
|
+
title: "Resolve ENS",
|
|
8
|
+
description: "Resolve an ENS name (e.g. celina.eth) to a 0x address via Ethereum mainnet. Defaults to Celo: tries the Celo coin record first, then falls back to the standard Ethereum address.",
|
|
9
|
+
inputSchema: z.object({
|
|
10
|
+
name: ensNameSchema,
|
|
11
|
+
chain: z
|
|
12
|
+
.enum(["celo", "ethereum"])
|
|
13
|
+
.default("celo")
|
|
14
|
+
.describe("Target chain. celo: Celo coin record with Ethereum fallback. ethereum: Ethereum address only."),
|
|
15
|
+
}),
|
|
16
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
17
|
+
}, async ({ name, chain }) => {
|
|
18
|
+
try {
|
|
19
|
+
return ok(await ctx.ens.resolveEns(name, chain));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return err(error instanceof Error ? error.message : String(error));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=ens.tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ens.tools.js","sourceRoot":"","sources":["../../src/tools/ens.tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,MAAM,QAAQ,GAAe;IAClC,QAAQ,CAAC,MAAiB,EAAE,GAAe;QACzC,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;YACE,KAAK,EAAE,aAAa;YACpB,WAAW,EACT,mLAAmL;YACrL,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;gBACpB,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,CAAC;qBACL,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;qBAC1B,OAAO,CAAC,MAAM,CAAC;qBACf,QAAQ,CACP,+FAA+F,CAChG;aACJ,CAAC;YACF,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;SAC1D,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;YACxB,IAAI,CAAC;gBACH,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACnD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACrE,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;CACF,CAAC"}
|
package/build/tools/index.js
CHANGED
|
@@ -6,9 +6,11 @@ import { transactionTools } from "./transaction.tools.js";
|
|
|
6
6
|
import { mentoFxTools } from "./mento-fx.tools.js";
|
|
7
7
|
import { aaveTools } from "./aave.tools.js";
|
|
8
8
|
import { walletTools } from "./wallet.tools.js";
|
|
9
|
+
import { ensTools } from "./ens.tools.js";
|
|
9
10
|
export const toolModules = [
|
|
10
11
|
blockchainTools,
|
|
11
12
|
accountTools,
|
|
13
|
+
ensTools,
|
|
12
14
|
tokenTools,
|
|
13
15
|
walletTools,
|
|
14
16
|
transactionTools,
|
package/build/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,CAAC,MAAM,WAAW,GAAiB;IACvC,eAAe;IACf,YAAY;IACZ,QAAQ;IACR,UAAU;IACV,WAAW;IACX,gBAAgB;IAChB,YAAY;IACZ,SAAS;IACT,eAAe;IACf,SAAS;CACV,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,GAAe;IACjE,KAAK,MAAM,MAAM,IAAI,WAAW,EAAE,CAAC;QACjC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { addressOrEnsSchema, tokenSymbolSchema } from "../schemas/common.js";
|
|
3
3
|
import { err, ok } from "./helpers.js";
|
|
4
4
|
const encryptedPrivateKeySchema = z
|
|
5
5
|
.string()
|
|
@@ -9,9 +9,9 @@ export const transactionTools = {
|
|
|
9
9
|
register(server, ctx) {
|
|
10
10
|
server.registerTool("estimate_send", {
|
|
11
11
|
title: "Estimate Send",
|
|
12
|
-
description: "Estimates gas for sending CELO or an ERC-20 token on mainnet. Requires encryptedPrivateKey (hosted) or CELO_PRIVATE_KEY (local).",
|
|
12
|
+
description: "Estimates gas for sending CELO or an ERC-20 token on mainnet. Accepts a 0x address or ENS name as recipient. Requires encryptedPrivateKey (hosted) or CELO_PRIVATE_KEY (local).",
|
|
13
13
|
inputSchema: z.object({
|
|
14
|
-
to:
|
|
14
|
+
to: addressOrEnsSchema,
|
|
15
15
|
token: tokenSymbolSchema.default("CELO"),
|
|
16
16
|
amount: z.string().describe("Human-readable amount, e.g. 1.5"),
|
|
17
17
|
encryptedPrivateKey: encryptedPrivateKeySchema,
|
|
@@ -19,7 +19,9 @@ export const transactionTools = {
|
|
|
19
19
|
annotations: { readOnlyHint: true },
|
|
20
20
|
}, async ({ to, token, amount, encryptedPrivateKey }) => {
|
|
21
21
|
try {
|
|
22
|
-
|
|
22
|
+
const { address, ens } = await ctx.ens.resolveAddressOrEns(to);
|
|
23
|
+
const estimate = await ctx.transaction.estimateSend(address, token, amount, encryptedPrivateKey);
|
|
24
|
+
return ok(ens ? { ...estimate, ens } : estimate);
|
|
23
25
|
}
|
|
24
26
|
catch (error) {
|
|
25
27
|
return err(error instanceof Error ? error.message : String(error));
|
|
@@ -27,9 +29,9 @@ export const transactionTools = {
|
|
|
27
29
|
});
|
|
28
30
|
server.registerTool("send_token", {
|
|
29
31
|
title: "Send Token",
|
|
30
|
-
description: "Send CELO or an ERC-20 token on mainnet. User must encrypt their private key with the server's public key (get_wallet_encryption_public_key) before calling.",
|
|
32
|
+
description: "Send CELO or an ERC-20 token on mainnet. Accepts a 0x address or ENS name as recipient. User must encrypt their private key with the server's public key (get_wallet_encryption_public_key) before calling.",
|
|
31
33
|
inputSchema: z.object({
|
|
32
|
-
to:
|
|
34
|
+
to: addressOrEnsSchema,
|
|
33
35
|
token: tokenSymbolSchema.default("CELO"),
|
|
34
36
|
amount: z.string().describe("Human-readable amount, e.g. 0.01"),
|
|
35
37
|
encryptedPrivateKey: encryptedPrivateKeySchema,
|
|
@@ -40,7 +42,9 @@ export const transactionTools = {
|
|
|
40
42
|
},
|
|
41
43
|
}, async ({ to, token, amount, encryptedPrivateKey }) => {
|
|
42
44
|
try {
|
|
43
|
-
|
|
45
|
+
const { address, ens } = await ctx.ens.resolveAddressOrEns(to);
|
|
46
|
+
const result = await ctx.transaction.sendToken(address, token, amount, encryptedPrivateKey);
|
|
47
|
+
return ok(ens ? { ...result, ens } : result);
|
|
44
48
|
}
|
|
45
49
|
catch (error) {
|
|
46
50
|
return err(error instanceof Error ? error.message : String(error));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.tools.js","sourceRoot":"","sources":["../../src/tools/transaction.tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"transaction.tools.js","sourceRoot":"","sources":["../../src/tools/transaction.tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED