weconnect_mvp
Provides an MCP interface to interact with Volkswagen vehicles, allowing AI assistants to query vehicle status (battery, doors, climate, position) and execute commands (lock, unlock, start climatization).
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., "@weconnect_mvpWhat vehicles are available?"
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.
weconnect_mvp — MCP Server for Connected Vehicles via Tibber
A developer-focused server that exposes vehicle data via a Model Context Protocol (MCP) interface. Originally built for Volkswagen vehicles — but since moving to the Tibber Data API backend, it isn't limited to VW: Tibber's vehicle integration is built on Enode, which covers 30+ EV brands (VW Group included), so any vehicle paired to your Tibber account works identically, regardless of make. This project is designed for integration, automation, and experimentation with connected car data.
See It In Action
Access your vehicle's status through AI assistants like Claude Desktop and GitHub Copilot
Related MCP server: Unoffical Polestar 2 MCP
What This Server Can Do
Why Tibber, not VW directly? In May 2026, VW shut down third-party access to its WeConnect
API (new device-attestation requirements open-source projects can't obtain).
This project's original direct integration (the carconnectivity library) stopped working
because of that, so the whole server was redesigned around the read-only
Tibber Data API instead. The old VW-direct code still
exists, unmaintained, on the permanent
carconnectivity branch.
That redesign is a trade-off: it loses most of what the old integration could do (see below), but in exchange it's no longer VW-specific — Tibber's vehicle integration covers 30+ EV brands, so this server now works with any vehicle paired to your Tibber account, not just VW.
The Tibber Data API is read-only and covers only what Tibber's 5 confirmed vehicle capabilities expose: identity (VIN, brand, model, name, online state) plus charging/range (state of charge, target SoC, remaining range, plug status, charging state) for electric vehicles. There is no door/window/tyre/light/climate/GPS/maintenance data, and no remote commands (lock, climate, charging control, lights) at all — Tibber's API has no write endpoints whatsoever.
vehicle_id resolution (VIN/name/license-plate lookup) and the response shape are the same
regardless of make — the brand field just reflects whatever your paired vehicle actually is
(e.g. "Volkswagen" for the vehicle this project was built and verified against).
See the full 51-point comparison against the old VW-direct data, the OAuth2/API research behind
this backend, and the current architecture in ARCHITECTURE.md.
Known Limitations
No license plate data (Tibber API limitation): The Tibber Data API does not provide license plate information, so there's no
license_platefield in any tool response and no way to identify a vehicle by license plate either. This is a limitation of Tibber's API, not this server.No door/window/tyre/light/climate/GPS/maintenance data: Tibber's confirmed capabilities cover only identity and charging/range — see above.
Read-only: No remote commands (lock, climate, charging control, lights) are possible — Tibber's API has no write endpoints at all.
Refresh token rotation: Tibber rotates the refresh token on every use; the token file must be on writable, persisted storage or re-authentication will eventually be required.
Vehicle pairing is manual, outside this server: a vehicle only shows up in
get_vehicles()after the user has paired it to their Tibber account in the Tibber app. This server has no tool to perform or check that pairing — if a vehicle is missing, that's the fix, not a bug here.
Features
MCP Server: Provides a standard MCP interface for accessing vehicle data
Tibber Data API backend — read-only, via Tibber (an official VW integration partner); works despite VW's third-party API block (see What This Server Can Do)
AI Assistant Ready: Works with Claude Desktop, VS Code Copilot, ChatGPT, Claude.ai and other MCP-compatible tools
Cloud Deployable: Ships with
Dockerfile,docker-compose.ymland Railway config for one-command cloud deploymentAPI-Key Authentication: Bearer token auth for secure public HTTP endpoints
Flexible CLI: Multiple transport modes (stdio for local, HTTP for cloud)
Configurable: Credentials via config file or environment variables (for Docker / Railway)
Quick Start
Get up and running in 3 steps:
Install
git clone https://github.com/Smengerl/weconnect_mvp.git cd weconnect_mvp ./scripts/setup.shConfigure — register an OAuth2 client and log in once:
cp src/tibber_config.example.json src/tibber_config.json # edit src/tibber_config.json with your client_id/client_secret python -m weconnect_mcp.cli.tibber_login_cli src/tibber_config.json # one-time interactive loginSee Setting Up Tibber Credentials for where to get the client id/secret and other options.
Connect an AI assistant
./scripts/create_mcp_config.sh claude # Claude Desktop -- copy output to Claude's configRestart Claude Desktop and ask: "What vehicles are available?"
See Connecting AI Assistants below for GitHub Copilot, Microsoft Copilot Desktop, Cline, or a cloud deployment (ChatGPT, Claude.ai, …).
Getting Started
Prerequisites
Python 3.8+
A Tibber account with a vehicle paired to it (any brand Tibber/Enode supports — not just VW, see What This Server Can Do), and an OAuth2 client registered at data-api.tibber.com (see Setting Up Tibber Credentials)
(Recommended) Virtual environment
Installation
Quick Start (Recommended):
Simply run the setup script which handles everything automatically:
git clone https://github.com/Smengerl/weconnect_mvp.git
cd weconnect_mvp
./scripts/setup.shThe script will:
✅ Detect your Python installation
✅ Create a virtual environment at
.venv/✅ Install the project in editable mode (
pip install -e .)✅ Create configuration template
Manual Installation (Alternative):
git clone https://github.com/Smengerl/weconnect_mvp.git
cd weconnect_mvp
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e .For running tests locally, install test extras:
pip install -e ".[test]"Windows-Specific Notes
⚠️ Important for Windows Users:
The setup script automatically detects and avoids Microsoft Store Python (which doesn't work). If you see errors about Python not found:
Install Python from python.org (not Microsoft Store)
Download from python.org
✅ Check "Add Python to PATH" during installation
Disable Microsoft Store Python alias (if you have it):
Settings → Apps → Advanced app settings → App execution aliases
Turn OFF:
python.exe,python3.exe,python3.x.exe
Verify your Python installation:
# Should return a path like: C:\Program Files\PythonXXX\python.exe where python
Setting Up Tibber Credentials
Register an OAuth2 client at https://data-api.tibber.com/clients/manage/ — see
ARCHITECTURE.mdfor the exact scopes to select and redirect URI to use.Provide credentials — two options, and you can mix them (environment variables override the file when both are present):
Option A — file (recommended for Claude Desktop / VS Code Copilot: those launch the server with their own environment, not your shell's, so
exported variables never reach it):cp src/tibber_config.example.json src/tibber_config.json # edit src/tibber_config.json with your client_id/client_secretsrc/tibber_config.jsonis gitignored.Option B — environment variables (recommended for Docker/Railway):
export TIBBER_CLIENT_ID="your-client-id" export TIBBER_CLIENT_SECRET="your-client-secret" export TIBBER_REDIRECT_URI="http://localhost:8515/callback" # optional, this is the default # export TIBBER_TOKEN_PATH="/custom/path/tibber_tokens.json" # optional -- default is an # OS-standard per-user data directory (e.g. ~/Library/Application Support/weconnect-mcp on # macOS), NOT the current directory, so every local MCP client shares one token file by # default. Only set this to deliberately opt out (e.g. isolated test accounts).Run the one-time interactive login (opens a browser; only needs to be done once — the server itself never opens a browser, it only refreshes the resulting token non-interactively).
tibber_login_clitakes the same optional credentials-file argument as the server, with identical file/env precedence — pass it if you used Option A above:python -m weconnect_mcp.cli.tibber_login_cli src/tibber_config.json # Option A (file) python -m weconnect_mcp.cli.tibber_login_cli # Option B (env vars)On success this writes the token to
token_path(from the file,TIBBER_TOKEN_PATH, or its OS-standard per-user default, see above) and lists the vehicle(s) found in your Tibber account. You won't be asked to log in again — every later run just refreshes this token.Start the server — the config file is optional (pass it if you used Option A above):
python -m weconnect_mcp.cli.mcp_server_cli [src/tibber_config.json]
./scripts/create_mcp_config.sh {claude,copilot-desktop,vscode} (see
Connecting AI Assistants)
already generates configs pointing at src/tibber_config.json with a correct "cwd" — no manual
editing of the generated MCP client config needed. If you hand-edit an MCP client config instead,
still give it a "cwd" pointing at this repo, so a relative config.json argument resolves
correctly — but note that token_path's own default no longer depends on cwd at all: it's a
fixed per-user directory (see step 2 above), specifically so that multiple local MCP clients
(Claude Desktop, VS Code Copilot, Claude Code, ...) launching this server with different working
directories still converge on the same cached token instead of each silently getting its own
(which used to make Tibber's rotating refresh_token strand whichever client refreshed second — see
ARCHITECTURE.md for troubleshooting specific error messages:
missing credentials, no cached token, invalid_grant).
Running the Server
The server supports two transport modes depending on the AI agent you want to use:
stdio: When running MCP server locally on the same machine as your AI agent (Claude Desktop, VS Code Copilot)
http: For cloud deployment or when the local AI agent requires this mode (e.g. ChatGPT)
CLI Options
You can start the MCP server using the provided CLI scripts or directly via Python:
1. Starting the server in foreground (with logs to console)
./scripts/start_server_fg.sh2. Starting the server in background (with logs to file)
./scripts/start_server_bg.shIf started in the background, stop the server using the script:
./scripts/stop_server_bg.shAlternatively, kill the process via PID.
3. Starting the server directly via Python
# No config file needed if TIBBER_CLIENT_ID/TIBBER_CLIENT_SECRET are set as env vars:
python -m weconnect_mcp.cli.mcp_server_cli --port 8089
# With a credentials file:
python -m weconnect_mcp.cli.mcp_server_cli src/tibber_config.json --port 8089
./scripts/start_server_fg.shand./scripts/start_server_bg.shboth forward extra arguments, so e.g../scripts/start_server_fg.sh src/tibber_config.json --port 8765works too.
CLI Parameters
The MCP server can be started with several command-line parameters to control its behavior:
Parameter | Default | Description |
| (none) | Path to a Tibber credentials JSON file; optional — env vars alone are sufficient |
|
| Set logging level: |
| (stderr only) | Path to log file (if not set, logs to stderr only) |
|
| Transport mode: |
|
| Port for HTTP mode (only relevant with |
Example:
python -m weconnect_mcp.cli.mcp_server_cli --log-level DEBUG --log-file server.log --transport http --port 8089Connecting AI Assistants
Claude Desktop Integration
Generate your configuration for Claude Desktop with the following script and follow the instructions to add it to your Claude Desktop configuration:
cd /path/to/weconnect_mvp
./scripts/create_mcp_config.sh claudeReload Claude Desktop and ask questions like:
"What vehicles are available?"
"Show me my car's battery status"
Example Usage
The screenshots and video below were captured against the old VW-direct (
carconnectivity) backend, before VW blocked third-party access and this project moved to Tibber — kept here for illustration. See MCP Tool & Prompt Reference below for what actually works today: battery status and charging status still work exactly like this; vehicle position and starting/stopping a charging session do not (Tibber has no position data at all, and no write endpoints).
Check battery status and state of charge (still works today):

Get complete vehicle status (today: identity + battery/charging only, no doors/climate/position):

Interactive demo video (recorded against the old carconnectivity backend):
GitHub Copilot (VS Code) Integration
Generate your configuration for GitHub Copilot with the following script and follow the instructions to add it to your VS Code settings:
cd /path/to/weconnect_mvp
./scripts/create_mcp_config.sh vscodeRestart VS Code and verify installation by typing /list in Copilot Chat. Look for tools starting with mcp_weconnect_
Example Usage
Captured against the old
carconnectivitybackend, same caveat as the Claude Desktop screenshots above — the doors/location parts of this workflow don't work with Tibber, only battery/charging status does.
Prepare for a trip - check battery, charging status, doors, and location:

Interactive demo video (recorded against the old carconnectivity backend):
Microsoft Copilot Desktop Integration (untested)
Generate your configuration for Microsoft Copilot Desktop with the following script:
cd /path/to/weconnect_mvp
./scripts/create_mcp_config.sh copilot-desktopCopy the configuration file to Microsoft Copilot Desktop's config directory:
mkdir -p ~/Library/Application\ Support/Microsoft/Copilot
cp tmp/copilot_desktop_mcp.json ~/Library/Application\ Support/Microsoft/Copilot/mcp.jsonRestart Microsoft Copilot Desktop completely and test
Other AI Tools (Cline)
The server uses the standard MCP protocol and works with all MCP-compatible tools.
Cline (VS Code Extension) - Configuration in .vscode/cline_mcp_settings.json:
{
"mcpServers": {
"weconnect": {
"command": "python",
"args": [
"-m",
"weconnect_mcp.cli.mcp_server_cli",
"/path/to/your/config.json"
]
}
}
}Local HTTP Mode
You can also start the server in HTTP mode locally, for programmatic access or to test the cloud setup before deploying.
Port strategy for HTTP mode
Railway / cloud: Railway injects
$PORTautomatically (default in image:8080). No manual configuration needed.Local Docker: Container runs internally on
8080;docker-compose.ymlmaps host port8089→ container port8080. Access viahttp://localhost:8089.Local CLI (no Docker):
start_server_http.shdefaults to port8089. Use a different port only when that port is already in use.Using a non-standard port (
8089) for local Docker/CLI avoids conflicts when multiple MCP servers are running side by side.
Via script (recommended):
# Reads credentials from .env automatically
./scripts/start_server_http.sh # starts on http://localhost:8089 (default)
./scripts/start_server_http.sh 8090 # override port if neededInline (manual override):
MCP_API_KEY=your-secret-key \
TIBBER_CLIENT_ID=your-client-id \
TIBBER_CLIENT_SECRET=your-client-secret \
./scripts/start_server_http.sh 8089The server will then be available at http://localhost:8089.
MCP endpoint:
http://localhost:8089/mcpHealth check:
http://localhost:8089/health
Connecting AI clients (VS Code Copilot, Claude Code) to a local HTTP server:
// VS Code: %APPDATA%\Code\User\mcp.json
{
"servers": {
"weconnect": {
"type": "http",
"url": "http://localhost:8089/mcp",
"headers": { "Authorization": "Bearer <YOUR_MCP_API_KEY>" }
}
}
}// Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"weconnect": {
"url": "http://localhost:8089/mcp",
"headers": { "Authorization": "Bearer <YOUR_MCP_API_KEY>" }
}
}
}MCP Tool & Prompt Reference
This MCP server provides 3 tools and 11 prompts that AI assistants can use. There is no
separate MCP Resources layer: it would have been a 1:1 duplicate of the tools with no added
capability for the clients this project targets (Claude Desktop, VS Code Copilot, Claude Code) —
see src/weconnect_mcp/server/mixins/read_tools.py for the reasoning. All 3 tools are fully
functional — everything the Tibber Data API doesn't provide (doors, windows, tyres, lights,
climate, GPS position, maintenance, and any remote command) simply has no tool at all, rather than
a tool that always returns an error. get_charging_status can still return {"error": "..."} for
an individual vehicle that resolves but doesn't support charging, separately from the usual
"vehicle not found" case.
Source of truth: The canonical, up-to-date reference — including the exact wording each tool/prompt reports — lives in
src/weconnect_mcp/server/AI_INSTRUCTIONS.mdand insrc/weconnect_mcp/server/mixins/{read_tools,prompts}.py.
MCP Tools
Tool | Description |
| List all vehicles: VIN, name, model (no |
| Manufacturer, model, name, online state, last-seen timestamp, plus a quick energy snapshot (electric range, charging flag, plug-connected flag) |
| Resolved vehicle VIN/name (confirms which vehicle matched, since |
What AI Assistants Can Do
✅ List vehicles and identify them by name or VIN ✅ Read battery level, range, and charging/plug status ✅ Answer "How much charge does my car have?" / "Is it plugged in?" ❌ Cannot read doors, windows, climate, position, tyres, lights, or maintenance data — not available via Tibber ❌ Cannot execute any remote command (lock, climate, charging control, lights) — the Tibber Data API is read-only, full stop
Cloud Deployment
The server ships with a Dockerfile and supports full cloud deployment, enabling connections from web-based AI services such as ChatGPT, Claude.ai, or any other MCP-compatible client.
Architecture
The server connects to Tibber (a non-interactive token refresh, then an initial vehicle-list fetch)
synchronously, once, before it starts serving any request — the same order stdio mode has always
used. There is no separate "still starting" state or error_type for it: by the time /health or
any tool call is reachable at all, that connection attempt has already resolved one way or the
other. (Docker/docker-compose's HEALTHCHECK gives the container a 60s start-period before the
first check even counts, which comfortably covers this.)
If the connection attempt fails — not configured, invalid credentials, the login was never done,
or a network problem — the server still starts, with every tool call (and /health) reporting the
real cause instead of crashing or silently returning an empty result. It also keeps retrying:
whenever a tool call or a /health probe hits the failure, the server attempts to reconnect
(subject to a cooldown that backs off the longer it stays broken, capped at 5 minutes) — so fixing
the underlying problem (finishing the login, correcting TIBBER_CLIENT_ID/SECRET) heals the
deployment on its own, without a restart, the next time either a tool is called or /health is
probed. See "Error Handling" in AI_INSTRUCTIONS.md
for the full list of error_type codes both tool calls and /health report:
{"status": "unavailable", "ready": false, "error_type": "not_configured",
"message": "TIBBER_CLIENT_ID, TIBBER_CLIENT_SECRET not set. ..."}⚠️ Cloud deployment — token bootstrap. The Tibber OAuth login is a one-time interactive step (browser + human click) that cannot run inside a headless container, and Tibber has no
client_credentialsgrant (confirmed live,ARCHITECTURE.md) —client_id/client_secretalone can never mint a fresh access token, so arefresh_tokenmust persist across restarts one way or another. The bridge: runpython -m weconnect_mcp.cli.tibber_login_clilocally first, then paste that run's token file contents into theTIBBER_TOKEN_JSONenvironment variable. On first boot only, the server writes that into the file atTIBBER_TOKEN_PATH(Dockerfile default:/tmp/tibber-tokens/tibber_tokens.json, on thetibber-tokensvolume indocker-compose.yml). Every token refresh after that rewrites the file directly (including Tibber's rotatingrefresh_token) — as long asTIBBER_TOKEN_PATHis on a persisted volume, it survives future restarts andTIBBER_TOKEN_JSONis never read again. Without a volume, each restart re-seeds from the same (increasingly stale) env var, which works until that seed'srefresh_tokenis rotated away — set up a volume for anything beyond quick local testing.
Option A: Railway (recommended)
Railway is a platform-as-a-service that builds and runs your Docker container automatically. It detects the Dockerfile and railway.toml in this repo with zero configuration.
Step 1 – Install Railway CLI and log in
brew install railway # macOS; see https://docs.railway.com/guides/cli for other OSes
railway loginStep 2 – Create project and deploy
cd /path/to/weconnect_mvp
railway init # creates a new Railway project linked to this directory
railway up --detach # builds the Docker image and deploys itStep 3 – Set secret environment variables
Never put credentials in the repository. Set them in the Railway dashboard instead (see the token
bootstrap caveat above before deploying):
railway variables set TIBBER_CLIENT_ID="your-client-id"
railway variables set TIBBER_CLIENT_SECRET="your-client-secret"
railway variables set MCP_API_KEY="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
# First deploy only -- paste in the contents of the token file produced by
# running `python -m weconnect_mcp.cli.tibber_login_cli` locally:
railway variables set TIBBER_TOKEN_JSON="$(cat tibber_tokens.json)"Then, in the Railway dashboard, add a Volume to the service mounted at
/tmp/tibber-tokens (Service → Settings → Volumes) so the token the server writes there survives
redeploys — without it, every redeploy re-seeds from the (increasingly stale) TIBBER_TOKEN_JSON
above, which stops working once Tibber rotates that seed's refresh_token away.
Or go to: railway.com → your project → service → Variables
Step 4 – Get the public URL
railway domain # e.g. https://weconnectmcp-production.up.railway.appStep 5 – Verify
curl https://<your-subdomain>.up.railway.app/health
# → {"status": "ok", "ready": true, "service": "weconnect-mcp"}Every git push followed by railway up redeploys the service.
Option B: Docker (local or any host)
Local test with Docker Compose:
cp .env.example .env # fill in your real credentials
# First run only: seed the token (see the caveat above).
# tibber_login_cli doesn't load .env itself, so export it into the shell first:
set -a && source .env && set +a
python -m weconnect_mcp.cli.tibber_login_cli
echo "TIBBER_TOKEN_JSON=$(cat tibber_tokens.json)" >> .env
docker compose up --buildThe server is then available at http://localhost:8089. The tibber-tokens volume in
docker-compose.yml persists the refreshed token across docker compose restart/rebuilds, so the
tibber_login_cli step above is only needed once, the very first time.
Environment Variables (Cloud / Docker)
Credentials and the API key are passed via environment variables — never put them in the repository:
Variable | Required | Description |
| Yes (or via file) | OAuth2 client id from data-api.tibber.com |
| Yes (or via file) | OAuth2 client secret |
| Optional | Default: |
| Optional | Image default: |
| First boot only | Contents of a token file produced locally by |
| Yes | Bearer token clients must send for authentication |
| Auto | HTTP port (Railway injects this automatically; default: |
| Optional | Comma-separated allowed origins (default: |
Generate a strong API key:
python3 -c "import secrets; print(secrets.token_urlsafe(32))"Connecting AI Clients to the Cloud Server
Once deployed, point any MCP-compatible client at your public URL:
MCP endpoint:
https://<your-host>/mcpAuthentication: HTTP header
Authorization: Bearer <MCP_API_KEY>
Claude.ai:
Settings → Integrations → Add MCP Server → enter URL and header
ChatGPT Custom GPT:
Configure → Actions → select MCP → enter URL and Authorization: Bearer <key>
GitHub Copilot (VS Code) via remote server:
Add to .vscode/mcp.json:
{
"servers": {
"weconnect-cloud": {
"type": "http",
"url": "https://<your-host>/mcp",
"headers": {
"Authorization": "Bearer <MCP_API_KEY>"
}
}
}
}Security
⚠️ Always set MCP_API_KEY – without it the server runs unauthenticated (locally or in the cloud)
⚠️ Never commit .env or src/tibber_config.json – both are gitignored
⚠️ The Tibber token file (tibber_tokens.json or wherever TIBBER_TOKEN_PATH points) contains session tokens – keep it secure
⚠️ Rotate MCP_API_KEY immediately if it was ever accidentally exposed (e.g. pasted into a chat)
⚠️ The /health endpoint is intentionally unauthenticated (required for Railway / Docker health checks)
Testing
Run the test suite with:
./scripts/test.sh
# Run with verbose output
./scripts/test.sh -v
# Show help
./scripts/test.sh --helpTest Structure:
47 tests - Run in ~0.1 seconds, no Tibber account needed (mock adapter + real fixture data)
No slow/real-API tests exist today — the Tibber Data API is read-only, so there's nothing beyond what the mock adapter and the extraction-logic fixtures already cover
For detailed test documentation, see tests/README.md
Contributing
Contributions are welcome! Please see CONTRIBUTING.md and follow the code of conduct.
Additional Documentation
ARCHITECTURE.md - Full Tibber Data API research, the 51-point data comparison against the old VW-direct (
carconnectivity) backend, current adapter architecture, and project historyscripts/README.md - All available scripts and how to use them
scripts/lib/README.md - Python detection library documentation
tests/README.md - Test suite overview
CONTRIBUTING.md - Contribution guidelines
License
This project is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License (CC BY-SA 4.0) — see LICENSE.txt for details or visit http://creativecommons.org/licenses/by-sa/4.0/
Credits
This project was originally built on top of the excellent CarConnectivity library by Till Steinbach, which provided direct VW WeConnect API access before VW blocked third-party clients. That integration lives on, unmaintained, on the permanent carconnectivity branch.
Additional Resources
Available Tools
3 toolsget_charging_statusGet Charging StatusARead-onlyIdempotent
Get charging/plug status for an electric vehicle: whether charging is running right now (is_charging, charging_state), plug-connected flag, target and current state of charge (%), electric range (km), last-seen timestamp -- plus the resolved vehicle's vin/name, since vehicle_id accepts a partial, case-insensitive name. {"error": "..."} means no vehicle matched vehicle_id or it has no charging data; {"error": "server_unavailable", "error_type": ...} means the server itself needs attention instead (e.g. re-authorization). Cached for 5 minutes.
| Name | Required | Description | Default |
|---|---|---|---|
| vehicle_id | Yes | Vehicle identifier (VIN or name, partial names allowed) |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly and idempotent hints, the description discloses meaningful behavior: 5-minute caching, partial/case-insensitive name resolution, the returned vin/name being resolved from that lookup, and the semantic difference between 'no vehicle matched' and 'server_unavailable' errors. This gives an agent a solid model of side effects and edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence contributes: the return field list, the resolution behavior, the two error modes, and caching. It is front-loaded with the core purpose and then layers details in a logical order with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read-only tool with an output schema, the description covers what is returned, how to provide vehicle_id, how errors are surfaced, and the caching behavior. Nothing an agent needs to decide whether to call or how to interpret the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already fully documents vehicle_id with 100% coverage, so the baseline is 3. The description adds extra semantics: it explicitly says matching is partial and case-insensitive, and that the resolved VIN/name will be included in the response. That is value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource — 'Get charging/plug status for an electric vehicle' — and enumerates the exact returned fields (is_charging, charging_state, plug-connected flag, target/current SoC, range, timestamp, resolved VIN/name). This clearly distinguishes it from siblings like get_vehicles and get_vehicle_info, which are about listing vehicles or general info, not charging state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The tool's intended use is clear from the description: you call it whenever you need an EV's charging status. It also provides practical guidance like partial case-insensitive name matching and what each error shape means. It doesn't explicitly mention when to prefer get_vehicle_info instead, but the focused domain makes the appropriate context obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_vehicle_infoGet Vehicle InformationARead-onlyIdempotent
Get a vehicle's identity plus a quick energy snapshot: manufacturer, model, name, VIN, online/connection state, last-seen timestamp, electric range (km), charging flag, plug-connected flag. vehicle_id accepts a VIN or a partial, case-insensitive name -- the response's own vin/name confirm exactly which vehicle matched. {"error": "..."} means no vehicle matched vehicle_id; {"error": "server_unavailable", "error_type": ...} means the server itself needs attention instead (e.g. re-authorization). Cached for 5 minutes. Read-only -- no vehicle setting can be changed and nothing can be started or stopped.
| Name | Required | Description | Default |
|---|---|---|---|
| vehicle_id | Yes | Vehicle identifier (VIN or name, partial names allowed) |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the readOnlyHint and idempotentHint annotations by disclosing caching behavior, read-only guarantees, partial-name case-insensitive matching, and detailed error semantics. It also clarifies that a server-side error means re-authorization may be needed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence contributes: scope, parameter matching semantics, response confirmation, error meanings, caching, and safety. The description is dense but not bloated, and it front-loads the core purpose before adding edge-case behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description does not need to enumerate return fields. It covers the one parameter's semantics, error cases, caching, and the read-only safety profile, making it fully actionable for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents vehicle_id as a VIN or partial name, so the description's baseline is 3. It adds value by noting case-insensitive matching and that the response's own vin/name fields confirm which vehicle matched, which is important when using a partial name.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Get') and resource ('vehicle info') and enumerates exactly what is returned: identity fields plus an energy snapshot. It clearly distinguishes itself from get_vehicles and get_charging_status by describing its combined identity-plus-energy scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly communicates how to use vehicle_id (VIN or partial case-insensitive name) and how to interpret the response as confirmation. It does not explicitly state when to prefer this over get_vehicles or get_charging_status, but the framing 'quick energy snapshot' implies it is not a full charging-status replacement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_vehiclesGet All VehiclesARead-onlyIdempotent
List all vehicles paired to the connected Tibber account (VIN, name, model). Read-only discovery step -- call this first to learn which vehicle_id values (VIN or name; names match by case-insensitive substring) the other two tools accept. Results are cached for 5 minutes. An empty result includes a hint explaining that pairing happens in the Tibber app, not through any tool here. A {"error": "server_unavailable", "error_type": ...} response means the server itself needs attention (e.g. re-authorization), not a bad request.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool read-only and idempotent, and the description adds substantial behavioral context: results are cached for 5 minutes, an empty result explains pairing happens in the Tibber app, and a server_unavailable error means server-side re-authorization rather than a bad request. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every sentence earns its place: the main action, the positioning as a discovery step, caching behavior, empty-result behavior, and error semantics are all packed into a compact, well-structured description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter discovery tool with an output schema, the description is remarkably complete. It covers the key output fields, the caching behavior, the empty-result hint, and error interpretation, leaving no meaningful gap for the agent to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so the baseline is 4. The description cannot add parameter meaning beyond the schema, but it does usefully define how vehicle_id values (VIN or case-insensitive name substring) are represented, which helps the agent use the sibling tools correctly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'List all vehicles paired to the connected Tibber account' and enumerates the included fields (VIN, name, model). It also clearly distinguishes this tool from its siblings by calling it the 'read-only discovery step' that supplies vehicle_id values to the other two tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly instructs the agent to 'call this first' and explains that the purpose is to learn which vehicle_id values the other tools accept. This gives clear when-to-use guidance and routes the agent into the correct workflow without ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
get_charging_status - First observed
get_vehicle_info - First observed
get_vehicles
TDQS
Scored across 3 tools
get_vehicles is clearly the discovery step, but get_vehicle_info and get_charging_status overlap significantly: both return charging flags, plug status, range, and last-seen data. The extra SoC and charging_state details in get_charging_status help, but an agent could easily pick the wrong one for a status query.
All three tools follow a consistent get_<resource> or get_<resource>_<detail> pattern with lowercase snake_case. The naming makes the data hierarchy clear: vehicles, then vehicle info, then charging status.
Three tools is small but appropriate for a narrow read-only MVP focused on vehicle discovery and status. Each tool has a distinct enough purpose, though the set is minimal and leaves no room for operations beyond reads.
The read-only discovery and status workflow is covered: list vehicles, get vehicle info, get charging status. However, there are no control or management operations, and the overlap between get_vehicle_info and get_charging_status leaves some status capabilities split rather than cleanly layered.
Maintenance
Related MCP Connectors
MCP server wrapping the Tesla Fleet API and TeslaMate API
A Model Context Protocol (MCP) server for Selise Blocks Cloud integration
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Related MCP Servers
- AlicenseAqualityAmaintenanceA Model Context Protocol (MCP) server that provides access to your TeslaMate database, allowing AI assistants to query Tesla vehicle data and analytics.18139MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP (Model Context Protocol) server that exposes Polestar 2 vehicle data to AI assistants like Claude. Query your car's battery status, vehicle info, and health data through natural conversation.MIT
- AlicenseAqualityDmaintenanceAn unofficial MCP (Model Context Protocol) server that exposes a Vaillant heat pump's data to AI assistants like Claude. Query outdoor and room temperatures, hot water status, energy consumption, COP estimates, schedules, and diagnostics through natural conversation.4MIT
- AlicenseAqualityBmaintenanceA Model Context Protocol (MCP) server that connects LLMs to vehicle data via the Eclipse Kuksa Databroker, enabling AI assistants to read and write vehicle signals using the standardized COVESA Vehicle Signal Specification.9Apache 2.0