{
  "markdown": "<!-- mcp-name: io.github.zepgram/toyota -->\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/zepgram/toyota-mcp/main/docs/toyota-emblem.svg\"\n       alt=\"Toyota\" width=\"76\">\n</p>\n\n<h1 align=\"center\">toyota-mcp — Toyota &amp; Lexus MCP server for Claude and other AI assistants</h1>\n\n<p align=\"center\">\n  <a href=\"https://pypi.org/project/toyota-mcp/\"><img src=\"https://img.shields.io/pypi/v/toyota-mcp\" alt=\"PyPI\"></a>\n  <a href=\"https://github.com/zepgram/toyota-mcp/actions/workflows/ci.yml\"><img src=\"https://github.com/zepgram/toyota-mcp/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <a href=\"https://pypi.org/project/toyota-mcp/\"><img src=\"https://img.shields.io/pypi/pyversions/toyota-mcp\" alt=\"Python\"></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\"></a>\n</p>\n\n**Ask your Toyota anything.** `toyota-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io)\n(MCP) server that connects a **MyToyota** or **MyLexus** Europe account to Claude,\nClaude Code, Cursor, VS Code or any MCP client — read your car's fuel level,\nrange, location, trips and health, and send remote commands (lock, climate,\ncharging) in plain language.\n\n> *\"How much range is left?\"* · *\"Where is the car?\"* · *\"Is it locked?\"* ·\n> *\"What did the last trip consume?\"* · *\"What's my EV share this month?\"* ·\n> *\"Pre-heat the car for 8 am.\"* · *\"Cheapest petrol near the car?\"*\n\n| | |\n|---|---|\n| **Vehicles** | Toyota and Lexus, **Europe only** (Toyota Connected Services / `ctpa-oneapi`) |\n| **Powertrains** | petrol, diesel, full hybrid, plug-in hybrid, electric |\n| **Tools** | 13 read + 11 remote commands + 1 prompt ([full list](#available-tools)) |\n| **Install** | `uvx toyota-mcp` — no clone, no build |\n| **Requires** | a MyToyota/MyLexus account **without MFA**, Python 3.11+ |\n| **Built on** | [pytoyoda](https://github.com/pytoyoda/pytoyoda), the community client for Toyota Europe |\n\n## Requirements\n\n- A **MyToyota Europe** account (the API covers Europe only — North America and\n  Japan use entirely different systems).\n- Sign in with `toyota-mcp login`, or with account credentials in the\n  environment (that path cannot handle MFA/2FA).\n- The vehicle must appear in the MyToyota mobile app.\n- Python 3.11+ and [`uv`](https://docs.astral.sh/uv/) for the zero-install `uvx` launcher\n  (`uvx` fetches a suitable Python by itself).\n\n> **Unofficial API.** Toyota can change or break this API at any time without\n> notice. All API access is isolated behind pytoyoda, which historically absorbs\n> such breakage within days.\n\n## Quickstart\n\n### Sign in once\n\n```bash\nuvx toyota-mcp login\n```\n\nYour browser opens Toyota's own sign-in page. Toyota then redirects to an\naddress the browser cannot follow (`com.toyota.oneapp:/…`) and shows an error —\nthat is expected: copy that address from the address bar and paste it back. The\nsession is saved in your operating system's credential store and refreshed\nautomatically. **Your password is never seen by this program and never written\nto a configuration file.** `uvx toyota-mcp logout` forgets it.\n\n### Claude Desktop\n\nAdd to `claude_desktop_config.json` (macOS:\n`~/Library/Application Support/Claude/claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"toyota\": {\n      \"command\": \"uvx\",\n      \"args\": [\"toyota-mcp\"]\n    }\n  }\n}\n```\n\nAdd options after `toyota-mcp` in `args` — for example\n`[\"toyota-mcp\", \"--addresses\", \"osm\"]` for postal addresses, or\n`[\"toyota-mcp\", \"--read-only\"]` to leave every remote command out.\n\n### Claude Code\n\n```bash\nclaude mcp add --transport stdio toyota -- uvx toyota-mcp\n```\n\n### Cursor, VS Code and other MCP clients\n\nAny client that speaks stdio works with the same shape — command `uvx`,\narguments `[\"toyota-mcp\"]`. In VS Code, add it to `.vscode/mcp.json`; in Cursor,\nto `~/.cursor/mcp.json`.\n\n### First contact: the doctor\n\nBefore wiring anything into an MCP host, check your setup from a terminal:\n\n```bash\nuvx toyota-mcp doctor\n```\n\nIt validates credentials, lists your vehicles, and prints which tools your\nspecific car supports. Exit codes: `0` ok · `2` config · `3` auth ·\n`4` no vehicle · `5` API error · `6` command rejected (`probe`).\n\n## Remote access — the \"Connect\" button\n\nBy default the server runs on stdio, next to the MCP client. Point it at a URL\ninstead and it becomes a remote MCP server with its own OAuth 2.1 authorization\nserver, so a client connects the way it connects to Gmail or GitHub: paste the\nURL, press **Connect**, approve in the browser.\n\n```bash\ntoyota-mcp --http https://toyota.example.com\n```\n\nNothing else to configure — **connecting is signing in to Toyota**:\n\n1. Paste `https://toyota.example.com/mcp` into your client and press Connect.\n2. The client registers itself and sends you to this server's sign-in page.\n3. Enter the MyToyota email and password, then pick which vehicle to use when\n   the account has several.\n4. The client gets its token; the server keeps only Toyota's refresh token.\n\nSigning in to the Toyota account *is* the proof that you own the deployment —\nthere is no shared code and no user database. A server already connected to one\naccount refuses a sign-in with a different one.\n\nToyota's own web login ends on a mobile deep link (`com.toyota.oneapp:/…`) that\na browser cannot follow — on a phone it opens the MyToyota app instead — so the\ncredentials are posted to Toyota by the server you run. They are never written\ndown: only the refresh token Toyota returns is kept. Accounts with two-factor\nauthentication cannot be used, as Toyota's vehicle API does not support them.\n\nRun it on a machine of yours (a home server, a Raspberry Pi), behind a\nTLS-terminating proxy: the server refuses a non-`https://` public URL other\nthan localhost, because bearer tokens must not travel in clear. Registered\nclients and issued tokens are kept in `~/.local/state/toyota-mcp/oauth.json`\n(mode 600) so a restart does not disconnect anyone; access tokens last an hour\nand refresh silently.\n\n### With Docker\n\n```bash\ndocker build -t toyota-mcp --build-arg VERSION=0.3.0 .\ndocker run -d --name toyota-mcp -v toyota-mcp:/data -p 127.0.0.1:8787:8787 \\\n  toyota-mcp --http https://toyota.example.com --host 0.0.0.0\n```\n\nA container has no credential store, so the session and the grants live in\n`/data` — keep it as a volume or signing in again is the price of a restart.\n`TOYOTA_SESSION_FILE` moves the session file elsewhere.\n\n## Authentication\n\nNothing has to be configured before the first use. A server with no account\nconnected still starts and says what to do: over HTTP the Connect flow signs you\nin, otherwise `toyota-mcp login` does. `toyota_list_vehicles` and\n`toyota_select_vehicle` then choose the car from the conversation.\n\nToyota has no third-party API programme: there is no developer portal, no\nper-application token, and the mobile app signs in with your account password.\nThis server therefore offers two ways in, and prefers the one where it never\nholds that password.\n\n**`toyota-mcp login`.** Asks for the MyToyota email and password, signs in to\nToyota, and keeps only the **refresh token** — in your operating system's\ncredential store (Keychain, Windows Credential Locker, Secret Service), or in a\nfile when there is none. The password is never written down, and never lands in\nan MCP host's configuration file.\n\n**Password in the environment.** `TOYOTA_USERNAME` and `TOYOTA_PASSWORD` still\nwork for unattended setups, but most MCP hosts store their `env` block as plain\ntext on disk, so prefer `toyota-mcp login`.\n\n## Configuration\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `TOYOTA_USERNAME` | no | — | MyToyota account email — only for the password sign-in |\n| `TOYOTA_PASSWORD` | no | — | MyToyota account password — only for the password sign-in |\n| `TOYOTA_VIN` | no | — | Pins one vehicle, overriding the choice made at sign-in |\n| `TOYOTA_BRAND` | no | `T` | `T` Toyota, `L` Lexus |\n| `TOYOTA_USE_METRIC` | no | `true` | `false` switches to miles/gallons |\n\nFeatures are command-line options, visible in `toyota-mcp --help` and in your\nhost's `args`:\n\n| Option | Default | Description |\n|---|---|---|\n| `--read-only` | off | register only the read tools — no lock, trunk, lights, climate or charging commands |\n| `--addresses osm\\|fr` | off | turn coordinates into addresses: `osm` worldwide (OpenStreetMap), `fr` France (also enables fuel prices). Off by default because it sends the car's position to that service |\n| `--places SPEC` | — | named places, `\"home=43.6045,1.4440;work=43.6290,1.3630\"`: positions within 200 m are labelled |\n\n```json\n\"toyota\": {\n  \"command\": \"uvx\",\n  \"args\": [\"toyota-mcp\", \"--addresses\", \"osm\", \"--places\", \"home=43.6045,1.4440\"]\n}\n```\n\nA local `.env` file works too (see `.env.example`). Credentials never touch disk\notherwise; tokens live in memory only.\n\n<a id=\"available-tools\"></a>\n\n## Available tools\n\nRead tools are `readOnlyHint: true`. The remote commands below are registered\nunless the server runs with `--read-only`.\n\n| Tool | Example question | Key fields |\n|---|---|---|\n| `toyota_list_vehicles` / `toyota_select_vehicle` | *Which cars? Use the Yaris.* | every vehicle on the account, and which one the tools act on |\n| `toyota_get_vehicle_info` | *What car is this? Is the subscription active?* | model, year, plate, colour, first use, subscriptions, declared remote capabilities |\n| `toyota_get_energy` | *How much range is left?* | fuel %, range (km/mi), battery or an explicit \"not applicable\" note |\n| `toyota_get_charging` | *Is it charging? When is the next scheduled charge?* | plug-in battery %, charging status, EV range, time to full, schedules (PHEV / EV only) |\n| `toyota_get_status` | *Is the car locked? Did I leave the lights on?* | doors/windows/trunk/hood, lock state, lights, rear-seat reminder, overall status |\n| `toyota_get_location` | *Where is the car?* | lat/lon, address (with open data), Google Maps link |\n| `toyota_get_odometer` | *How many km on the clock?* | odometer with unit |\n| `toyota_get_last_trip` | *What did the last trip consume?* | distance, duration, consumption, EV share, hybrid mode split, start/end places |\n| `toyota_get_trips` | *This week's trips?* | individual trips, newest first (≤ 92 days back) |\n| `toyota_get_trip_summary` | *Average consumption over the last 7 days? This month's EV share?* | rolling window or calendar period (`today`, `this_week`, `this_month`, `this_year`), recomputed L/100km, EV distance & time share |\n| `toyota_get_health` | *Any alerts on the car? When was it serviced?* | warning lights, oil indicators, notifications, full service history |\n| `toyota_get_climate` | *Is the pre-heating running? What's the preset?* | remote climate state, target temperature, preset (duration, defrosters, heated seats) |\n| `toyota_find_fuel_stations` | *Cheapest station near the car?* | cheapest stations for a fuel around the car (France, open data) |\n| `toyota_refresh_data` | *I just parked — refresh.* | bounded cloud re-fetch (never wakes the car) |\n\n## Remote commands\n\nRegistered by default; start with `--read-only` to leave them out:\n\n| Tool | Effect | Annotation |\n|---|---|---|\n| `toyota_lock_doors` / `toyota_unlock_doors` | locks / unlocks the doors | reversible / **destructive** |\n| `toyota_lock_trunk` / `toyota_unlock_trunk` | locks / unlocks the trunk only | reversible / **destructive** |\n| `toyota_find_car` | flashes the hazard lights briefly (silent) | reversible |\n| `toyota_sound_horn` | short horn signal | reversible |\n| `toyota_close_windows` | closes the power windows (model-dependent) | reversible |\n| `toyota_start_climate` | starts pre-conditioning with the saved preset (or a given temperature, 15–30 °C by 0.5) — on a hybrid this runs the engine | **destructive** |\n| `toyota_charge_now` | starts charging immediately (PHEV / EV, plugged in) | reversible |\n| `toyota_stop_climate` | stops pre-conditioning | reversible |\n| `toyota_wake_vehicle` | asks the car to report its state now (costs a little 12 V battery) | reversible |\n\nNot every car accepts every command: Toyota answers \"vehicle not supported\"\n(or \"unknown command\") and the tool says so — nothing reaches the car. See\n[docs/architecture.md](docs/architecture.md) for the vocabulary observed so far.\n\nSafety model:\n\n- Every command takes a `confirm` parameter. `confirm=false` (the default)\n  returns a preview and **sends nothing**; the agent is instructed to preview,\n  get the user's agreement, then call with `confirm=true`.\n- Your MCP host still applies its own permission prompt for non-read-only tools.\n- Toyota's acknowledgement is checked: a command it refuses (return code\n  other than `000000`) comes back as `failed` and nothing is polled.\n- The outcome is **verified**: after sending, the server asks the car to\n  report (the same wake request the MyToyota app issues) and polls the\n  reported state for up to 40 s. `verified` means the car reported the new\n  state or a fresh report; `accepted` means Toyota took the command but no\n  change was reported yet — check again in a minute, and `toyota_get_health`\n  shows any message Toyota sent about it (e.g. the car was moving).\n- Commands are rate-limited to one every 10 s.\n\n## Prompt\n\n`vehicle_briefing` (optional `language` argument) asks the model to produce a\nshort status briefing — range, doors and lights, position, last trip, alerts,\ncheapest fuel when the tank is low — from the tools above.\n\n## Addresses and fuel prices (optional)\n\n`--addresses` turns coordinates into addresses on the parked position and on\ntrip start/end points — no account, no key:\n\n| Option | Addresses | Fuel prices |\n|---|---|---|\n| (default) | — | — |\n| `osm` | worldwide, OpenStreetMap Nominatim (throttled to 1 request/s per its usage policy, results cached) | — |\n| `fr` | France, national address base (`api-adresse.data.gouv.fr`) | `toyota_find_fuel_stations`, prices self-reported by stations to `data.economie.gouv.fr` |\n\nEnabling it sends the car's coordinates to that service; nothing is sent\nanywhere otherwise. Address lookups fail open (the answer simply has no\naddress).\n\n## For AI agents\n\nIf you are an AI assistant reading this to decide whether and how to use this\nserver, here is what you need:\n\n**What it is.** A stdio MCP server exposing one MyToyota/MyLexus Europe vehicle.\nEvery tool answers with `structuredContent` matching its output schema.\n\n**Choosing a tool.** `toyota_get_energy` for fuel and range on any powertrain;\n`toyota_get_charging` only for plug-in hybrids and EVs (it errors with an\nexplicit \"not applicable\" otherwise). `toyota_get_status` for doors, windows,\nlocks, lights; `toyota_get_health` for warning lights, oil indicators,\nnotifications and service history. `toyota_get_trips` lists individual drives,\n`toyota_get_trip_summary` aggregates a window or a calendar period — prefer the\nsummary for averages, and pass `period` to match the figures shown in the\nMyToyota app.\n\n**Data is never live by default.** The car uploads telemetry at ignition-off and\nits position when it parks, so every response carries a `freshness` block:\n`fetched_at`, `age_seconds`, `source` (`live` / `cache` / `stale_cache`) and\n`vehicle_reported_at` when Toyota provides the car-side timestamp. When the user\nasks about *current* state, cite that age rather than implying real time. If they\nneed the state as of now, `toyota_wake_vehicle` asks the car to report (it costs\na little 12 V battery, so do not call it routinely).\n\n**Commands.** Every command tool takes `confirm`. Call it with `confirm=false`\nfirst to preview: nothing is sent, and the report shows the current state. Send\n`confirm=true` only after the user explicitly agreed, and never on your own\ninitiative — `toyota_unlock_doors`, `toyota_unlock_trunk` and\n`toyota_start_climate` carry `destructiveHint` (the last one runs the engine on\na hybrid, which is dangerous indoors). The result tells you exactly what\nhappened: `verified` (the car reported the new state), `accepted` (Toyota took\nthe command but the car has not confirmed within the timeout — say so, do not\nclaim success), or `failed` with Toyota's reason. Do not resend on `accepted`;\ncommands are rate-limited to one per 10 seconds.\n\n**Limits worth stating to the user.** Europe only. Self-charging full hybrids\nexpose no traction-battery level — the tools say so explicitly instead of\nguessing. Toyota keeps roughly 12 months of trip history. Not every car accepts\nevery command; Toyota answers \"vehicle not supported\" and nothing reaches the car.\n\n## How fresh is the data?\n\nToyota's cloud is **push-on-event**: the car uploads telemetry at ignition-off\nand its position when it parks. Polling faster returns the same payload, so\nthis server caches snapshots for 5 minutes (15 for health and service data)\nand serializes all upstream calls\n(the gateway rate-limits bursts aggressively).\n\nEvery response carries a `freshness` block:\n\n- `fetched_at` / `age_seconds` — when this server last read Toyota's cloud;\n- `source` — `live`, `cache`, or `stale_cache` (Toyota briefly unavailable,\n  serving last known data instead of failing);\n- `vehicle_reported_at` — the car-side timestamp, when Toyota provides one.\n\n`toyota_refresh_data` exists for the one real gap (you just parked and want the\nnewest position) and is floor-limited to once per minute. It re-reads the\ncloud — it never wakes the car, so it cannot drain the 12V battery.\n\n## Powertrain coverage\n\n| Data | Full hybrid (self-charging) | PHEV / EV | Petrol/diesel |\n|---|---|---|---|\n| Fuel level & range | ✅ | ✅ | ✅ |\n| Doors/windows/locks | ✅ | ✅ | ✅ |\n| Location, odometer, health | ✅ | ✅ | ✅ |\n| Trips incl. **EV share** | ✅ | ✅ | ✅ (no EV share) |\n| Plug-in battery %, charging status, EV range, schedules, charge now | — explicit \"not applicable\" note | ✅ (untested by the author — see docs) | — |\n\nToyota exposes **no traction-battery charge for self-charging hybrids** — it\nonly exists on the in-car display. The tools say so explicitly instead of\nreturning misleading nulls.\n\n## Limitations\n\n- Europe only (`ctpa-oneapi` — Toyota Connected Europe).\n- Accounts with MFA/2FA cannot authenticate.\n- Toyota retains roughly **12 months** of trip history server-side.\n- Lock/door status can lag reality; every answer self-reports its age.\n- `--read-only` removes every remote command.\n\n## Troubleshooting\n\n| Message | What it means |\n|---|---|\n| `MyToyota sign-in failed…` | Wrong credentials, or MFA is enabled on the account. Login pauses 60 s between attempts. Run `uvx toyota-mcp doctor`. |\n| `No saved session and no credentials…` | Run `uvx toyota-mcp login`, or set `TOYOTA_USERNAME` / `TOYOTA_PASSWORD`. |\n| A saved session stops working | Toyota can invalidate it (password change, session revocation). Run `uvx toyota-mcp login` again. |\n| `…rate-limiting or temporarily unavailable…` | Transient — NOT an auth problem. The gateway 429s freely; retry in a minute. |\n| `Toyota appears to have changed this API endpoint…` | Toyota migrated a route. Update toyota-mcp / pytoyoda. |\n| `No parked location has been reported…` | The car has never pushed a position (or lacks the capability). |\n| `No vehicles are attached to this MyToyota account.` | Pair the car in the MyToyota mobile app first. |\n| `…does not know that remote command (CTP-REMOTE-40006)` | The command string is not in Toyota's current vocabulary (observed for `find-vehicle`, `engine-start`, `engine-stop`, `hazard-off`, `headlight-off`). Nothing reached the car. |\n| `…does not support that remote command (CTP-REMOTE-40041)` | Toyota knows the command but this car lacks the feature (observed for `headlight-on` on a 2026 Corolla). Nothing reached the car. |\n\n## Security & privacy\n\n- With `toyota-mcp login` the password never reaches this program: only a\n  refresh token is kept, in the operating system's credential store. With the\n  environment path the password is held as a `SecretStr` and never logged.\n- GPS coordinates, VINs and payloads are never written to logs — pytoyoda's\n  debug logging (which dumps full HTTP exchanges) is disabled; only warnings\n  reach stderr.\n- No tokens or snapshots are persisted to disk.\n- `doctor --dump` output is recursively redacted, but review it manually before\n  sharing.\n\n## Contributing\n\n[docs/architecture.md](docs/architecture.md) describes the layers, the contracts\n(freshness, verification, privacy) and what to touch to add a tool, a command or\na provider. Changes are tracked in [CHANGELOG.md](CHANGELOG.md).\n\n## Development\n\n```bash\ngit clone https://github.com/zepgram/toyota-mcp && cd toyota-mcp\nuv sync                             # version derives from the git tag (hatch-vcs)\nuv run pytest                       # 127 tests, no network\nuv run ruff check && uv run ruff format --check\nuv run mypy src tests\n```\n\nTests fake Toyota at pytoyoda's own `controller_class` seam and exercise the\nreal parsing pipeline against anonymized payloads — CI never touches Toyota.\nPre-release, run the live smoke tests with real credentials:\n\n```bash\nuv run pytest -m live\n```\n\nCheck which raw command strings your car's backend accepts — Toyota's vocabulary\ndiffers from the documented one (pytoyoda#274) and only a live probe settles it.\nEvery command is physical, so run it next to the car:\n\n```bash\nuvx toyota-mcp probe headlight-on --watch lights\nuvx toyota-mcp probe find-vehicle --beeps 2\n```\n\n### Releasing\n\nThe version is the git tag — nothing to edit. Tagging publishes the package\n(PyPI trusted publishing, `pypi` environment) and creates the GitHub release\nfrom the matching `CHANGELOG.md` section:\n\n```bash\ngit tag v0.1.0 && git push origin v0.1.0\n```\n\nDebug interactively with the MCP Inspector:\n\n```bash\nnpx @modelcontextprotocol/inspector uvx toyota-mcp\n```\n\nNote for contributors (and their coding agents): this project uses MCP Python\nSDK **v2** — `MCPServer`, `mcp.server.mcpserver.Context`, `ToolError`. Most\ntutorials still show v1's `FastMCP` imports, which no longer exist.\n\n## Related projects\n\n- [pytoyoda](https://github.com/pytoyoda/pytoyoda) — the Python client this\n  server is built on; report API breakage there.\n- [ha_toyota](https://github.com/pytoyoda/ha_toyota) — Home Assistant\n  integration on the same library.\n- [tyta](https://github.com/Stopa/tyta) — CLI and MCP server for the same API,\n  with its own HTTP client.\n\n## Keywords\n\nToyota MCP server, Lexus MCP server, MyToyota MCP, Toyota Connected Services\nAPI, Toyota Claude integration, connected car MCP, vehicle telemetry MCP, remote\nlock unlock MCP, EV charging MCP, Model Context Protocol car, Toyota Corolla\nRAV4 Yaris C-HR bZ4X, Claude Desktop car integration, pytoyoda MCP.\n\n## Trademarks\n\nThe Toyota emblem shown above, together with Toyota, Lexus, MyToyota and MyLexus\nand all related logos and marks, are the exclusive property of Toyota Motor\nCorporation and its affiliates. All rights reserved. They appear here only to\nidentify the service this tool connects to. This is an independent, unofficial\nproject: it is not affiliated with, endorsed by, sponsored by, or supported by\nToyota, and Toyota's own terms govern your account and your vehicle.\n\n## License\n\n[MIT](LICENSE) © Benjamin Calef\n",
  "bytes": 23164,
  "sha": "5e0cea1dae1c9715cae188ad87b7c13c9e16f9109ce309a565d0b8a05798f3d8",
  "repo_slug": "zepgram/toyota-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_zepgram_toyota_aa623461/readme"
}