WarEra MCP
Read-only WarEra MCP server — lets an AI look up live game facts (players, companies, countries, markets, battles, events) and answer questions about them; the 17 tools shown here are part of a 44-tool read-only suite.
Players — public profile and stats (
get_player), plus companies they own with product, location, workforce and production bonus (get_player_companies).Companies — single company details: output, location, workforce, normalized production bonus, optional region context (
get_company_overview).World — country facts/regions (
get_country_overview), region detail by id or exact name (get_region), and current war list plus active battles (get_country_wars).Market — latest global quoted price for one item (
get_market_price), visible buy/sell order book with optional depth estimate (search_market), and wage benchmark with matching work offers (get_work_market).Battles — search battles by country/active state (
search_battles), battle status with optional live round snapshot (get_battle), and battle leaderboards by damage or points for users, countries or MUs (get_battle_ranking).Events — bounded page of recent world events filtered by country or event type (
search_events).Safety traits — every tool is read-only, idempotent, non-destructive; credentials are optional and request-scoped via
player_context; results are bounded/paginated snapshots, not complete histories.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@WarEra MCP What is the current price of iron and how deep is the order book?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
WarEra MCP
A read-only MCP server that lets an AI assistant look things up in the browser game WarEra — players, companies, countries, markets, battles, military units, equipment, governments, rankings, workers, transactions, events and articles — and answer questions about them in plain language.
⚠️ Unofficial project
This is an unofficial, community-made project. It is not affiliated with, endorsed by, sponsored by, or supported by the WarEra developers or publishers in any way. "WarEra" and related names belong to their respective owners.
It is a personal open-source hobby project, built in my free time with the help of AI. It uses WarEra's web API and its official documentation. API behavior and response shapes can change; documented requests are checked against live responses where possible. Please be considerate with the game's servers.
What is this?
MCP (Model Context Protocol) is a standard way to plug tools into AI assistants such as Claude Desktop. This server gives an assistant purpose-built, game-aware tools instead of raw web requests, so you can simply ask:
"What does player Kiro own, and what are their production bonuses?"
"What is the current price of iron, and how deep is the order book?"
"Which countries is Freedonia at war with, and are there active battles?"
"Who is dealing the most damage in this battle?"
"Elencami gli ultimi 10 articoli pubblicati."
"Elencami gli articoli in italiano."
"Riassumi le ultime novità di WarEra tramite gli articoli."
The server fetches data and returns structured facts with stable identifiers, observation times
and explicit gaps. The assistant can combine those facts for analysis, calculations and simulations.
Successful tools return these facts in both structuredContent and a JSON text block in
content, after a short summary, so clients that consume only text receive the same data.
The output-byte limit applies to the projected facts before this compatibility copy is added.
For company construction, get_game_rules(topic="companies") includes the concrete
cost formula: configured increment × (owned companies + 1). With the current increment
of 50, the sixth company costs 300 concrete. This formula was verified against the
official companies page on 2026-10-06; the increment
comes from the fetched configuration. An explicit company number needs no player lookup.
For personalized questions such as production time for "my next company", the server
instructs the assistant to ask for the in-game username when no player identity is known.
Selected tools accept optional request-scoped WarEra credentials (api_key or jwt) in
player_context; the project never provides a default or global WarEra credential.
Related MCP server: WoW Armory MCP Server
Connect to the hosted MCP
Use this HTTPS MCP endpoint in Claude or ChatGPT:
https://warera-mcp-agkzl5m3va-ew.a.run.app/mcpRemote connections use Streamable HTTP and require no account or token.
Claude
Open Customize → Connectors → + Add → Add custom connector.
Enter WarEra as the name and the HTTPS endpoint above as the server URL.
Continue to the authentication settings and choose No sign in. Leave OAuth settings and request headers empty.
Click Add, then enable WarEra from + → Connectors in a conversation.
Try: "Using WarEra, what is the current market price of iron?"
The hosted endpoint requires no MCP client token. WarEra player API keys and JWTs are separate, optional credentials for selected tools; public tools need none. For Team or Enterprise, an organization owner or authorized administrator must add the connector before members can enable it. See Claude's remote connector guide.
ChatGPT
Use ChatGPT on the web. If required, enable Developer mode under Settings → Security and login. Workspace permissions may restrict access.
Open Plugins, select + → Create custom MCP server, and enter WarEra with the HTTPS
/mcpURL above.Select No authentication. Leave OAuth client ID and secret empty.
Review the connection warning, create the plugin, and install it.
Enable WarEra in a conversation and ask a game-data question.
See OpenAI's custom MCP setup guide.
Quick start
You need Python 3.11+ and uv.
git clone https://github.com/LorissDaniel/warera-mcp.git
cd warera-mcp
uv sync --locked
uv run warera-mcp # serves over stdioUse it from an MCP client (for example Claude Desktop) by adding it to the client's config:
{
"mcpServers": {
"warera": {
"command": "uv",
"args": ["--directory", "/path/to/warera-mcp", "run", "warera-mcp"]
}
}
}Run it as a small web service instead:
uv run warera-mcp --transport streamable-http --port 8000 # MCP endpoint: http://127.0.0.1:8000/mcpThis command binds to loopback by default. For remote use, configure client authentication, trusted hosts and HTTPS through your deployment or reverse proxy. The server warns about missing authentication/trusted hosts when configured to bind outside loopback; it cannot detect every externally reachable proxy configuration. See .env.example for the environment variable names. MCP client bearer tokens are separate from WarEra credentials.
If a desktop client cannot find uv, use its absolute executable path in command.
After updates, restart or reconnect the MCP server so the client refreshes its tool schemas.
Tools
Area | Tools |
Players |
|
Companies |
|
Discovery |
|
World |
|
Market |
|
Battles |
|
Mercenaries |
|
Rankings |
|
Labor |
|
Transactions |
|
Military units |
|
Events |
|
Articles |
|
Official configuration |
|
All 44 tools are marked read-only. There is deliberately no generic "call any endpoint" tool.
Locations and player resources
get_recommended_regions(item_code="iron", include_deposit=false) asks the game for
its ranked company locations excluding deposit bonuses. It exposes bonus components and
taxes as fractions, plus region/country names when available. API key is required;
JWT is never used for this operation, even when supplied. The ranking covers the recommendations
returned by the game (five in the verified responses), not every world region. offset and limit page
that returned list; deposit bonuses are never subtracted using an inferred formula.
get_player_resources(user_id=..., item_codes=["iron", "steel", "fish"]) reads available
money and basic materials from the player's inventory, market reservations, and owner-order
aggregates including quantities for sale. Use an exact username instead of user_id when
necessary. Set include_orders=false to skip the order read. Keep available materials,
market reservations and sell quantities separate: reservations and orders can describe
the same stock. Expected sales proceeds are not available money. Missing/malformed fields
remain unknown; a valid sparse quantity map treats omitted requested codes as zero.
Inventory and owner orders require JWT. On 2026-10-05, authorized live checks returned 401 anonymously, 403 with API key, and 200 with JWT for both reads. These responses are never shared-cached. The resource tool exposes only selected numeric fields and the requested player identity, without equipment, managers or other raw inventory fields.
Public operations always run anonymously, even if player_context contains credentials.
Credentials are requested only after a tool reports missing authentication. Authentication
is selected independently for each endpoint; there is no automatic retry with a JWT after
an API key is rejected. The project never loads credentials from another project's .env.
Multiple material prices
For price comparisons, use one call such as
get_market_prices(item_codes=["iron", "steel", "fish"]) instead of one
get_market_price call per material. Omit item_codes to retain the complete
catalog behavior. Lists accept 1–64 codes; duplicates are removed. Filtering is
local over one shared, cached itemTrading.getPrices snapshot; it does not issue
one upstream request per material. Prices remain sorted by descending value.
Omit limit to return every selected quote. If supplied, it applies after
filtering and sets truncated=true when prices are omitted. Missing/unquoted
codes are reported in missing_item_codes, while available prices are retained;
partial=true indicates missing quotes or truncation. Missing prices must not be
used as zero in calculations. These are global quoted prices; for visible
bids, asks and quantities, use search_market for each relevant material. Visible
orders are snapshots and do not guarantee execution or sufficient liquidity.
Filtered pages and visible market depth
get_work_market(item_code="iron", region_id=..., citizenship=..., level=18, energy=10, production=10) sends the documented offer filters upstream before
pagination. user_id filters by the offer's user ID. Zero numeric values are sent
explicitly; omitted values are not inferred from a player profile. The tool does
not independently certify eligibility. item_code selects the wage benchmark,
not the product of the offers. Wages retain API units; no hourly rate is inferred.
limit (1–10) controls the upstream offer page size. Continue with
page.next_cursor and the same filters. minimum_net_wage remains a local filter
of the fetched page, using gross wage when net wage is absent. A reported net wage
of zero remains zero. An empty filtered page can still have later matches when
page.has_more=true. Offer IDs, user/company/region IDs, remaining and initial
quantities, and timestamps support follow-up queries. If offers cannot be read,
partial=true and the absent page indicate unavailable coverage.
search_battles applies country_id, war_id, defender_region_id and
is_active upstream. A defender region is the attacked region. The optional
direction is forward or backward; these API pagination directions do not
promise chronological ordering. search_events applies country_id and
event_types upstream. Its parameter schema lists the documented event codes;
input casing is normalized and unsupported codes are rejected before a request.
Output event types are lowercase, with unknown: for unrecognized types.
If the API exceeds the requested page size, bounded output carries partial=true
and a warning about omitted rows; the cursor may not recover those rows.
For both tools, follow page.next_cursor with identical filters and direction
where applicable; a page is not complete historical coverage.
search_market(item_code="iron", max_orders=25) forwards max_orders as the API's
limit, supporting 1–100 visible orders per side. Existing buy orders are bids;
sell orders are asks. To estimate purchases choose side="sell"; to estimate
sale proceeds choose side="buy". With depth_quantity, compare returned
depth.quantity against the target: the reported VWAP may cover only a partial
fill. The book remains a bounded snapshot, not guaranteed executable liquidity.
get_player_companies requests up to 100 company IDs per upstream page using the
verified perPage parameter, then retrieves bounded detail batches using limit
and the configured fan-out cap. To consume the current page, follow next_offset
with the same cursor. Once next_offset is absent, follow page.next_cursor
with offset=0. total_count is returned only when the first upstream page
contains the entire owned list; a final continuation page's size is not a total.
Ownership lists can change between reads; preserve observation times.
Public player facts
get_player(user_id=...) returns the public profile's MU id, military rank,
active flag, creation date, numerical leveling/statistics and reported activity
timestamps, alongside skills and rankings. fields can select profile,
location, level, skills_summary, rankings_summary, activity and
statistics, missions and equipment; omitting fields selects all groups. The profile's military_unit_id
links directly to the MU tools without a membership search.
By default, get_player enriches user.getUserLite with reviewed game facts from
user.getUserById: region and location IDs, company and party references, profile
update time, reported MU maximum level rewarded, equipped-slot item IDs, mission
statistics/claim timestamps and completed-tour flags. The company_id reference
does not establish ownership; use get_player_companies for owned companies.
equipment_ids identifies equipped slots, not inventory quantities or full item
attributes. Missing fields remain unknown; account metadata and UI preferences
are not exposed. include_full_profile=false skips enrichment. Focused reads
containing only level, skills or rankings also use lite only. profile_source
reports lite or full; failed or mismatched full-profile reads retain lite facts
with partial=true and a warning. Additional maps are bounded to 64 entries.
skills and rankings remain compact numerical maps, compatible with older
scalar payloads. Nested skill summaries use reported total, falling back to
value only when total is absent; they do not recompute the game's formula.
skill_details additionally preserves level, current bar, base value, weapon,
equipment, overflow, limited amount, total, total after soft cap, regeneration
and prestige. Named percentage modifiers are exposed in modifiers_fraction
(e.g. militaryRankPercent:6.25 becomes militaryRank:0.0625). Skill totals/base
values keep the game's skill-specific units, including percentage-point values
where applicable. They must not be combined blindly with modifier fractions.
Future numeric skill components are kept in additional_numeric_components.
ranking_details preserves reported value, rank and tier independently.
wealth_breakdown preserves public stats.wealth components: money, items,
companies, equipments, weapons and total, plus additional numeric components
when reported. It belongs to the statistics field group and is capped at 64 entries.
Values and the reported total are retained independently; the server does not recompute
or reconcile them with the wealth ranking, which may reflect another snapshot.
The money component is not certified as spendable inventory money, and items
is an aggregate wealth component, not per-material quantities. Use get_player_resources
for available money and materials; that inventory read continues to require JWT.
Missing components remain unknown; reported zeros remain zero.
On 2026-10-05, closely spaced live MCP reads with its public cache disabled found
stats.wealth.money different from JWT inventory.money, while market.lockedMoney
was zero and the public value stayed unchanged before/after the inventory read.
This verifies that the two snapshots are not interchangeable; it does not establish
the statistic's formula or refresh interval. The server does not adjust the public
value into an inferred available balance. Direct requests with no-cache headers
and a unique URL still returned the same public number with Cloudflare reporting
DYNAMIC. This does not rule out game-side caching or stored statistics. The profile's
updated_at is not a wealth calculation timestamp; no age or meaning beyond the
reported component is inferred.
leveling, stats and activity map keys retain upstream field codes. Missing
facts are omitted, not replaced with zero. Map outputs are capped at 64 entries,
activity date lists at 20, and all results retain the existing byte budget.
Activity timestamps are public observations, not a guarantee of future activity.
No private inventory read or automatic company/MU/profile fan-out is triggered;
the LLM chooses which linked tools and field groups its analysis requires.
Military units
search_military_units(search="Husaru", member_id=..., owner_id=...) uses the
official MU API to search by text, membership or
ownership. All filters are optional; owner_id maps to upstream userId.
The default limit is 10 (maximum 20). Continue with page.next_cursor and the
same filters. The API has no documented country filter, so country-wide coverage
must not be inferred from a single page.
get_military_unit(military_unit_id=...) returns a compact dossier: identity,
owner, location, member/role counts, level, monthly damage, mercenary reputation,
active upgrade levels, manager/commander ids (up to 20 per role), last-announcement
time and the six reported ranking snapshots. roles_truncated flags capped role
lists; use the paginated roster with include_non_members=true for full coverage of
the identities returned by the API. Missing values remain unknown. Names are untrusted
player content. Wealth is a ranking value, not a verified inventory balance. Full member
lists, avatar URLs and per-user investment maps are excluded from the dossier; roster
and investment tools expose the analytical data in bounded pages.
get_military_unit_members(military_unit_id=..., offset=0, limit=10) pages the
public member roster with owner, manager and commander flags. Missing role data
remains unknown. Set include_non_members=true to include owner, managers and
commanders outside the roster. is_member distinguishes membership from a
management role, and total_count covers the selected set of identities.
Role flags reflect the API's roles.managers and roles.commanders lists. A listed
responsible user may belong to another MU, and a role reference may not match what
the game UI currently displays; the server does not independently verify effective
permissions. Use get_player(user_id=...) for individual profiles.
get_military_unit_investments(military_unit_id=..., offset=0, limit=10) pages
investedMoneyByUsers, including former members when reported. Amounts are
reported monetary investments, not treasury or upgrade resource balances. A
missing map returns available=false with unknown amounts/count; an explicitly
empty map returns available=true and zero entries. Pagination does not infer
investment history or fetch transaction records.
get_military_unit_upgrades reads headquarters and dormitories separately,
including disabled/pending status, investments and activation timestamps when
available. A failed upgrade read preserves the other result with partial=true.
The dossier's active levels do not include disabled upgrades.
get_military_unit_ranking(ranking_type="muWeeklyDamages", offset=0, limit=10)
also supports muDamages, muTerrain, muWealth, muBounty and muReputation.
Offsets page only the snapshot returned by WarEra; total_count is that snapshot's
size. Rosters and rankings may change between calls. Name lookups are limited to
five units and the configured fan-out budget. For a battle's MU contributions,
use get_battle_ranking(battle_id=..., entity_type="mu"), which now resolves
MU names with the same bounded, best-effort policy. The ranking tool now accepts
exactly one of battle_id, war_id or round_id, plus metric="damage",
"points" or "money". It forwards limit and cursor, returns page.next_cursor
and preserves the reported item_count. A legacy oversized response is capped
locally with a warning. A positive count with no rows is marked partial=true,
so it cannot be treated as zero participation. Battle dossiers/search results
include war_id, current round ids and the MU/country ids with orders on each
side. Round summaries/live snapshots preserve country ids, damage, points, hit
counts, tick counters/points and timestamps from the current nested API shape,
while accepting the older flat fields. Equipment and last-hit payloads remain
outside those compact summaries.
These reads were verified anonymously on 2026-10-05 against OpenAPI 0.17.4-beta
(machine-readable specification). They use
GET, public TTL caching and the existing output-byte limit. MU management actions
remain outside this read-only project. For MU transaction history use
search_transactions(military_unit_id=...) with a request-scoped API key.
Public identifier discovery
get_country_players(country_id=..., limit=10) returns player IDs and account
creation timestamps, with upstream cursor pagination. Follow page.next_cursor
with the same country. Account creation is not a country joining date or last
activity; one page is not total or active population. Use get_player for details.
search_entities(search="Loris") returns candidate IDs grouped by user, MU,
country, region, party and alliance. Offset/limit page the API's retained matches
locally per group; there is no upstream cursor or guarantee of complete search
coverage. Missing groups remain unknown; explicit empty lists are known empty.
It does not certify an exact identity. For an exact username use get_player.
Equipment, upgrades and government
get_player_equipment(user_id=...) or get_player_equipment(username="Loris")
returns publicly equipped item attributes, condition, skill bonuses and acquisition dates.
Explicit null slots appear in empty_slots; omitted slots are unknown. This is the
current loadout, not inventory or tradable stock. Slot maps are capped at 20 and skill
maps at 64 entries.
get_company_upgrades covers storage, automatedEngine and breakRoom;
get_region_upgrades covers bunker, base and pacificationCenter. Omit
upgrade_types for all three, or select a subset. Reported status/level,
money/concrete/steel investments and activation/change dates remain separate.
absent_upgrade_types means a successful null response; it does not infer a zero level.
unavailable_upgrade_types identifies failed or invalid reads and sets partial=true.
Use company/region tools for context and game configuration for supported upgrade costs/effects.
get_country_government(country_id=..., offset=0, limit=10) returns president/minister
user IDs, a locally paginated congress roster and reported activity dates. congress_member_count
covers the returned roster; a missing roster is unknown. Follow the user IDs with
get_player. These references describe reported roles, not independently verified permissions.
Rounds, orders, loot and mercenary contracts
get_round(round_id=...) retrieves a specific round, including battle/country links,
damage, points, hits, ticks and timestamps. get_round_hits pages the recent hit arrays
with separate snapshot_counts and has_more per side. Its offset/limit are local,
not historical API pagination. include_equipment=true adds weapon, ammo and up to
10 equipment items as recorded at the hit. A reported missed hit can still have
reported damage; neither is overwritten using an inferred formula.
get_battle_orders(battle_id=..., side="attacker") returns a locally paginated
order snapshot with country/MU/user links, priority and activity. The anonymous view
may hide text and rank. Empty text with rank zero is exposed as
text_and_rank_visibility="restricted_in_anonymous_view", with text/rank unknown.
It must not be interpreted as a public zero rank or permission to inspect private orders.
get_battle_loot(battle_id=..., user_id=...) preserves reported case counts,
hits, total damage, bounty/contract money and pool-loot numeric facts/references.
A missing summary produces an error, not zero earnings. Pool loot is capped at
20 entries; truncation sets partial=true. These are reported rewards, not a current balance.
search_mercenary_auctions(country_id=..., battle_id=..., status="won") applies
filters upstream. The schema lists supported status values; omitted status uses the
API default and does not promise all historical statuses. Follow page.next_cursor
with the same filters. Budget, minimum damage, current rates/payouts, winner IDs,
battle/round links and expiry remain distinct. include_bids=true adds up to
bid_limit bids per auction (maximum 20); bid_count covers the record and
bids_truncated signals omissions. Duration/rate fields retain API units; no
undocumented conversion or guaranteed final payout is inferred.
Global rankings
get_global_ranking(ranking_type="userDamages", offset=0, limit=10) supports all
36 documented user, country, alliance and MU ranking types. It preserves entity IDs,
reported rank/value/tier, tier thresholds and country/MU links when supplied.
There is no automatic profile fan-out; use linked tools as needed. Offsets page the
snapshot returned by the API, not the entire world population; snapshot_count
reports that snapshot's size. No historical week selector is documented. For MU name
expansion use get_military_unit_ranking. Wealth rankings are not spendable inventory.
Work details and API-key transactions
get_work_offer(work_offer_id=...) or get_work_offer(company_id=...) reads one
public offer; supply exactly one identifier. It includes wage/net wage, offer quantities,
minimum energy/production/level when supplied, linked entities and timestamps.
Use get_work_market for filtered offer discovery.
get_workers and search_transactions require player_context={"api_key":"..."}.
They were verified with API key on 2026-10-05; neither requires JWT and neither uses
shared caching. Credentials are never returned in the result.
get_workers(company_id=...) lists company workers; get_workers(user_id=...)
lists workers grouped across that user's employer company portfolio, not the user's
own employment. Supply exactly one scope. Wage, fidelity, joining/locking dates and
worker IDs support profile/company joins. Offset/limit page the returned snapshot
locally. snapshot_count and the separately reported reported_total_workers_count
are distinct observations; missing or failed counts remain unknown. Empty worker
arrays are known empty lists. Company summaries are capped at 20, with explicit truncation.
search_transactions(user_id=..., military_unit_id=..., transaction_types=["donation"]) also supports country, party and item-code filters. All filters apply upstream; omission leaves the API scope unfiltered. Follow page.next_cursor` with identical filters.
Transactions preserve reported money/quantity, timestamps, item attributes and separate
buyer/seller user, MU, country and party IDs. Keep those API labels: for a donation,
“buyer” is not automatically a goods buyer. Reported money is neither a unit price nor
a signed cash flow; establish the transaction semantics before calculating totals.
A page alone does not establish complete income, expenditure or historical coverage.
Articles
search_articles defaults to the latest published feed (feed="last", limit=10,
maximum 20). It forwards language codes (languages=["it"] for Italian), categories
(e.g. ["news"]), author_id and positive_score_only to WarEra. Other public feeds
are daily, weekly and top; use last for chronological news. Pass page.next_cursor
with the same filters to continue. Personal my/subscriptions feeds are not exposed.
Metadata-only results are compact. Set include_content=true for summaries: the feed
already includes article bodies, which the server converts from HTML into untrusted
plain text without fetching images or links. Each body is limited to 12,000 characters
(lower it with content_max_chars, minimum 280); content_truncated and warnings
identify incomplete text. The existing output-byte budget still applies: request fewer
articles or shorter content if necessary. The LLM writes the summary, cites titles/IDs,
authors and publication dates, and distinguishes player claims from official announcements.
get_article(article_id=...) returns only title and basic statistics via the lite
endpoint. It does not provide body text. The full-detail article.getArticleById endpoint
is deliberately excluded because it can count a view; summaries use the paginated feed.
Both exposed queries were checked anonymously against the
official API documentation.
How it works (the short version)
Read-only by design. The client calls only explicitly reviewed read operations. Most are public; selected reads require request-scoped authentication. State-changing operations and the potentially view-counting full article endpoint are excluded.
Structured, linkable facts. Tools project upstream data into documented fields; some combine related reads. The assistant chooses further tools using stable IDs or item codes and retains snapshot times, units and coverage limits.
Bounded. Results, number of upstream calls, response size and time per request are all capped, so one question can't run away.
Stateless and lightweight. No database and no accounts. Only a small in-memory cache of public data.
Game text is treated as untrusted. Player and company names are cleaned and length-limited before the assistant sees them.
Privacy & security
Public operations run anonymously and never forward supplied credentials.
Region recommendations, workers and transactions require API key.
Inventory and owner orders require JWT; current equipped items are public.
Configuration tools always read anonymously and do not accept
player_context.If supplied,
player_contextcarries the caller's own request-scopedapi_keyorjwt.The project has no default or global WarEra credential.
Submitted credentials are not logged, cached or persisted by the server, but they are sent in the MCP tool request and may be visible in the LLM/client conversation history.
The server has no persistent conversation store. Public responses can be held in its in-memory TTL cache, and operational logs record sanitized request metadata. The MCP client, hosting platform and reverse proxy have their own storage/logging policies.
Found a security problem? Please report it privately (for example via your hosting platform's security-advisory feature) rather than opening a public issue.
Configuration
Settings are read from WARERA_MCP_* environment variables. .env.example documents
commonly used settings; the complete list and defaults are in
src/warera_mcp/config.py. The server does not automatically
load a .env file, so pass these variables through your shell, MCP client configuration,
or deployment environment. Command-line options can override the bind host, port and log level.
The upstream defaults to https://api2.warera.io and rejects another host unless
WARERA_MCP_ALLOW_CUSTOM_UPSTREAM=true is explicitly enabled for local mocks. HTTP
redirects are never followed.
The upstream response cap defaults to 8,000,000 bytes to accommodate verified global ranking snapshots larger than 2 MB. The complete HTTP body (including a batch) is still bounded. Structured tool output remains capped at 262,144 bytes; use bounded pages and optional detail flags to control output size.
Game rules, skill progression, item details and schedule data are fetched from WarEra's
gameConfig.getGameConfig and gameConfig.getDates queries. When public caching is
enabled, their cache lifetimes are five minutes and 30 seconds respectively. Set
WARERA_MCP_CACHE_ENABLED=false to disable caching; API batching works independently.
Results include the time the server fetched the data and a SHA-256 fingerprint computed
locally from the returned content. The fingerprint is not an official game revision.
These tools expose selected fields from the API responses, including production recipes,
skill levels, upgrade costs and schedule timestamps. They do not provide every game
formula. get_item_catalog returns a separate built-in list of item codes; configuration
items and the live market price list can differ from that list. Player skill summaries
are included only when the public profile supplies them.
Company overviews fetch recipes from game configuration when available. Raw materials expose
production points per unit through get_item_details, have no ingredient recipe, and do not trigger
missing-recipe warnings. Manufactured products still require validated recipe inputs.
Python integrations can supply a recipe catalog through ServiceRuntime to override that lookup;
the standard CLI uses no local recipe overrides. Contract fixtures are used only by tests and are not
production defaults.
API batching
Concurrent upstream reads with identical authentication headers are collected for
up to 300 ms and sent directly to WarEra in a single GET tRPC batch. Identical
reads within that batch share one response; responses are not retained by the
batcher. This works with WARERA_MCP_CACHE_ENABLED=false.
Company details and bonuses, battle details and live status, and player profile enrichment can share batches. Reads that depend on an earlier result still run in separate stages. Sequential MCP tool invocations cannot share a batch.
Batches send early at 20 pending calls and split before their encoded URL exceeds 8000 bytes. Isolated reads use the original single-query GET format. The existing response byte limit applies to the entire batch. Queue capacity bounds active and pending logical reads; cancellation removes unused work without affecting other callers. Each item retains its own result, error, and retry budget.
Configure batching through:
Environment variable | Default | Meaning |
|
| Set |
|
| Collection delay per stage (0–0.4 seconds) |
|
| Maximum pending calls per batch (1–50) |
|
| Maximum encoded batch URL size |
An individual query too large for the batch URL budget uses the existing single
GET path. Transport limits and rate limiting apply to physical HTTP requests.
With batching enabled, circuit health is also recorded once per HTTP attempt;
retries check the circuit before sending again. warera_requests_total counts
logical reads, while warera_http_requests_total counts actual HTTP attempts;
warera_batches_total and warera_batch_size describe multi-query batches.
Batching reduces HTTP overhead; WarEra may still count each procedure toward its
API quota.
Development
uv sync --locked
uv run pytest # tests run offline against a stubbed API
uv run ruff check . # lint
uv run mypy # strict type checkingThe test suite never calls the real game API unless you opt in with WARERA_MCP_LIVE_TESTS=1.
Contributing
Issues and pull requests are welcome. Please keep changes small and focused, add or update tests,
and make sure ruff, mypy and pytest pass. The one hard rule: this project stays read-only —
changes that add anything able to modify game state will not be accepted.
Acknowledgements
Built with the help of AI tooling, by a player, for players. Thanks to the WarEra community for sharing what they've learned about the game.
License
Released under the MIT License. Provided "as is", without warranty of any kind.
Available Tools
13 toolsget_battleARead-onlyIdempotent
Get a battle's status: sides, current round and optionally a live snapshot. Live values are volatile snapshots with an explicit tick timestamp. It does not return last-hits lists or equipment payloads.
Inputs: battle_id (required), player_context, include_history, include_live_status.
get_battle_rankingARead-onlyIdempotent
Get a battle leaderboard by damage or points for players, countries, or military units on one side. The upstream ranking is unbounded, so results are capped locally and truncated says whether more rows exist. This is a battle ranking, not the deferred global wealth ranking.
Inputs: battle_id (required), side, limit, metric, +2 more.
get_company_overviewARead-onlyIdempotent
Get one company's details: what it produces, where it sits, its workforce and its production bonus (normalized to a fraction). Recipe inputs are only reported when a validated local catalog is available, and they describe configured requirements, not current stock.
Inputs: company_id (required), player_context, include_region_context.
get_country_overviewARead-onlyIdempotent
Get selected verified facts about a country plus, optionally, its regions. Names are matched exactly and case-insensitively, and this is a current snapshot only. It does not report war relationships — use get_country_wars for those.
Inputs: country_id, country_name, limit_regions, player_context, +1 more.
get_country_warsARead-onlyIdempotent
Show the countries this country is currently listed as at war with, plus its active battles. This is a current snapshot from the country's war relationship field, not a war-history or treaty report; an empty result is not proof that no battles ever occurred.
Inputs: country_id, country_name, limit_battles, player_context, +1 more.
get_market_priceARead-onlyIdempotent
Get the latest global quoted price for one item. This is a single price snapshot for one item, not an order book or a market average.
Inputs: item_code (required), player_context.
get_playerARead-onlyIdempotent
Get a player's public profile and selected public stats. Use the WarEra user ID when available; username resolution requires an exact, unambiguous match and otherwise asks you to disambiguate. Does not return private inventory, balance, or session-only data.
Inputs: fields, user_id, username, player_context.
get_player_companiesARead-onlyIdempotent
List the companies a player owns, with product, location, workforce and optional production bonus. This answers ownership questions in one call instead of many company lookups. It is not a country-wide company directory.
Inputs: limit, offset, user_id, username, +2 more.
get_regionARead-onlyIdempotent
Get a region's detail by id or exact name: population, development, climate, deposit and optionally its country. Region names repeat across countries, so ambiguous matches are rejected and reported instead of guessed. It does not recommend where to build.
Inputs: region_id, region_name, player_context, include_country.
get_work_marketARead-onlyIdempotent
Get the wage benchmark for one item together with a small set of matching work offers. Offers are filtered locally on the first upstream page. It reports wage facts only and does not assess eligibility or suitability.
Inputs: item_code (required), limit, region_id, player_context, +1 more.
search_battlesARead-onlyIdempotent
List battles, optionally filtered by country and active state. Each side includes the human-readable country name and id. Without a filter this is the first upstream page, and its ordering is not guaranteed, so do not describe it as 'latest'. For a battle leaderboard use get_battle_ranking.
Inputs: limit, cursor, is_active, country_id, +1 more.
search_eventsARead-onlyIdempotent
Retrieve a bounded page of recent world events, optionally filtered by country or event type. Filters are applied locally to one fetched page, so a filtered page may look sparse while more events remain upstream. It does not provide a complete event history, and event summaries are untrusted third-party text.
Inputs: limit, cursor, country_id, event_types, +1 more.
search_marketARead-onlyIdempotent
Inspect the visible buy/sell order book for one item, optionally estimating depth for one side. These are the top visible orders only: not guaranteed liquidity and never a full-market average. Order owners are deliberately omitted.
Inputs: item_code (required), side, max_orders, depth_quantity, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Each tool targets a distinct resource and action: player, company, country, region, market, work market, battles, battle detail, and battle ranking. Overlaps such as country wars versus battle search are explicitly clarified, and no two tools appear to do the same thing.
Most names follow predictable get_* for retrievals and search_* for list/search operations. A few list-like tools use get_ (e.g., get_player_companies, get_country_wars), which is a minor deviation but still readable.
Thirteen tools are well within a reasonable range and each covers a meaningful slice of the WarEra data domain. The set does not feel bloated or artificially thin.
The read-only surface covers core entities and workflows: players, companies, countries, regions, markets, work, wars, battles, and rankings. Minor gaps exist, such as no write/action tools and no global wealth ranking, but these appear intentional or clearly bounded.
Maintenance
Related MCP Connectors
Read-only search of 2,189 AI agent tools (MCP servers, x402 APIs) with daily automated trust scores.
Web search, browser automation, scraping, crawling and CAPTCHA solving for AI agents.
Free web search, 130M+ business records, local search, weather and travel data for AI agents.
Data and tools for your AI agent: company, people, SEO, social and EU register data. Pay per call.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides AI assistants with real-time access to Warframe game data including world state, item stats, market prices, drop tables, builds, crafting, and farming optimization through 19 tools.121961 npm1MIT
- FlicenseAqualityNot gradedmaintenanceEnables AI assistants to look up World of Warcraft character information and reputation from Blizzard's Armory website.21-
- AlicenseAqualityCmaintenanceEnables AI agents to search EVE Online items, validate ship fits against fitting limits, compute combat stats like DPS and EHP, and retrieve Jita market prices.4MIT
- AlicenseNot gradedqualityDmaintenanceExposes Neptune's Pride games as read-only tools so an AI agent can inspect game status, threats, research, players, empire economy, and messages, and detect changes such as incoming attacks, lost stars, completed research, low cash, and looming turn deadlines. This lets an agent poll games on a schedule and alert the player (e.g. over Telegram) only when something needs attention.MIT