etoro-mcp
MCP server for the eToro public API, providing read and write tools for market data, account management, trading, watchlists, alerts, and social feeds.
README
etoro-mcp
MCP server for the eToro public API: market data, balances, portfolio, trade history, watchlists, alerts, investor rankings, user statistics, social feeds, and — behind explicit opt-in gates — trading, watchlist/alert mutations, and social posting. Coverage is organised into toolsets; only the toolsets you select are registered, and write tools are not registered at all unless their gate is enabled — a client connected to the default configuration cannot see or call them.
Features
- Read-first by design — 62 read tools across eleven of the twelve toolsets (the
socialtoolset is write-only); the 43 write tools exist only when their gate is set — 5 trading tools behindETORO_MCP_ENABLE_TRADING, the other 38 (watchlist, alert, social, and notification mutations) behindETORO_MCP_ENABLE_WRITES. - Demo environment by default — trading tools call the demo endpoint group unless
ETORO_ENV=realis set. - Toolset selection — register only what you need via
ETORO_MCP_TOOLSETS; unknown names fail startup rather than serving a partial tool set. - Trimmed responses — tools return the fields an investor acts on (prices, units, P&L,
timestamps), not raw API dumps; list tools are bounded by a
limitparameter or by explicit range parameters (min_date/max_date/count) on the time-series tools. - Credentials stay in the environment — keys travel only in the
x-api-key/x-user-keyheaders, and error messages never echo header or credential values. - Rate-limit aware — HTTP 429 responses are retried once, honouring
Retry-After, re-sending the samex-request-idso a retried order cannot fill twice.
Toolsets
Select toolsets with ETORO_MCP_TOOLSETS (comma-separated; all selects every toolset).
Without it, the trading core is registered:
| Toolset | Default | Covers |
|---|---|---|
market |
✓ | Instrument search and details, candles, rates, closing prices, exchanges, instrument types, stock industries |
account |
✓ | Profile, balances (aggregated, by account type, history), cash transactions |
trading |
✓ | Portfolio, P&L, order status, eligibility and cost estimates; gated order/position writes |
history |
✓ | Closed-trade history |
watchlists |
✓ | Own/public/curated watchlists and recommendations; gated watchlist mutations |
alerts |
✓ | Price alerts; gated alert mutations |
rankings |
Investor rankings, presets, tags, sector/industry summaries | |
users |
User search, profiles, portfolios, performance and copier statistics | |
feeds |
News, following, for-you, instrument, and user feeds; posts and comments | |
social |
Gated posting, comments, replies, reactions, polls, and media upload | |
clubs |
eToro Club dashboard | |
notifications |
Notifications; gated mark-read |
Tools
Read tools (registered whenever their toolset is selected)
| Toolset | Tool | Description |
|---|---|---|
market |
search_instruments |
Search instruments by ticker symbol and return candidate matches (contains-matching, so verify the symbol) |
market |
get_instrument_details |
Display data for instruments by id: name, symbol, type, exchange |
market |
get_candles |
OHLCV candles for one instrument (nine intervals, up to 1000 bars) |
market |
get_closing_prices |
Latest official closing prices per instrument |
market |
get_rates |
Real-time bid/ask quotes for instruments |
market |
list_exchanges |
Exchanges (id and description) for resolving exchangeId values |
market |
list_instrument_types |
Instrument types (stocks, crypto, ETFs, ...) by id |
market |
list_stock_industries |
Stock industries (id and name) for resolving stocksIndustryId values |
account |
get_profile |
The authenticated user's identity and the scopes their keys carry |
account |
get_aggregated_balances |
Current balances aggregated across the user's accounts |
account |
get_balances_by_type |
Current balances for every account of one account type |
account |
get_balance |
Current balance of one specific account |
account |
get_balance_history |
Daily balance snapshots over a date range |
account |
list_cash_transactions |
Transactions of a cash account, newest pages first |
trading |
get_portfolio |
Aggregated portfolio: account totals plus one aggregate per instrument |
trading |
get_portfolio_breakdown |
Position-level snapshot: open positions and pending orders |
trading |
get_pnl |
Portfolio snapshot with unrealized P&L per position |
trading |
get_order |
Look up an order's status by order id or reference id |
trading |
get_close_order_info |
Look up a close order's status by its order id |
trading |
check_trading_eligibility |
Per-instrument trading rules: leverage, sizing mode, SL/TP bounds |
trading |
get_trading_cost_estimate |
Estimate the costs of a hypothetical open order without placing it |
history |
list_trading_history |
Closed trades since a given date |
watchlists |
list_watchlists |
The authenticated user's watchlists with their items |
watchlists |
get_watchlist |
A single watchlist owned by the authenticated user, by id |
watchlists |
get_default_watchlist |
Items of the authenticated user's default watchlist |
watchlists |
get_public_watchlists |
Another user's public watchlists by their numeric user id |
watchlists |
get_curated_lists |
eToro's curated investment lists available to the user |
watchlists |
get_market_recommendations |
Personalized market recommendations for the authenticated user |
alerts |
list_price_alerts |
The active price alerts on the account |
rankings |
list_ranking_presets |
Names of all publicly available ranking presets |
rankings |
get_rankings_by_preset |
Investor ranking rows for a named preset |
rankings |
get_investor_rankings |
A filtered, sorted page of investor ranking rows |
rankings |
get_investor_ranking_row |
The full ranking row for a single investor |
rankings |
bulk_ranking_rows |
Ranking rows for several investors in one call |
rankings |
list_ranking_tags |
The catalog of ranking tags with resolved names |
rankings |
get_ranking_universe_by_sector |
The ranking universe aggregated by sector or industry |
users |
search_users |
Search public investors with performance and risk filters |
users |
get_user_profile |
Public profile and account summary for one or more users |
users |
get_user_portfolio |
A user's live public portfolio: open positions and copy trades |
users |
get_user_performance |
A user's full monthly and yearly gain history |
users |
get_user_daily_performance |
A user's day-by-day or aggregated gain over a date range |
users |
get_user_trade_info |
A user's trading statistics for a period (copy-trading due diligence) |
users |
get_gain_timeseries |
An investor's gain time-series at daily, monthly, or yearly resolution |
users |
get_asset_allocation_history |
A portfolio's daily asset-allocation breakdown over time |
users |
get_market_exposure_history |
A portfolio's daily directional market exposure per instrument |
users |
get_copier_stats |
An investor's live copier count and assets-under-management tier |
users |
get_copiers_info |
Anonymised info about the people copying your portfolio |
users |
search_portfolios |
Search trader portfolios or Smart Portfolios by free text |
users |
get_top_assets |
A user's top assets ranked by impressions-based score |
feeds |
get_news_feed |
The eToro news feed (editorial and market-news posts) |
feeds |
get_following_feed |
Posts from users the authenticated user follows |
feeds |
get_for_you_feed |
The personalised For You feed for the authenticated user |
feeds |
get_instrument_feed |
The discussion feed for one instrument (market) |
feeds |
get_user_feed |
The posts published by one user |
feeds |
get_saved_feed |
The posts the authenticated user has saved |
feeds |
get_pinned_posts |
The posts a user has pinned to their profile |
feeds |
get_post |
A single post by its UUID, with an inline comments preview |
feeds |
list_post_comments |
The top-level comments on a post |
feeds |
list_comment_replies |
The replies nested under one comment |
feeds |
list_post_shares |
Who shared a post (plain reshares and quote shares) |
clubs |
get_club_dashboard |
The eToro Club dashboard for the authenticated user |
notifications |
list_notifications |
The account's in-app notifications, newest data first |
Trading write tools (registered only when ETORO_MCP_ENABLE_TRADING is set)
| Toolset | Tool | Description |
|---|---|---|
trading |
create_order |
Place an order to open a position — not idempotent: every call places a new order |
trading |
cancel_order |
Cancel a pending (not yet executed) order |
trading |
close_position |
Close an open position, fully or partially, at market |
trading |
cancel_close_order |
Cancel a pending close order before it executes |
trading |
modify_position_sltp |
Edit an open position's stop-loss and/or take-profit |
Other write tools (registered only when ETORO_MCP_ENABLE_WRITES is set)
| Toolset | Tool | Description |
|---|---|---|
watchlists |
create_watchlist |
Create a new watchlist |
watchlists |
rename_watchlist |
Rename a watchlist |
watchlists |
delete_watchlist |
Delete a watchlist |
watchlists |
add_watchlist_items |
Add items to a watchlist |
watchlists |
remove_watchlist_items |
Remove items from a watchlist |
watchlists |
update_watchlist_items |
Reorder items within a watchlist |
watchlists |
set_default_watchlist |
Set a watchlist as the selected default |
watchlists |
change_watchlist_rank |
Change a watchlist's display position |
alerts |
create_price_alert |
Create a price alert on an instrument |
alerts |
update_price_alert |
Change the target price of an existing price alert |
alerts |
delete_price_alert |
Permanently delete a price alert |
notifications |
mark_notifications_read |
Mark all in-app notifications as read |
social |
create_post |
Create a discussion post on the authenticated user's feed |
social |
create_poll_post |
Create a poll post with 2 to 4 answer options |
social |
update_post |
Edit the authenticated user's own post in-place |
social |
delete_post |
Soft-delete the authenticated user's own post |
social |
pin_post |
Pin one of the authenticated user's own posts to their profile |
social |
unpin_post |
Remove the authenticated user's pin from a post |
social |
create_comment |
Create a top-level comment on a post |
social |
update_comment |
Edit the authenticated user's own comment in-place |
social |
delete_comment |
Soft-delete the authenticated user's own comment |
social |
create_reply |
Create a reply nested under an existing comment |
social |
update_reply |
Edit the authenticated user's own reply in-place |
social |
delete_reply |
Soft-delete the authenticated user's own reply |
social |
share_post |
Share an existing post, as a plain reshare or a quote share |
social |
upload_media |
Upload a JPEG or PNG image (max 8 MB) for use as an attachment; requires ETORO_MCP_UPLOAD_DIR and reads only inside it |
social |
like_post |
Like a post |
social |
unlike_post |
Remove a like from a post |
social |
like_comment |
Like a comment on a post |
social |
unlike_comment |
Remove a like from a comment |
social |
like_reply |
Like a reply (a nested answer to a comment) |
social |
unlike_reply |
Remove a like from a reply |
social |
save_post |
Bookmark a post |
social |
unsave_post |
Remove a post from the saved bookmarks |
social |
follow_post |
Subscribe to notifications on a post |
social |
unfollow_post |
Unsubscribe from notifications on a post |
social |
vote_poll |
Cast a vote on a poll option |
social |
remove_poll_vote |
Remove a vote from a poll option |
Configuration
All configuration comes from environment variables — no config files, no flags carrying secrets.
| Variable | Default | Purpose |
|---|---|---|
ETORO_API_KEY |
— (required) | eToro API key, sent as the x-api-key header |
ETORO_USER_KEY |
— (required) | eToro user key, sent as the x-user-key header |
ETORO_ENV |
demo |
demo or real — which trading endpoint group tools call. eToro keys are environment-bound, so a mismatched key fails at the API |
ETORO_MCP_TOOLSETS |
trading core (see table above) | Comma-separated toolset names; all selects every toolset. Unknown names fail startup with the list of valid names |
ETORO_MCP_ENABLE_TRADING |
off | true/1/yes registers the trading write tools: order placement/cancel, position close, stop-loss/take-profit edits |
ETORO_MCP_ENABLE_WRITES |
off | true/1/yes registers all other write tools: watchlist/alert mutations, notification mark-read, social posting and reactions |
ETORO_MCP_UPLOAD_DIR |
— (unset) | The only directory upload_media may read images from. Unset (the default) leaves upload_media unregistered, so the server can never read local files. Paths and symlinks resolving outside the directory are rejected |
Getting started
Run straight from the repository with uv:
uvx --from git+https://github.com/florinel-chis/etoro-mcp etoro-mcp
The server speaks stdio by default; pass --transport http --host 127.0.0.1 --port 8000 to serve
streamable HTTP at /mcp instead.
Warning: the HTTP endpoint has no authentication — anyone who can reach the port can call every registered tool with your API keys' privileges. The server therefore refuses to bind a non-loopback
--hostunless--allow-remoteis also passed; only use that flag behind an authenticated reverse proxy or an otherwise-restricted network (such as the Docker port-mapping below).
MCP client configuration
Add the server to your MCP client's configuration (the exact file location depends on the client):
{
"mcpServers": {
"etoro": {
"command": "uvx",
"args": ["--from", "git+https://github.com/florinel-chis/etoro-mcp", "etoro-mcp"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENV": "demo"
}
}
}
}
Or run the Docker image (build it first, see below):
{
"mcpServers": {
"etoro": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "ETORO_API_KEY", "-e", "ETORO_USER_KEY", "-e", "ETORO_ENV", "etoro-mcp"],
"env": {
"ETORO_API_KEY": "your-api-key",
"ETORO_USER_KEY": "your-user-key",
"ETORO_ENV": "demo"
}
}
}
}
Docker
docker build -t etoro-mcp .
# stdio (for MCP client configs)
docker run -i --rm -e ETORO_API_KEY -e ETORO_USER_KEY etoro-mcp
# streamable HTTP at http://127.0.0.1:8000/mcp
# (-p 127.0.0.1:8000:8000 keeps the unauthenticated endpoint off external interfaces;
# --host 0.0.0.0 refers to interfaces inside the container and is required for the
# port mapping to work, hence --allow-remote — the host-side bind stays loopback-only)
docker run --rm -p 127.0.0.1:8000:8000 -e ETORO_API_KEY -e ETORO_USER_KEY \
etoro-mcp --transport http --host 0.0.0.0 --port 8000 --allow-remote
Safety
- Trading tools call the demo endpoint group unless
ETORO_ENV=realis set. Keys are environment-bound, so a demo key againstreal(or vice versa) fails at the API rather than trading the wrong account. - Write tools are hidden behind two independent gates —
ETORO_MCP_ENABLE_TRADINGfor order/position writes andETORO_MCP_ENABLE_WRITESfor everything else (watchlists, alerts, notifications, social). Gated tools are never registered, not merely rejected, so MCP clients cannot discover or call them. - The HTTP transport is unauthenticated: anyone who can reach the port can call every
registered tool — read balances and positions always, and place or close orders when trading
is enabled — using your API keys' privileges. The server refuses non-loopback binds unless
--allow-remoteis passed; only use that flag behind an authenticated proxy or an otherwise-restricted network (the stdio transport has no such exposure). upload_mediareads local JPEG/PNG files and returns a public CDN URL for them — an extra privilege class beyond the API keys themselves. It is therefore triple-gated: thesocialtoolset,ETORO_MCP_ENABLE_WRITES, andETORO_MCP_UPLOAD_DIRmust all be set, and it reads only inside that directory (paths and symlinks resolving outside it are rejected, file content must actually be JPEG/PNG, and the 8 MB limit is checked before the file is read).create_orderis not idempotent across calls: each call mints a freshx-request-id, which is the order's idempotency key, so calling it twice places two orders. (The 429 retry re-sends the same id, so a rate-limited retry cannot double-fill.) Poll fill status withget_orderbefore treating an order as filled — an accepted order can still be rejected.- Candle history is capped at roughly 1000 bars per interval with no way to page further
back; use a coarser interval (e.g.
OneWeek) for deeper history. - The API enforces per-group rate quotas (order execution 20 requests/60 s; market data
120/60 s; portfolio and most other groups 60/60 s; eligibility and cost estimates 20/60 s
each). Each tool's description names its quota; 429s are retried once, honouring
Retry-After. - Trading leveraged products is risky. Use at your own risk, and test everything against a demo account first.
Development
uv sync
uv run pytest
uv run ruff check .
Tests are hermetic: all HTTP is mocked with respx and the server is exercised through the
in-memory FastMCP client. No credentials or network access are needed to run them. A manual,
read-only smoke check against a real demo account lives at scripts/smoke.py.
License
MIT — see LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
Neon Database
MCP server for interacting with Neon Management API and databases
E2B
Using MCP to run code via e2b.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.