Skip to main content
Glama
betterkey-mcp
by CMLabsLLC

betterkey-mcp

Model Context Protocol server that lets AI agents inspect and act on your BetterKey-connected vehicles.

Open-source bridge. You mint the key, you keep the control. The server never calls your vehicle's API directly; every request goes to BetterKey's existing /v1/* HTTP API over HTTPS, authenticated with an API key you mint in the BetterKey iOS app.

Tools

Six tools are exposed over stdio:

Tool

What it does

API-key scope required

list_vehicles

Lists your connected vehicles (id, make, model, color).

telemetry.read

get_vehicle_telemetry

Returns the latest windows/odometer/fuel/battery snapshot with per-signal oemUpdatedAt and an isStale flag.

telemetry.read

get_vehicle_capabilities

Identity + which signals have fresh data right now + known upstream quirks the agent should respect.

telemetry.read

lock_vehicle

Lock the vehicle. Destructive. The agent must obtain explicit human confirmation. Returns confirmation: "confirmed" or "unconfirmed".

vehicle.command

unlock_vehicle

Unlock the vehicle. Destructive, with the same confirmation and confirmation-status semantics as lock_vehicle.

vehicle.command

get_vehicle_location

Current OEM-reported location. Location-sensitive; coordinates are never logged by the server.

vehicle.command

Related MCP server: Tessie MCP Server

What it does not do

  • It does not read your Firestore directly.

  • It does not hold a vehicle-manufacturer OAuth token.

  • It does not call the vehicle manufacturer's API itself.

  • It does not expose any way to bypass BetterKey's ownership, subscription, or rate-limit checks. Those run server-side on every call.

Setup

1. Mint a BetterKey API key

Open the BetterKey app on iOS → Settings → API Keys → Create. Copy the bk_... secret immediately; it is shown only once.

Scopes. A key minted from the iOS app today holds telemetry.read + vehicle.command by default, enough to drive every tool here. If you'd rather keep this bridge read-only (Homebridge-style), mint the key via a direct POST /v1/account/api-keys with {"label":"...","scopes":["telemetry.read"]}. A read-only key calling a command tool returns a tool-level insufficient_scope error naming the missing scope.

2. Register with your MCP host

Claude Desktop. Edit ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "betterkey": {
      "command": "npx",
      "args": ["-y", "betterkey-mcp"],
      "env": {
        "BETTERKEY_API_KEY": "bk_..."
      }
    }
  }
}

Restart Claude Desktop. The six tools should appear in the tool palette.

3. Try it

  • "List my vehicles."

  • "What's the latest telemetry for <make>?"

  • "What can BetterKey tell me about <make> right now?"

  • "Lock <make>." The agent should confirm with you first; a confirmation: "unconfirmed" result means the lock command was accepted by BetterKey but the automaker's acknowledgement is unavailable (the lock may or may not have actuated).

  • "Where is <make>?" Returns latitude/longitude plus the OEM timestamp.

Environment

Variable

Required

Default

Description

BETTERKEY_API_KEY

yes

(none)

A bk_... secret from the BetterKey app. The server refuses to start without one.

BETTERKEY_API_BASE

no

https://betterkey.xyz

Point at https://staging.betterkey.xyz for staging, or http://localhost:8080 when developing against a local BetterKey backend.

Revoking access

Agent-host access to your vehicle data lasts only as long as the API key is valid. To revoke:

  1. In the BetterKey app → Settings → API Keys → delete the key.

  2. All subsequent tool calls from the MCP server will fail with unauthorized.

To rotate: delete the old key, mint a new one, update BETTERKEY_API_KEY in your MCP host config, restart the host.

Known upstream quirks the agent sees

The get_vehicle_capabilities tool returns a knownQuirks list so agents don't over-report:

  • isLockedWebhookUnreliable. The vehicle's upstream lock-state webhook is defective at the provider level; any lock state reported here is last-known, not live.

  • statusEndpointReturns501. There is no engine-run-state (on/off) tool for your vehicle.

If a tool call returns 401 unauthorized, the API key was revoked or regenerated. If it returns 403 with type: "insufficient_scope" and requiredScope: "vehicle.command", the key does not have the scope the called route requires; mint a new key with that scope and retry.

Security notes

  • The API key lives in your MCP host's env; it is never written to tool output or error messages.

  • Zod validates every tool argument before it reaches the HTTP client.

  • All path segments interpolated into URLs go through encodeURIComponent.

  • Timeouts default to 20s with Retry-After honored on 429.

  • fetch is called with redirect: "manual"; redirects fail closed.

Development

git clone https://github.com/CMLabsLLC/betterkey-mcp.git
cd betterkey-mcp
npm install
npm run dev        # run with tsx (no build step)
npm run typecheck  # strict typecheck without emit
npm test           # Node's built-in test runner
npm run build      # emit dist/

Point at a local backend by setting BETTERKEY_API_BASE=http://localhost:8080 and running make simulator-backend in the main BetterKey repo.

License

MIT

Available Tools

6 tools
get_vehicle_capabilitiesGet vehicle capabilitiesA

Describe what BetterKey can currently report for one vehicle: identity (make/model/color), the per-signal freshCapabilities map (true when a fresh reading is available right now), and a knownQuirks list of upstream Smartcar behaviors the agent should respect when reasoning about results.

Inputs: vehicle_id (required).

get_vehicle_locationGet vehicle locationA
Read-only

Fetch the vehicle's current reported location (latitude, longitude, and the OEM timestamp when known). This is sensitive data — the agent host should treat the result accordingly and should not persist coordinates beyond what the user asked for. Returns 503 with a Retry-After hint when the automaker's location pipe is in-flight or temporarily unavailable; retry only when the user asks.

Inputs: vehicle_id (required).

get_vehicle_telemetryGet vehicle telemetryA

Fetch the latest telemetry snapshot (windows, odometer, fuel, battery) for one vehicle. Each returned signal carries oemUpdatedAt from the automaker and an isStale flag (true when the reading is older than 15 minutes). Pass refresh=true to bypass the server's 15-minute cache; most calls should leave it false.

Inputs: vehicle_id (required), refresh.

list_vehiclesList vehiclesA

List the user's Smartcar-connected vehicles in BetterKey. Returns id, display name, make, model, and exterior color. Use the id as the vehicle_id argument to other tools.

Inputs: no named parameters.

lock_vehicleLock vehicleA
Destructive

Lock the vehicle. This physically actuates the car's door locks. The agent MUST obtain explicit human confirmation before invoking this tool. The response includes a confirmation field: 'confirmed' means the automaker acknowledged the command; 'unconfirmed' means BetterKey accepted it but the automaker's acknowledgement is unavailable (the lock may or may not have actuated) — report that honestly rather than assuming success.

Inputs: vehicle_id (required).

unlock_vehicleUnlock vehicleA
Destructive

Unlock the vehicle. This physically actuates the car's door locks and will leave the car unsecured until it is locked again. The agent MUST obtain explicit human confirmation before invoking this tool and should surface a clear reason to the user. The response includes a confirmation field: 'confirmed' means the automaker acknowledged the command; 'unconfirmed' means BetterKey accepted it but the automaker's acknowledgement is unavailable — report that honestly rather than assuming success.

Inputs: vehicle_id (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 6 tools

    Disambiguation4/5

    Each tool maps to a distinct resource plus action, and lock vs unlock vs list are unmistakable. The only candidate for confusion is get_vehicle_telemetry vs get_vehicle_capabilities, both returning per-vehicle diagnostic data, though the descriptions do clarify that one returns readings and the other returns what can be reported.

    Naming Consistency5/5

    All six tools follow a strict verb_noun pattern (list_vehicles, get_vehicle_*, lock_vehicle, unlock_vehicle) with the domain noun consistently suffixed. No mixing of conventions or vague verbs.

    Tool Count5/5

    Six tools is tightly scoped to the server's purpose: enumerate vehicles, read state, read capabilities, and two lock actuation commands. Every tool earns its place and none is redundant.

    Completeness4/5

    The surface covers discovery, telemetry, capabilities, location, and the lock/unlock command pair, which is solid coverage for a Smartcar read/control wrapper. Gaps remain for other common actuations (charging, climate, engine start) and there is no bulk/multi-vehicle read, but agents can work around these.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI assistants to access vehicle data from the DIMO Network, including querying telemetry, executing vehicle commands (lock/unlock, charging), decoding VINs, minting vehicle NFTs, and creating verifiable credentials.
      2
      MIT
    • F
      license
      A
      quality
      D
      maintenance
      Exposes a connected-vehicle OBD-II/telematics platform to AI agents, enabling vehicle health scoring, live data queries, DTC decoding, maintenance prediction, and gated remote commands via a pluggable data layer.
      9
      -
    • A
      license
      A
      quality
      B
      maintenance
      Enables AI assistants to monitor a Hyundai, Kia, or Genesis vehicle's charge, fuel, range, locks, and location, and optionally pre-heat the cabin, lock or unlock doors, and start or stop charging. Reads come from cached Bluelink data with rate-limited refreshes, and all write commands are disabled by default.
      6
      2
      MIT