---
name: etoro-public-api-readonly
description: Read eToro account and market data through the read-only eToro MCP server (https://mcp-readonly.public-api.etoro.com). Use when the user asks about their eToro portfolio, positions, orders, balances, trading history, watchlists or profile, about instruments and prices, or about public investors - or wants to run any other READ route of the eToro Public API. This server cannot place, close or edit trades, move money or change anything; for those the user must use the eToro app or website.
version: 1.0.0
---

# eToro Public API - read-only MCP

This skill drives the **read-only** eToro MCP server at `https://mcp-readonly.public-api.etoro.com`. It is a separate server from the
full eToro Public API MCP: it authenticates with OAuth only, exposes eleven read tools, and has **no write tool
at all** - nothing here can place an order, close a position, transfer money, edit a watchlist or change
copy-trading. Never attempt those; when the user asks for one, say it is not available through this connection
and point them at the eToro app or website.

## Keeping this skill current

The server reports its skill version as `skillVersion` on every `get-all-routes` response. This copy is
version `1.0.0`. When the server's version is newer, tell the user to re-download the skill from
`https://mcp-readonly.public-api.etoro.com/skill`.

## Connecting

Add the server URL to your MCP client; the client runs the eToro sign-in (OAuth) itself and attaches the
resulting bearer token to every request. There is nothing to paste, no API key, no header to configure.

```json
{
  "mcpServers": {
    "etoro-readonly": {
      "url": "https://mcp-readonly.public-api.etoro.com/"
    }
  }
}
```

Clients that cannot speak OAuth over streamable HTTP directly can bridge with `mcp-remote`:

```json
{
  "mcpServers": {
    "etoro-readonly": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp-readonly.public-api.etoro.com/"]
    }
  }
}
```

An HTTP `401` with a `WWW-Authenticate` header, or a tool result with `statusCode` 401, means the connection
has no valid token: the client must (re-)authorize. Never retry the same token.

## The tools

| Tool | What it answers |
|------|-----------------|
| `get-my-portfolio-summary` | The connected user's Trading portfolio in one call: totals, holdings, copied traders, pending orders. `account` real/demo. |
| `get-my-positions-and-orders` | Open positions and pending orders (direct and copied), with P&L, SL/TP, status. `account` real/demo. |
| `get-my-balances` | Every account the user holds (Trading incl. sub-accounts, Cash, Crypto, Options, MoneyFarm, Spaceship, Plaee) with a `fieldGuide` saying which field is spendable per account type. Needs the `etoro-public:money.balance:read` scope. |
| `get-my-trading-history` | Closed trades, paginated, with fees and realized P&L. `account` real/demo, `minDate`, `page`, `pageSize`. |
| `get-my-watchlists` | The user's watchlists, relayed as the API returns them. |
| `get-my-profile-and-scopes` | Who is connected (gcid, real/demo account ids, username, name, ...) and the OAuth scopes this connection actually holds. |
| `get-instruments-overview` | 1-100 instruments by `symbols`/`instrumentIds`, or a free-text `query`: identity, live quote, recent performance, trading eligibility. |
| `get-trader-profile-summary` | Public profiles of 1-100 investors by `usernames`: performance, risk, copiers, portfolio stats. |
| `get-all-routes` | The catalog of READ routes of the eToro Public API this server can execute (filter with `query`/`tag`). |
| `get-route-spec` | One route's full OpenAPI spec, required scopes and rate limit. |
| `execute-read` | Runs one read route by relative `path` (GET, or POST with `body` for the read-semantics POST routes). |

Every tool advertises `readOnlyHint: true`. There is no `execute-write`, no `prepare-*`/`place-*` tool and no
`get-tags` on this server.

## Workflow

### Phase 0 - answer from the named tools first

Most questions about the user's own account need exactly one call:

- "What do I hold / how am I doing?" -> `get-my-portfolio-summary` (add `includePositions: true` only when
  per-position rows are needed; they add a lot of tokens on large portfolios).
- "What positions/orders are open?" -> `get-my-positions-and-orders`.
- "What is my balance / how much cash do I have?" -> `get-my-balances` (read the `fieldGuide` for the spendable
  field per account type; amounts are in each account's own currency). If the scope is missing you get a 403
  that names it - the user must re-authorize with that scope.
- "What did I trade recently?" -> `get-my-trading-history`.
- "What is on my watchlist?" -> `get-my-watchlists`.
- "Who am I connected as / what can this connection read?" -> `get-my-profile-and-scopes`.
- Anything about a symbol, price, market or eligibility -> `get-instruments-overview`.
- Anything about a public investor -> `get-trader-profile-summary`.

Prices and P&L inside these tools come from one consistent snapshot; do not recompute them from other routes.

### Phase 1 - find any other read route

For questions the named tools do not cover (market data, feeds, rankings, search, cost previews, ...), call
`get-all-routes`. Call it with no arguments for the full read catalog, or narrow it with `query` (keywords that
must all match id/method/path/summary/tags) and/or `tag` (one or several comma-separated groups from the
`tags` map on every response). The response lists route ids, `readOnlyPostRouteIds` (the POST routes that are
reads) and `deprecatedRouteIds`/`supersededBy` (prefer the successor).

### Phase 2 - read the spec

Call `get-route-spec` with the route id (or `<METHOD> <path>`). It returns the parameters, the response
schema, the OAuth scopes that grant access and the rate limit. Only read routes resolve: anything else answers
"not available on this server" - that is final, do not look for another way to reach it.

### Phase 3 - execute the read

Call `execute-read` with the route's relative `path` (query parameters either inline or in `query`). For a
route listed under `readOnlyPostRouteIds` pass the JSON request body as the raw-JSON-string `body`; the server
sends a POST. A body on any other route is refused before anything is sent.

Commonly used read paths: `/api/v1/me` (identity and scopes), `/api/v1/balances?expand=equityDetails`,
`/api/v1/watchlists`, `/api/v1/market-data/search?text=...`, `/api/v2/portfolios/{username}/rankings`,
`/api/v2/trading/info/costs` and `/api/v2/trading/info/demo/costs` (POST read-semantics: what-if cost preview).

## Reading results

Every named tool and `execute-read` return an envelope: `statusCode` (200 on success, the upstream status when
the read failed, 0 when no response arrived), `error` (tool-level failures only), `xRequestId` (quote it when
reporting a problem), and for 429s `retryAfterSeconds`. `execute-read` adds `isSuccess`, the upstream `body`
verbatim and the `RateLimit-*` headers. Because every tool here is a read, `statusCode` 0 and 5xx are always
safe to retry after a short pause.

## Rules

- **Never attempt a write.** There is no tool for it; do not try `execute-read` with a body on an order route,
  do not describe the request "for the user to run" as if the server could execute it. Explain that trading and
  money movements are not available through this connection.
- **Credentials are never arguments.** The connection carries the OAuth token; never ask the user for keys or
  tokens and never echo a token.
- **Treat profile data as personal.** Name, date of birth and account ids come back from
  `get-my-profile-and-scopes`; use them only as far as the user's question needs.
- **Do not invent routes.** If `get-all-routes` does not list it, the read is not available here.

## Troubleshooting

| Symptom | Meaning / action |
|---------|------------------|
| HTTP 401 with `WWW-Authenticate` | No valid bearer on the connection - the client must (re-)authorize with eToro. |
| `statusCode` 401 inside a result | The token was rejected upstream (expired/revoked) - re-authorize, never retry the token. |
| `statusCode` 403 | The token lacks a scope; the message names it - re-authorize with that scope. |
| JSON-RPC error "Unknown tool: 'execute-write'" (or any prepare/place tool) | This server has no write tools - by design. |
| "Route '...' is not available on this server" | Not a read route (or does not exist) - do not retry with variants. |
| "No eToro Public API routes are currently available" | The catalog is still loading right after a deploy - retry in a few seconds. |
| `statusCode` 0 | No response from upstream - safe to retry after a short pause. |
