Skip to main content
Glama
ranjanvipul88

eToro MCP Server

README.md
# eToro Read-Only MCP Server

A local **read-only** [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server
that wraps eToro's [Agent Portfolios](https://www.etoro.com/news-and-analysis/etoro-updates/agent-portfolios-let-your-ai-agent-trade-for-you/)
public API (`https://public-api.etoro.com/api/v1`).

It lets an MCP-compatible AI client (Claude Desktop, Claude Code, Cursor, etc.)
read your real eToro portfolio — total value, P&L, holdings breakdown,
positions, and instrument lookups — directly in chat.

## Why "read-only" matters

eToro's official agent-portfolios skill bundles **read** endpoints (portfolio
info, instrument search) together with **write/trade** endpoints (create
agent-portfolio, open position, close position). This project deliberately
implements **only the read endpoints**. There is no code path, tool, or
parameter anywhere in this repo that can place, modify, close, or rebalance
a trade.

See [`src/etoroClient.js`](src/etoroClient.js) — it has a hardcoded allowlist
of GET-only paths and the underlying `fetch` call's method is hardcoded to
`"GET"`.

## Tools exposed

| Tool | Description |
|---|---|
| `etoro_check_connection` | Verifies your API credentials and lists any agent-portfolios on the account |
| `etoro_portfolio_summary` | Total portfolio value, total invested, unrealized P&L, available cash |
| `etoro_holdings` | Open positions grouped by instrument: invested, current value, P&L, P&L%, portfolio weight% |
| `etoro_raw_pnl` | Raw `/trading/info/real/pnl` payload — every open position, pending order, account-level PnL |
| `etoro_search_instrument` | Resolve a trading symbol (e.g. `AAPL`) to an eToro instrument ID |
| `etoro_instrument_details` | Display metadata (name, type, exchange, etc.) for one or more instrument IDs |

## Prerequisites

- **Node.js >= 18** (tested on Node 24). Download from [nodejs.org](https://nodejs.org/).
- An **eToro account** with the [Agent Portfolios](https://www.etoro.com/settings/trade)
  feature available (beta).
- An MCP-compatible client: [Claude Desktop](https://claude.ai/download),
  [Claude Code](https://docs.claude.com/en/docs/claude-code), or Cursor.

## 1. Get your eToro API credentials

1. Go to **eToro → Settings → Trade** (https://www.etoro.com/settings/trade).
2. Generate an API key. **Use a key WITHOUT `real:write` permission** — this
   server only ever issues GET requests and doesn't need (or use) write access.
3. You'll get:
   - A **shared application key** (`x-api-key`) — documented publicly in
     eToro's [agent-portfolios SKILL.md](https://www.etoro.com/wp-content/uploads/agent-portfolios/SKILL.md).
   - Your **private per-user key** (`x-user-key`) — keep this secret.

## 2. Clone and install

```bash
git clone https://github.com/ranjanvipul88/eToro-mcp.git
cd eToro-mcp
npm install
```

## 3. Configure credentials

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

Edit `.env`:

```env
ETORO_API_KEY=<shared app key from eToro's agent-portfolios SKILL.md>
ETORO_USER_KEY=<your private per-user key>
```

`.env` is gitignored — **never commit it**.

## 4. Test the connection (optional but recommended)

```bash
npm run test:connection
```

This calls the eToro API directly (no MCP) and prints:
- your agent-portfolio list (and whether your key has write permission)
- an aggregated holdings breakdown with current value, invested amount, and P&L

If this works, the MCP server will work too.

## 5. Set up with Claude Code

A project-scoped `.mcp.json` is included. From the project directory, open
Claude Code and run:

```
/mcp
```

Approve the `etoro-readonly` server when prompted. Then ask things like:

> What's my eToro portfolio summary?
> Show me my eToro holdings breakdown.

Or register it manually from any directory:

```bash
claude mcp add etoro-readonly node /absolute/path/to/eToro-mcp/src/index.js
```

## 6. Set up with Claude Desktop

Edit your Claude Desktop config file:

- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`

Add an entry under `mcpServers` (use the **absolute path** to `src/index.js`):

```json
{
  "mcpServers": {
    "etoro-readonly": {
      "command": "node",
      "args": ["/absolute/path/to/eToro-mcp/src/index.js"]
    }
  }
}
```

> **Windows path tip**: use double backslashes, e.g.
> `"D:\\eToro-mcp\\src\\index.js"`.

Fully **quit and restart** Claude Desktop (MCP servers are only loaded on
startup — closing the window isn't enough). Start a new chat and ask:

> Check my eToro connection.
> What's my eToro portfolio summary?

Approve the `etoro-readonly` tools when prompted.

### Troubleshooting: `fetch failed` in Claude Desktop

If the server connects in Claude Code / `npm run test:connection` but
Claude Desktop reports `fetch failed`, it's usually a TLS/CA issue specific
to how the Desktop app spawns Node (not an issue with the API or this code).
Add an `env` block to the config entry:

```json
{
  "mcpServers": {
    "etoro-readonly": {
      "command": "node",
      "args": ["/absolute/path/to/eToro-mcp/src/index.js"],
      "env": {
        "NODE_OPTIONS": "--use-system-ca",
        "NODE_TLS_REJECT_UNAUTHORIZED": "0"
      }
    }
  }
}
```

> Note: `NODE_TLS_REJECT_UNAUTHORIZED=0` disables TLS certificate validation
> for this process. Only use this if you understand the trade-off (it's a
> common workaround on machines with corporate antivirus/TLS-inspection
> proxies that intercept HTTPS).

## Project structure

```
.
├── src/
│   ├── index.js           # MCP server (stdio transport), registers tools
│   ├── etoroClient.js      # eToro API client — GET-only allowlist
│   └── test-connection.js  # standalone script to test API credentials
├── .env.example             # credential template (copy to .env)
├── .mcp.json                 # Claude Code project-scoped MCP registration
├── package.json
└── README.md
```

## Notes & limitations

- Requests are throttled to ~1 every 3.1 seconds, per eToro's documented
  20 requests/minute rate limit.
- `instrumentIds` query parameters are sent with **literal commas**
  (`instrumentIds=1,2,3`) — eToro's API returns HTTP 500 if commas are
  URL-encoded as `%2C`.
- This project is unaffiliated with eToro. Use at your own risk and review
  [`src/etoroClient.js`](src/etoroClient.js) before connecting your own
  account.

## License

MIT

TDQS

A4/5.0

Scored across 6 tools

Disambiguation4/5

Most tools are clearly distinct: connection check, portfolio summary, holdings, raw PnL, instrument search, and instrument details. However, etoro_holdings and etoro_raw_pnl both report open positions, and could be confused if an agent doesn't read descriptions carefully.

Naming Consistency3/5

All tools share the etoro_ prefix and use snake_case, but there is a mix of verb-led names (check_connection, search_instrument) and noun-led names (portfolio_summary, holdings, raw_pnl, instrument_details). The pattern is not uniform verb_noun, but it remains readable and predictable.

Tool Count5/5

With 6 tools, the set is well-scoped for a read-only eToro integration. Each tool covers a distinct aspect of account viewing and instrument lookup, avoiding bloat while providing essential functionality.

Completeness4/5

The toolset provides solid read-only coverage: connection verification, portfolio overview, detailed positions, raw P&L, instrument search, and instrument details. Minor gaps include no historical transactions or watchlist functionality, but core usage is well supported.

Maintenance

ActivityInactive
ResponsivenessNo issues