Skip to main content
Glama
README.md
<div align="center">

# 🌐 PortalMCP

### Universal AI gateway to Ethereum

**One server. Every AI. The whole chain.**

Plug any Model-Context-Protocol client β€” Claude, ChatGPT, Gemini, Cursor, Windsurf, Cline, custom agents β€” into Ethereum with natural language. Check balances, swap tokens, mint NFTs, generate and deploy smart contracts.

[![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.29-blue)](https://github.com/modelcontextprotocol/typescript-sdk)
[![Ethers](https://img.shields.io/badge/ethers-v6.16-7b3fe4)](https://docs.ethers.org/v6/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178c6)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
[![Release](https://img.shields.io/badge/release-v1.2.0-brightgreen)](./CHANGELOG.md)

[**Quick start**](#-quick-start) Β· [**Setup**](#-client-setup) Β· [**Tools**](#-what-it-can-do) Β· [**Roadmap**](./ROADMAP.md) Β· [**Changelog**](./CHANGELOG.md)

</div>

---

## ✨ Why PortalMCP

Most AI-blockchain integrations lock you to one LLM or one client. PortalMCP is a spec-compliant **MCP server** β€” the same server, running locally or on your VPS, powers every MCP-capable client.

> πŸ” **Non-custodial** β€” private keys never leave your machine
> πŸ›°οΈ **Live chain context** β€” resources stream ETH balances, tx receipts and token metadata straight into your chat
> πŸ›‘οΈ **Safety-first** β€” every tool declares read/destructive/idempotent hints so clients can confirm before broadcasting
> 🧩 **Universal** β€” works over both stdio and HTTP, plays with every MCP client out there

---

## 🧭 Compatible clients

| Client | Transport | Notes |
|---|---|---|
| 🟣 **Claude Desktop** (macOS/Windows) | stdio | Drop-in config below |
| 🌐 **Claude.ai web + mobile** | HTTP | Add as *Custom Connector* (Pro/Team/Enterprise) |
| πŸ’» **Claude Code / CLI** | either | |
| 🧠 **Cursor · Windsurf · Cline · Continue · Zed AI** | stdio | Native MCP |
| πŸ’¬ **ChatGPT** (Team/Enterprise) | HTTP | MCP connector |
| πŸ› οΈ **ChatGPT Custom GPTs** | REST | Uses bundled `openapi.json` |
| ✴️ **Google Gemini / Vertex Agents** | HTTP | MCP connector |
| 🐍 **LangChain · LlamaIndex · OpenAI Agents SDK** | either | via their MCP adapters |
| πŸ€– **Any HTTP agent** | HTTP | Plain JSON-RPC + SSE on `/mcp` |

---

## 🎯 What it can do

<details open>
<summary><b>17 tools</b> β€” click to expand</summary>

#### ⚑ General
| Tool | Action |
|---|---|
| `eth_get_balance` | ETH balance of any address or the default wallet |
| `eth_call_contract` | Read-only call against any contract + ABI |
| `eth_send_transaction` | Prepare a generic unsigned transaction |

#### πŸ“œ Smart contracts
| Tool | Action |
|---|---|
| `eth_generate_contract` | Claude-authored Solidity from natural language |
| `eth_compile_contract` | solc compile β†’ bytecode + ABI |
| `eth_deploy_contract` | Prepare deployment tx for external wallet signing |
| `eth_deploy_contract_with_signer` | Deploy directly using `DEPLOYER_PRIVATE_KEY` |

#### πŸͺ™ ERC-20 tokens
| Tool | Action |
|---|---|
| `eth_create_token` | Generate ERC-20 Solidity |
| `eth_get_token_balance` | ERC-20 balance of any holder |
| `eth_transfer_token` | Signed transfer or unsigned-tx prep |

#### πŸ–ΌοΈ ERC-721 NFTs
| Tool | Action |
|---|---|
| `eth_create_nft_collection` | Generate ERC-721 Solidity |
| `eth_mint_nft` | Prepare `mint` / `safeMint` / `mintWithURI` |
| `eth_get_nft_owner` | `ownerOf()` lookup |

#### 🏦 DeFi
| Tool | Action |
|---|---|
| `eth_create_staking_contract` | Generate staking Solidity |
| `eth_stake_tokens` | Prepare approve + stake txs |
| `eth_swap_tokens` | Universal Uniswap V3 swap (any ERC-20 pair) |
| `eth_swap_eth_to_usdt` | Convenience alias of the above |

</details>

<details>
<summary><b>4 resources</b> (live chain data as context)</summary>

| URI | Returns |
|---|---|
| `eth://wallet` | Configured signer address, network, ETH balance |
| `eth://balance/{address}` | Live ETH balance for any address |
| `eth://tx/{hash}` | Transaction + receipt (status, gas, block, logs, explorer URL) |
| `eth://token/{address}` | ERC-20 metadata (name, symbol, decimals, total supply) |

</details>

<details>
<summary><b>2 prompts</b> (slash commands)</summary>

- `/swap_tokens` β€” guided token-swap flow
- `/deploy_erc20` β€” generate β†’ compile β†’ deploy end-to-end

</details>

---

## πŸš€ Quick start

```bash
git clone https://github.com/PortalFnd/PortalMCP.git
cd PortalMCP/portalmcp
npm install
cp .env.example .env
# fill in .env β€” ANTHROPIC_API_KEY, DEPLOYER_PRIVATE_KEY,
# and ETHEREUM_RPC_URL (or a real ALCHEMY_API_KEY)
npm run build
npm run smoke          # βœ“ 17 tools / 1 resource / 3 templates / 2 prompts
npm start              # stdio (Claude Desktop, Cursor, …)
# or
npm run start:http     # Streamable HTTP on http://0.0.0.0:3333/mcp
```

---

## πŸ”Œ Client setup

<details open>
<summary><b>🟣 Claude Desktop</b> (stdio)</summary>

<br>

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "portalmcp": {
      "command": "node",
      "args": ["/absolute/path/to/PortalMCP/portalmcp/dist/index.js"],
      "env": {
        "ETHEREUM_NETWORK": "mainnet",
        "ETHEREUM_RPC_URL": "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
        "DEPLOYER_PRIVATE_KEY": "0x...",
        "ANTHROPIC_API_KEY": "sk-ant-..."
      }
    }
  }
}
```

Restart Claude Desktop. 17 tools, `eth://` resources, and two slash commands appear automatically.

</details>

<details>
<summary><b>🌐 Claude.ai web / mobile</b> (Streamable HTTP)</summary>

<br>

1. Host the HTTP server with a public HTTPS URL (Caddy / Cloudflare Tunnel / Nginx).
2. Set `MCP_HTTP_TOKEN=<long-random-string>` so only you can call it.
3. In Claude.ai β†’ **Settings β†’ Connectors β†’ Add Custom Connector**:
   - **URL:** `https://your-host.example.com/mcp`
   - **Auth:** `Authorization: Bearer <MCP_HTTP_TOKEN>`
4. Works on both web and the mobile app.

</details>

<details>
<summary><b>🧠 Cursor / Windsurf / Cline / Continue</b></summary>

<br>

All speak MCP natively. Add an entry to their MCP config pointing at:
```
node /absolute/path/to/PortalMCP/portalmcp/dist/index.js
```
(Same stdio command as Claude Desktop.)

</details>

<details>
<summary><b>πŸ’¬ ChatGPT, Gemini, custom agents</b></summary>

<br>

**Preferred β€” MCP connector** (ChatGPT Team/Enterprise, Gemini/Vertex Agents):
Point at `https://your-host/mcp`, optionally with a Bearer token.

**Legacy REST** (ChatGPT Custom GPT Actions or any HTTP agent):
```bash
npm run start:api
# OpenAPI spec: http://localhost:3001/openapi.json
```

</details>

---

## πŸ’¬ Example conversations

> **Deploy a token from scratch**
>
> *"Deploy an ERC-20 called PortalToken (PRTL) with initial supply 1,000,000."*
>
> β†’ `eth_generate_contract` β†’ shows code β†’ `eth_compile_contract` β†’ `eth_deploy_contract_with_signer` β†’ returns the contract address + Etherscan link.

> **Universal swap**
>
> *"Swap 0.01 ETH for USDC."*
>
> β†’ `eth_swap_tokens { tokenIn:"ETH", tokenOut:"USDC", amount:"0.01" }` β€” approves (if needed) and executes via Uniswap V3.

> **Live on-chain context**
>
> *"What's the balance of `vitalik.eth`?"*
>
> β†’ client attaches the `eth://balance/0xd8dA…` resource straight into the conversation.

---

## βš™οΈ Configuration

All via env vars (`.env` file or host env). Full list in `.env.example`.

| Var | Required | Purpose |
|---|:---:|---|
| `ETHEREUM_NETWORK` | – | `mainnet`, `sepolia`, `arbitrum`, `optimism`, `base`, `polygon`, … (default `mainnet`) |
| `ETHEREUM_RPC_URL` | ⭐ | Full JSON-RPC URL β€” overrides Infura/Alchemy key setup |
| `ALCHEMY_API_KEY` | alt | Key only β€” PortalMCP builds the modern `g.alchemy.com` URL |
| `INFURA_API_KEY` | alt | Infura project ID |
| `DEPLOYER_PRIVATE_KEY` | writes | `0x`-prefixed hex β€” enables signer-backed tools |
| `ANTHROPIC_API_KEY` | generate | For `eth_generate_contract` |
| `ANTHROPIC_MODEL` | – | Override default `claude-sonnet-4-5-20250929` |
| `MCP_HTTP_PORT` | – | Default `3333` |
| `MCP_HTTP_HOST` | – | Default `0.0.0.0` |
| `MCP_HTTP_TOKEN` | πŸ›‘οΈ | Bearer token for the HTTP transport |
| `MCP_HTTP_CORS_ORIGIN` | – | Default `*` |

> πŸ’‘ **Placeholder detection** β€” any env value starting with `your_`, `changeme`, `xxx`, `placeholder`, `<…>` is treated as unset. Stops silent misconfigurations dead.

---

## 🌍 Networks supported

<table>
<tr><td><b>L1</b></td><td>Ethereum mainnet Β· Sepolia Β· Goerli Β· Holesky</td></tr>
<tr><td><b>L2</b></td><td>Arbitrum Β· Optimism Β· Base Β· Polygon <em>(+ every testnet)</em></td></tr>
<tr><td><b>Custom</b></td><td>Any EVM chain β€” BSC, Avalanche, Linea, zkSync, … β€” via <code>ETHEREUM_RPC_URL</code></td></tr>
</table>

---

## πŸ›‘οΈ Security

- 🚫 **Never commit `.env`** β€” already in `.gitignore`.
- πŸ”‘ **`DEPLOYER_PRIVATE_KEY` is a loaded gun.** Use a dedicated agent wallet with only funds you can lose.
- πŸ›°οΈ **Always set `MCP_HTTP_TOKEN`** when exposing HTTP beyond localhost, and put TLS (Caddy/Cloudflare) in front.
- πŸ§ͺ **Testnet first** β€” use `sepolia` for development, mainnet only after you've verified the flow.
- 🏷️ **Tool annotations** let clients prompt before destructive txs β€” don't auto-approve them.
- πŸ‘€ **Review generated Solidity** β€” `eth_generate_contract` is a starting point, not an audit.

---

## πŸ§‘β€πŸ’» Development

```bash
npm install
npm run dev          # stdio, ts-node hot-reload
npm run dev:http     # HTTP, ts-node
npm run build        # tsc β†’ dist/
npm run smoke        # assert MCP surface is registered
npm test             # Jest
```

| Script | Purpose |
|---|---|
| `npm start` | stdio MCP server (prod) |
| `npm run start:http` | Streamable HTTP MCP server (prod) |
| `npm run start:api` | Legacy REST for ChatGPT Actions / HTTP clients |
| `npm run smoke` | Registration smoke test β€” great for CI |

### Repo layout

```
portalmcp/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts              # stdio entrypoint
β”‚   β”œβ”€β”€ mcp-http.ts           # Streamable HTTP entrypoint
β”‚   β”œβ”€β”€ server-factory.ts     # createPortalServer() β€” shared wiring
β”‚   β”œβ”€β”€ smoke-test.ts         # CI registration check
β”‚   β”œβ”€β”€ tools/                # general Β· contracts Β· defi Β· tokens Β· nfts
β”‚   β”œβ”€β”€ blockchain/           # EthereumService Β· CompilerService
β”‚   β”œβ”€β”€ claude/               # ContractGenerator (Anthropic SDK)
β”‚   β”œβ”€β”€ contracts/            # Solidity templates
β”‚   └── adapters/             # Legacy REST / LangChain / OpenAI adapters
β”œβ”€β”€ dist/                     # tsc output
β”œβ”€β”€ .env.example
└── package.json
```

---

## πŸ—οΈ Architecture

```
          stdio                                 Streamable HTTP (SSE)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Claude Desktop     β”‚                  β”‚   Claude.ai web + mobile    β”‚
β”‚  Cursor Β· Windsurf  β”‚                  β”‚   ChatGPT Β· Gemini          β”‚
β”‚  Cline Β· Continue   β”‚                  β”‚   Custom agents             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚                                             β”‚
          β”‚        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”‚
          └───────▢│   PortalMCP server   β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚  (server-factory.ts) β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β–Ό                      β–Ό                      β–Ό
  EthereumService         Uniswap V3             Anthropic
  (ethers v6 +        (eth_swap_tokens)      (eth_generate_contract)
   Alchemy/Infura/
   custom RPC)
```

---

## πŸ—ΊοΈ Roadmap highlights

<table>
<tr>
  <td valign="top"><b>Shipped βœ…</b><br><br>
    MCP SDK 1.29 β€” stdio + HTTP<br>
    17 tools Β· 1 resource Β· 3 templates Β· 2 prompts<br>
    Tool annotations + outputSchema<br>
    Universal Uniswap V3 swap<br>
    L1 + L2 + testnets<br>
    Anthropic SDK 0.90 Β· Claude Sonnet 4.5<br>
    Smoke test for CI
  </td>
  <td valign="top"><b>Next πŸ”­</b><br><br>
    Elicitation (confirm destructive txs)<br>
    ENS / gas helpers<br>
    Tx simulation with revert decoding<br>
    Multi-DEX aggregation (1inch, 0x)<br>
    Aave / Compound read positions<br>
    Ledger hardware signer<br>
    Gnosis Safe + ERC-4337<br>
    Docker + Python SDK
  </td>
</tr>
</table>

Full plan in [ROADMAP.md](./ROADMAP.md).

---

## 🀝 Contributing

PRs welcome! Priority areas: more `outputSchema` coverage, additional tools, Docker packaging, Python client, test coverage. Open an issue first for non-trivial changes.

---

<div align="center">

**[⭐ Star this repo](https://github.com/PortalFnd/PortalMCP)** Β· **[πŸ› Report an issue](https://github.com/PortalFnd/PortalMCP/issues)** Β· **[πŸ“œ MIT License](./LICENSE)**

Built with πŸ’œ by the **Portal Foundation**

</div>