eToro MCP Server
# 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
Scored across 6 tools
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.
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.
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.
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.