Skip to main content
Glama
README.md
# code-mode-alchemy-mcp

An MCP (Model Context Protocol) server that gives Claude access to Alchemy's blockchain APIs. Instead of defining 88+ individual tools, it loads all Alchemy API specs at startup and exposes just two tools — `search` and `execute` — keeping context usage minimal (~1,000 tokens vs. thousands).

Inspired by [Cloudflare's Code Mode MCP pattern](https://blog.cloudflare.com/code-mode-mcp/).

## How It Works

At startup, the server fetches 10 Alchemy API specifications (6 REST OpenAPI + 4 JSON-RPC OpenRPC) and indexes all their endpoints. Claude can then:

1. **Search** — find relevant endpoints by keyword
2. **Execute** — run JavaScript code in a sandboxed environment against those endpoints

```
Claude → search("NFT owner") → finds getNFTsForOwner endpoint
Claude → execute(code)       → runs against Alchemy API, returns data
```

### Loaded Specifications

**REST (OpenAPI):**
- `nft` — NFT operations (mint, transfer, ownership queries)
- `portfolio` — Wallet portfolio data
- `prices` — Token pricing information
- `notify` — Webhook notifications
- `transactions` — Transaction history and details
- `accounts` — Account management

**JSON-RPC (OpenRPC):**
- `transfers` — Token transfer history (`alchemy_getAssetTransfers`)
- `token` — Token metadata and balances (`alchemy_getTokenBalances`, etc.)
- `transaction-simulation` — Simulate transactions before sending
- `bundler` — ERC-4337 bundler operations

## Prerequisites

- Node.js 18+
- An [Alchemy API key](https://www.alchemy.com/)

## Installation

```bash
git clone https://github.com/your-username/code-mode-alchemy-mcp
cd code-mode-alchemy-mcp
npm install
npm run build
```

## Configuration

Copy the example environment file:

```bash
cp .env.example .env
```

Set your values in `.env`:

```
ALCHEMY_API_KEY=your_key_here
ALCHEMY_NETWORK=eth-mainnet
```

### Supported Networks

Any network string Alchemy supports in their URL format, e.g.:
- `eth-mainnet`
- `eth-sepolia`
- `polygon-mainnet`
- `arb-mainnet`
- `opt-mainnet`
- `base-mainnet`

## Add to Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "alchemy": {
      "command": "node",
      "args": ["/absolute/path/to/code-mode-alchemy-mcp/dist/index.js"],
      "env": {
        "ALCHEMY_API_KEY": "your_key_here",
        "ALCHEMY_NETWORK": "eth-mainnet"
      }
    }
  }
}
```

Restart Claude Desktop. You should see the Alchemy MCP server connected.

## Tools

### `search(query, limit?)`

Searches across all loaded endpoint metadata (summaries, descriptions, paths, tags) and returns the most relevant endpoints.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | required | Keywords to search for |
| `limit` | number | 8 | Max results to return (max 20) |

**Example queries:**
- `"NFT owner"` — find endpoints for fetching NFTs by owner
- `"token balance"` — find token balance endpoints
- `"asset transfers"` — find transfer history endpoints
- `"simulate transaction"` — find simulation endpoints

### `execute(code)`

Runs JavaScript code in a sandboxed Node.js VM with access to the Alchemy API. Execution is isolated with a 30-second timeout.

**Available in the sandbox:**

| Name | Type | Description |
|------|------|-------------|
| `request(url, params?, options?)` | function | Simplified API caller. Replaces `{apiKey}` and `{network}` in the URL automatically. |
| `fetch` | function | Raw Fetch API for custom requests |
| `ALCHEMY_API_KEY` | string | Your API key |
| `ALCHEMY_NETWORK` | string | Your configured network |
| `console.log` | function | Output captured and returned |
| `console.error` | function | Errors captured and returned |

**REST example:**

```javascript
const data = await request(
  "https://{network}.g.alchemy.com/nft/v3/{apiKey}/getNFTsForOwner",
  { owner: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", withMetadata: true }
);
return data;
```

**JSON-RPC example:**

```javascript
const data = await request(
  "https://{network}.g.alchemy.com/v2/{apiKey}",
  {
    jsonrpc: "2.0",
    method: "alchemy_getTokenBalances",
    params: ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"],
    id: 1
  },
  { method: "POST" }
);
return data;
```

## Usage Examples

### NFTs for a wallet

> "Show me all NFTs owned by 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

Claude will:
1. Call `search("NFT owner")` → finds `getNFTsForOwner`
2. Call `execute(...)` → fetches and returns NFT data

### Token balances

> "What ERC-20 tokens does this address hold: 0x..."

Claude will:
1. Call `search("token balances")` → finds `alchemy_getTokenBalances`
2. Call `execute(...)` → returns token list with balances

### Transaction history

> "Show recent transactions for vitalik.eth"

Claude will:
1. Call `search("asset transfers")` → finds `alchemy_getAssetTransfers`
2. Call `execute(...)` → returns transfer history

### Simulate a transaction

> "What would happen if I sent 1 ETH from 0x... to 0x...?"

Claude will:
1. Call `search("simulate transaction")` → finds simulation endpoint
2. Call `execute(...)` → returns simulation result

## Development

```bash
# Run in development mode (no build step needed)
npm run dev

# Build for production
npm run build

# Run the compiled server
npm start
```

## Project Structure

```
code-mode-alchemy-mcp/
├── src/
│   └── index.ts        # Everything: spec loading, search, sandbox, MCP server
├── dist/
│   ├── index.js        # Compiled output (run this)
│   └── index.d.ts      # TypeScript declarations
├── .env.example        # Environment variable template
├── package.json
└── tsconfig.json
```

## Architecture

```
Claude Desktop
     │
     │  MCP (stdio)
     ▼
MCP Server
     │
     ├── Startup: fetch 10 specs → index all endpoints
     │
     ├── search(query)
     │     └── score & rank endpoints by keyword match
     │
     └── execute(code)
           └── vm.runInNewContext() with 30s timeout
                 └── request() helper → Alchemy REST/RPC APIs
```

**Why two tools instead of 88+?**

Defining every Alchemy endpoint as a separate MCP tool would consume thousands of tokens just in tool definitions, before Claude even starts reasoning. This pattern loads specs once, searches them dynamically, and executes code — using ~1,000 tokens of tool definitions regardless of how many endpoints Alchemy adds.

## Security Notes

- Code in `execute()` runs in a Node.js `vm` sandbox with no access to the filesystem, environment, or `require`
- The API key is injected by the sandbox via placeholder replacement — it is never returned in search results or exposed to user-visible output
- Execution is limited to 30 seconds to prevent runaway code

## References

- [Cloudflare Code Mode MCP](https://blog.cloudflare.com/code-mode-mcp/) — the pattern this is based on
- [Cloudflare Agents API Reference](https://developers.cloudflare.com/agents/api-reference/codemode/)
- [Alchemy OpenAPI Specs](https://github.com/alchemyplatform/docs-openapi-specs)
- [Model Context Protocol](https://modelcontextprotocol.io/)
- [Alchemy API Docs](https://docs.alchemy.com/)

## License

MIT

TDQS

A4.1/5.0

Scored across 2 tools

Disambiguation5/5

Search and execute have clearly distinct roles: search discovers API endpoints, while execute runs arbitrary JavaScript against them. There is no overlap in purpose, and the descriptions explicitly guide sequential use.

Naming Consistency5/5

Both tool names are concise, lowercase imperative verbs following the same bare-verb convention. No inconsistent casing or mixed noun/verb patterns.

Tool Count4/5

Two tools is slightly below the typical 3-15 range, but both are essential and earn their place: one for discovery and one for universal execution. The set is minimal rather than redundant.

Completeness4/5

Search plus execute covers endpoint discovery and arbitrary REST/JSON-RPC calls, which is nearly complete for a code-mode API gateway. Minor gaps remain, such as no structured endpoint-schema lookup or built-in pagination helpers, but agents can work around these.