@andrewkimjoseph/celina
v0.2.18
Published
Celina — MCP server for Celo mainnet. Balances, transfers, and chain reads for LLM agents.
Maintainers
Readme
Install
npm i @andrewkimjoseph/celinaQuick start
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. Install the package, then add it to your MCP config (see Cursor / Claude Desktop config).
From npm:
npm i @andrewkimjoseph/celinaFrom source (development):
npm install
npm run build
npm startDeploy to Render
This project includes a Render Blueprint for one-click deployment as a public Streamable HTTP MCP server.
1. Generate an RSA key pair
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private.pem -out public.pem2. Deploy
- Push this repo to GitHub
- Render Dashboard → New → Blueprint → connect the repo
- Set
WALLET_ENCRYPTION_PRIVATE_KEYin the Render Environment tab (paste contents ofprivate.pem) - (Optional) Add a custom domain in Render and set
ALLOWED_HOSTSto that hostname (comma-separated if multiple) - Your MCP endpoint will be at your Render URL +
/mcp
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.
Cursor / Claude Desktop config
Cursor — remote (recommended)
{
"mcpServers": {
"celina": {
"type": "streamable-http",
"url": "https://mcp.celina.andrewkimjoseph.com/mcp"
}
}
}Custom domains must be listed in
ALLOWED_HOSTSon the server. Render's default hostname (RENDER_EXTERNAL_HOSTNAME) is always allowed automatically.
Claude Desktop — remote (free plan)
Claude Desktop's claude_desktop_config.json only supports local stdio servers — it does not accept "type": "streamable-http" with a "url". Free-plan users should bridge the hosted server with mcp-remote. Install it once (npm i -g mcp-remote), then:
{
"mcpServers": {
"celina": {
"command": "mcp-remote",
"args": [
"https://mcp.celina.andrewkimjoseph.com/mcp",
"--transport",
"http-only"
]
}
}
}Fully quit and relaunch Claude Desktop after editing the config (closing the window is not enough).
Pro / Max / Team / Enterprise: you can skip
mcp-remoteand addhttps://mcp.celina.andrewkimjoseph.com/mcpunder Settings → Integrations instead.
Local stdio (npm)
After npm i @andrewkimjoseph/celina, point your MCP client at the installed entry file (use an absolute path):
{
"mcpServers": {
"celina": {
"command": "node",
"args": ["/absolute/path/to/node_modules/@andrewkimjoseph/celina/build/index.js"]
}
}
}Local stdio (from source)
{
"mcpServers": {
"celina": {
"command": "node",
"args": ["/absolute/path/to/celina/build/index.js"]
}
}
}For local write tools, add a funded mainnet wallet:
"env": {
"CELO_PRIVATE_KEY": "0x..."
}Never commit private keys. Use env vars only.
Local LLM integration
Celina is an MCP tool server. A local LLM stack needs an MCP client that can connect to Celina and pass tool definitions to a model that supports function / tool calling.
Read-only tools (balances, blocks, GoodDollar status, etc.) work out of the box. For local write tools (send_token, estimate_send, execute_mento_fx), set CELO_PRIVATE_KEY in the MCP server env block (stdio) or use the hosted encryption flow (HTTP).
LM Studio (0.3.17+)
LM Studio can host MCP servers directly via mcp.json (same format as Cursor).
- Open LM Studio → Program → Install → Edit mcp.json
- Add Celina under
mcpServers - In Server Settings, enable Allow calling servers from mcp.json
- Chat with a tool-capable model (e.g. Qwen 2.5, Llama 3.1+)
{
"mcpServers": {
"celina": {
"command": "node",
"args": ["/absolute/path/to/node_modules/@andrewkimjoseph/celina/build/index.js"],
"env": {
"CELO_PRIVATE_KEY": "0x..."
}
}
}
}Omit CELO_PRIVATE_KEY if you only need read-only chain queries.
Open WebUI + Ollama
Open WebUI supports streamable HTTP MCP natively (not stdio).
Hosted Celina (easiest): Admin Settings → External Tools → Add Server → Type: MCP (Streamable HTTP) → URL:
https://mcp.celina.andrewkimjoseph.com/mcpLocal HTTP server: run Celina in HTTP mode, then point Open WebUI at it:
npm run build
npm run start:httpAdd an External Tool with Type MCP (Streamable HTTP) and URL http://localhost:10000/mcp.
For write tools over HTTP, set WALLET_ENCRYPTION_PRIVATE_KEY in .env (see Deploy to Render) and use the encrypt-key flow.
If Open WebUI runs in Docker, use
http://host.docker.internal:10000/mcpinstead oflocalhost.
Continue (VS Code)
Continue works with local models (Ollama, LM Studio, etc.) in agent mode.
Create .continue/mcpServers/celina.yaml in your workspace:
name: Celina
version: 0.0.1
schema: v1
mcpServers:
- name: celina
type: stdio
command: node
args:
- "/absolute/path/to/node_modules/@andrewkimjoseph/celina/build/index.js"
env:
CELO_PRIVATE_KEY: "0x..."Alternatively, copy the local stdio JSON from the Cursor section into .continue/mcpServers/mcp.json — Continue picks up Claude/Cursor-style configs automatically.
Test without an LLM
Use MCP Inspector to call Celina tools directly over stdio:
npm run build
npm run inspectTips
- Use models with reliable tool-calling support; small or older models may skip tools or call them incorrectly.
- Start with read-only prompts, e.g. "What's the USDm balance of 0x…?" or "Is this wallet GoodDollar whitelisted?"
- Keep private keys in env vars only — never commit them to config files in git.
Write tools (hosted mode)
Write tools (send_token, estimate_send, execute_mento_fx) accept an RSA-encrypted private key per request — never plaintext.
Flow
- Fetch the server's public key:
- MCP tool:
get_wallet_encryption_public_key - HTTP:
GET https://mcp.celina.andrewkimjoseph.com/public-key
- MCP tool:
- Encrypt your key locally:
npm run encrypt-key -- --url https://mcp.celina.andrewkimjoseph.com --key 0xYOUR_PRIVATE_KEY- Give the agent the encrypted blob (base64 output) along with your transaction details
- The agent calls
send_tokenwith theencryptedPrivateKeyparameter
The server decrypts the key ephemerally to sign the transaction — it is not stored.
Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| CELO_RPC_URL_MAINNET | Forno public RPC | Override mainnet RPC |
| CELO_PRIVATE_KEY | — | Local stdio write tools only |
| WALLET_ENCRYPTION_PRIVATE_KEY | — | RSA private key PEM for HTTP write tools |
| ALLOWED_HOSTS | — | Comma-separated custom hostnames (e.g. mcp.celina.andrewkimjoseph.com) |
| PORT | 10000 | HTTP server port (set by Render) |
Copy .env.example to .env for local development.
Known tokens
All supported tokens live in a single registry (src/config/chains.ts):
| Category | Symbols |
|----------|---------|
| Native | CELO |
| Mento stablecoins | USDm, EURm, BRLm, XOFm, KESm, PHPm, COPm, GBPm, CADm, AUDm, ZARm, GHSm, NGNm, JPYm, CHFm |
| Bridged / third-party | USDT, USDC, vEUR, vGBP, vCHF, USDM, USDA, EURA, USDGLO, BRLA, COPM |
| GoodDollar | GoodDollar, G$ (0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A) |
Token symbols are resolved case-insensitively. Legacy aliases cUSD and cEUR map to USDm and EURm. You can also pass any ERC-20 contract address directly.
get_celo_balances— check specific tokens (defaults toCELO+USDm)get_stablecoin_balances— scan all registry stablecoins in one call (omits zero balances by default)
Tools (v0.2)
| Tool | Type | Description |
|------|------|-------------|
| get_network_status | read | Mainnet chain ID, block, gas price |
| get_block | read | Block by number/hash/latest |
| get_latest_blocks | read | Recent blocks |
| get_transaction | read | Tx + receipt |
| get_account | read | CELO balance, nonce |
| get_celo_balances | read | CELO + ERC-20 balances (default: CELO + USDm) |
| get_stablecoin_balances | read | All registry stablecoins including GoodDollar |
| get_token_info | read | Token metadata |
| get_wallet_encryption_public_key | read | RSA public key for encrypting private keys |
| estimate_send | read* | Gas estimate (needs encrypted or env key) |
| send_token | write | Send CELO or ERC-20 |
| get_mento_fx_quote | read | Mento FX expected output (no wallet) |
| estimate_mento_fx | read | Mento FX gas estimate (*needs encrypted or env key) |
| execute_mento_fx | write | Execute Mento FX conversion |
| supply_aave_usdt | write | Supply USDT to Aave V3 on Celo |
| withdraw_aave_usdt | write | Withdraw USDT from Aave V3 on Celo |
Adding a new tool
- Create
src/tools/my-feature.tools.tsimplementingToolModule:
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { AppContext } from "../context/app-context.js";
import type { ToolModule } from "./types.js";
export const myFeatureTools: ToolModule = {
register(server, ctx) {
server.registerTool("my_tool", { /* ... */ }, async (args) => { /* ... */ });
},
};- Append to
toolModulesinsrc/tools/index.ts. - Add domain logic in
src/services/if needed. - Rebuild:
npm run build.
No changes to src/index.ts or server bootstrap required.
Roadmap
- [x] Mento FX routing (
get_mento_fx_quote,estimate_mento_fx,execute_mento_fx) - [x] Aave lending tools (
supply_aave_usdt,withdraw_aave_usdt) - [ ] Self proof verification (
ai.self.xyz) - [ ] Self Agent ID check
Development
npm run dev # watch TypeScript
npm run inspect # MCP Inspector UI (stdio)
npm run start:http # HTTP server on PORT (default 10000)
npm run encrypt-key # encrypt a private key for write toolsLicense
MIT
