ituran-mcp
v3.0.0
Published
Stdio MCP server exposing the Ituran Global V2 API as MCP tools. Mints and refreshes its own access tokens via identity-server-v1 from credentials in env vars; tokens never reach the LLM.
Maintainers
Readme
ituran-mcp
Stdio MCP server that exposes the Ituran Global V2 / Trips / BI & operational Reports / Safety / Maintenance / Advanced / ITC APIs as 118 MCP tools (88 register on the default read-only server — the 5-tool maintenance family is opt-in per deployment). The server holds the user's long-lived credentials in env vars, mints its own access tokens against identity-server-v1, and refreshes them silently. Tokens never reach the LLM.
⚠️ Defaults to QA (
https://api-qa.ituran.com/api/v2). Many tools mutate state (DELETE / PUT / PATCH / POST). To run against the production fleet setITURAN_GLOBAL_BASE_URL=https://api.ituran.com/api/v2explicitly.
Install
Published to the public npm registry as ituran-mcp — no auth, no registry config. npx fetches it on first run and Claude Code launches it over stdio.
Prerequisites
- Node.js ≥ 20 (
node -v) - Claude Code (
claude --version) - An Ituran service account without 2FA — password grant returns an OTP challenge that the MCP can't satisfy on its own. Use a non-OTP service account.
Step 1 — Register the MCP with Claude Code
The examples below register against QA (api-qa.ituran.com). Setting ITURAN_GLOBAL_BASE_URL explicitly — even to the default — is recommended so the environment is obvious in claude mcp get ituran. To switch to prod, see Switching to production.
Linux / macOS:
claude mcp add --scope user ituran \
--env ITURAN_USERNAME='[email protected]' \
--env ITURAN_PASSWORD='your-password' \
--env ITURAN_GLOBAL_BASE_URL=https://api-qa.ituran.com/api/v2 \
-- npx -y ituran-mcpWindows (PowerShell):
claude mcp add --scope user ituran `
--env ITURAN_USERNAME='[email protected]' `
--env ITURAN_PASSWORD='your-password' `
--env ITURAN_GLOBAL_BASE_URL=https://api-qa.ituran.com/api/v2 `
-- npx -y ituran-mcpThe published launcher (start.js) auto-detects the system CA bundle on Linux (/etc/ssl/certs/ca-certificates.crt) and macOS (/etc/ssl/cert.pem) and sets NODE_EXTRA_CA_CERTS for you, so corp TLS to api-qa.ituran.com / api.ituran.com works out of the box. On Windows it leaves the env untouched — if upstream calls fail with UNABLE_TO_GET_ISSUER_CERT_LOCALLY, add --env NODE_EXTRA_CA_CERTS=C:\path\to\corp-ca-bundle.crt.
Switching to production
⚠️ The MCP has 24 mutating tools (create/update/delete — alerts, places, contacts, vehicle-groups, send-command, start-tracking, etc.). They register only with
ITURAN_ALLOW_MUTATIONS=true; on prod, anything an LLM decides to call happens for real on the live fleet. Only flip when the work requires it.
Re-register with the prod URL:
claude mcp remove ituran -s user
claude mcp add --scope user ituran \
--env ITURAN_USERNAME='[email protected]' \
--env ITURAN_PASSWORD='your-password' \
--env ITURAN_GLOBAL_BASE_URL=https://api.ituran.com/api/v2 \
-- npx -y ituran-mcpStart a new Claude Code session for the change to take effect.
Step 2 — Verify
claude mcp listExpected: ituran: npx -y ituran-mcp - ✓ Connected.
Run this from any directory other than a clone of this repo. If your working directory contains a
package.jsonnamedituran-mcp(i.e., the source checkout), npm short-circuits to a local lookup and the bin won't resolve. From~,/tmp, or any unrelated project — fine.
Step 3 — Use it
Tools don't load mid-session, so start a new Claude Code session (claude from any directory). Inside Claude, type /mcp to confirm the tools loaded (88 in the default read-only configuration), then try a call — e.g. "List my top 10 vehicles."
Tools
118 tools across 22 families (117 registerable — 1 degraded tool is hidden by default — and 88 on the default read-only server: the 5-tool maintenance family is default-off, see ITURAN_ENABLE_MAINTENANCE). Full catalog with arguments and descriptions: TOOLS.md.
| Domain | Tools | File |
|---|---:|---|
| Vehicles | 8 | src/tools/vehicles.ts |
| Alerts | 12 | src/tools/alerts.ts |
| Events | 6 | src/tools/events.ts |
| Places | 6 | src/tools/places.ts |
| Contacts | 5 | src/tools/contacts.ts |
| Drivers | 3 | src/tools/drivers.ts |
| Commands | 5 | src/tools/commands.ts |
| Diagnostics | 3 | src/tools/diagnostics.ts |
| Vehicle groups | 6 | src/tools/vehicle-groups.ts |
| Drive-permission groups | 4 | src/tools/drive-permission-groups.ts |
| Exports | 2 | src/tools/exports.ts |
| Info | 3 | src/tools/info.ts |
| Users | 1 | src/tools/users.ts |
| Trips | 8 | src/tools/trips.ts |
| BI reports + composites | 9 | src/tools/reports.ts |
| Operational reports | 7 | src/tools/reports-operational.ts |
| Safety / UBI | 9 | src/tools/safety.ts |
| Maintenance (default-off — opt in via ITURAN_ENABLE_MAINTENANCE) | 5 | src/tools/maintenance.ts |
| Advanced | 2 | src/tools/advanced.ts |
| ITC temperatures | 5 | src/tools/itc.ts |
| Fleet composites + resolver | 3 | src/tools/fleet.ts |
| Named-group planner | 6 | src/tools/groups.ts |
| Total | 118 | |
Tool names follow ituran_<domain>_<verb> and match upstream paths 1:1.
Excluded by design: account/admin endpoints with no AI value (PUT /users/me/password, PUT /users/me/accept-terms, GET/PUT /app/usersettings, GET /internal/testip), and the entire identity-server-v1 surface (login flows, OTP, password-reset, onboarding, refresh-token admin) — those are either credential-handling anti-patterns or require a different auth model. The token manager calls POST /oauth/token internally; nothing else from identity-server-v1 is exposed.
How auth works
ITURAN_USERNAME + ITURAN_PASSWORD (env vars)
↓
token-manager → POST /api/v1/oauth/token (password grant) → access_token + refresh_token
↓
Global V2 tools ← Authorization: Bearer <access_token> (added per-request, internally)- First tool call (per process) is lazy: does a
grant_type=passwordexchange against identity-server using the env-var creds. - Subsequent calls reuse the cached access_token until 30 s before expiry, then refresh in-place via
grant_type=refresh_token(using the in-memory refresh_token that came back with the access_token). - Nothing on disk. Both tokens live only in the process's memory. When the MCP process exits (Claude Code session ends, machine reboots, etc.), they're gone — the next process starts fresh with another password grant. The password is read from env vars at startup; the LLM never sees any of these.
- 2FA accounts are not supported — password grant returns an OTP challenge instead of tokens, and the MCP can't complete login without a human in the loop. Use a service account that doesn't require 2FA.
Configuration
| Var | Required | Default | Purpose |
|---|---|---|---|
| ITURAN_USERNAME | yes | — | Username for POST /oauth/token password grant |
| ITURAN_PASSWORD | yes | — | Password for POST /oauth/token password grant |
| ITURAN_APP_ID | no | unset | Optional integer included in the password-grant body |
| ITURAN_GLOBAL_BASE_URL | no | https://api-qa.ituran.com/api/v2 | Override to prod (https://api.ituran.com/api/v2) for the live fleet |
| ITURAN_IDENTITY_BASE_URL | no | derived from global URL | Override only if identity host diverges from global |
| ITURAN_ALLOW_MUTATIONS | no | false | Gate for write tools. When unset/false the server is read-only — the 24 mutating tools (create/update/delete, send-command, start-tracking, share-drive) are not registered at all (no catalog weight, no dead-end calls). Set true to register and enable writes. |
| ITURAN_ENABLE_MAINTENANCE | no | false | Opt-in for the default-off maintenance family (5 read tools). Maintenance data is per-customer — accounts that don't maintain it get empties or stale schedules read as "overdue service" — so unless enabled the module is invisible: no tools registered and zero mentions in prompts, instructions, or served resources (absence, never "disabled"). Naming maintenance in an explicit ITURAN_TOOL_FAMILIES list also enables it (the explicit list is authoritative). |
| ITURAN_PLANNER_INLINE_THRESHOLD | no | 10 | Planner selects at/under this count inline their full rows (0 disables). |
| ITURAN_DRIVERS_CACHE_TTL_SECONDS | no | 300 | TTL for the driver-code→name registry cache behind ituran_safety_rank_drivers (0 disables). |
| ITURAN_CONFIRM_DESTRUCTIVE | no | false | With mutations enabled: ask the user for a per-call confirmation (MCP elicitation) before any destructive tool (deletes, live device commands) executes. Fail-closed: when the connected client does not support elicitation, destructive calls are refused (destructive_confirmation_unavailable) instead of silently proceeding — use the host's own approval controls or unset the flag. |
| ITURAN_MAX_RESULT_CHARS | no | 24000 | Size-guard ceiling (chars) for tool results — summarizers/trimming engage above it. Match it to your host's inline budget so the server's smart summaries run before the host's blunt head/tail slice (integer ≥ 2000). |
| ITURAN_VERIFY_CREDENTIALS_AT_BOOT | no | true | Mint a token at startup: a wrong password / 2FA-gated account exits(1) with an authentication failed message before the transport connects (an attributable boot failure instead of dead tools mid-conversation). Transient identity trouble warns and continues. Costs one identity round-trip per process start; set false to restore the fully-lazy first mint. |
| ITURAN_REQUEST_TIMEOUT_MS | no | 30000 | Per-request timeout. Stalled upstream calls abort with a TimeoutError instead of hanging the tool call. |
| ITURAN_MAX_RETRIES | no | 2 | Retries for transient failures (timeout / network / 5xx / 429) on read-only tools (including read-only POSTs like search/reports), with exponential backoff + jitter. Mutating tools are never auto-retried. |
| NODE_EXTRA_CA_CERTS | no | unset | Linux: /etc/ssl/certs/ca-certificates.crt to trust corp TLS chain |
Catalog profiles. The default 88-tool read-only catalog costs ~114KB (~28k tokens) of model-facing definitions per conversation (93 tools / ~117KB / ~29k with ITURAN_ENABLE_MAINTENANCE=true); ITURAN_TOOL_FAMILIES shrinks it to what a deployment needs (measured with npm run measure:catalog):
| Profile | ITURAN_TOOL_FAMILIES | Tools | Model-facing catalog |
|---|---|---:|---:|
| Default (read-only) | (unset) | 88 | ~114KB (~28k tokens) |
| Fleet ops | vehicles,trips,safety,fleet,groups,drivers | 36 | ~48KB (~12k tokens) |
| Tracking lite | vehicles,fleet,groups | 16 | ~19KB (~4.8k tokens) |
| Reports only | reports-bi,reports-operational,safety | 25 | ~48KB (~12k tokens) |
Deploying under small-model / security-first hosts (e.g. Comis)
Hosts that target small or local models budget tool results and catalogs much tighter than a frontier-model client. The recommended deployment:
ITURAN_TOOL_FAMILIES=vehicles,fleet,trips,safety,groups,drivers # 37 tools, ~12k catalog tokens
ITURAN_MAX_RESULT_CHARS=14000 # host offloads MCP results >15k chars; ~740 of those are its own
# untrusted-content wrapper — 14000 keeps results inline with the
# server's summaries + guidance markers intact. Small models: ~11000;
# nano: ~7000.- Entrypoint: always
npx -y ituran-mcp(ornode <path>/start.js) — never barenode <path>/dist/server.js. Only the launcher setsNODE_EXTRA_CA_CERTSbefore Node starts; behind a corp TLS chain the bare form fails every call withUNABLE_TO_GET_ISSUER_CERT_LOCALLY, which reads as a network outage. - Read-only posture is model-legible: in read-only mode the tool descriptions, server instructions (front-loaded POSTURE), and the served tool-catalog resource all state that command/write verbs are unavailable — agents are told to say so plainly instead of constructing confirm-then-send flows.
- Elicitation:
ITURAN_CONFIRM_DESTRUCTIVErequires a client that supports MCP elicitation; on clients without it (Comis included) destructive calls are refused, not silently run — rely on the host's own approval controls if you enable mutations. - Parallel tool calls are safe: the server is stateless per call and its token cache is mutex-guarded with a generation-guarded invalidate (a lagging 401 can't discard a freshly minted token). Hosts that serialize stdio servers by default (Comis does) can enable per-server parallelism — in Comis, set
supportsParallelToolCalls: trueon the connection config. - Ask the server what it can do: the
ituran://reference/capabilitiesresource states this deployment's posture (read-only vs mutations), the active environment (production vs QA host), unavailable verbs, and any family filters — agent-readable, byte-stable per deployment.
Safety & resilience. The server starts read-only — write tools are registered only when ITURAN_ALLOW_MUTATIONS=true; on prod it prints a loud banner and reports the mutation state at startup. Every tool accepts an optional format argument ("json" default, or "text" for a compact markdown rendering), and large list responses are size-capped to stay under a context budget (trimmed to valid JSON with a _truncated marker). Tools carry MCP readOnlyHint / destructiveHint / idempotentHint annotations, and thrown errors surface as structured JSON envelopes ({error, status?, error_code?, message}) with remedy hints intact. On a 401 the token is refreshed and the request retried once.
Rotating creds
Tokens never persist, so there's no on-disk state to clean up. To swap accounts:
- Update
ITURAN_USERNAME/ITURAN_PASSWORDin your Claude Code MCP config (claude mcp remove ituran --scope user, then re-add with new creds). - Start a new Claude Code session.
Upgrading from 1.0.0 → 1.1.0+? The old version cached a refresh token at
~/.ituran-mcp/state.json. Since 1.1.0 nothing reads or writes there, but the file is left in place by the upgrade. Runrm -rf ~/.ituran-mcp/once after upgrading to remove the orphan.
Local development (contributors)
Clone the repo, build, and run from source:
git clone https://github.com/ituranlabs/ituran-mcp.git
cd ituran-mcp
npm install
npm run build
ITURAN_USERNAME=... ITURAN_PASSWORD=... node start.jsThe server reads JSON-RPC on stdin and writes responses on stdout. There are no HTTP endpoints — Claude Code launches the binary and talks over the inherited pipes. start.js is a cross-platform launcher (Linux / macOS / Windows) that auto-detects the system CA bundle on Linux and macOS so corp TLS works without extra config.
To register your local checkout instead of the published package:
claude mcp add --scope user ituran-local \
--env ITURAN_USERNAME=... --env ITURAN_PASSWORD=... \
-- node /absolute/path/to/ituran-mcp/start.jsSmoke test (no upstream call — just exercises the JSON-RPC plumbing):
( printf '%s\n%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| ITURAN_USERNAME=fake ITURAN_PASSWORD=fake node dist/server.js 2>/dev/null \
) | head -2Publishing a new version (maintainers)
Published to the public npm registry under the unscoped name ituran-mcp.
# one-time: log in to the public npm registry (needs an npmjs.com account with publish rights)
npm login
# bump version, then publish — `prepublishOnly` runs the build for you
npm version patch # or minor / major
npm publish # public by default (unscoped name)
git push --follow-tagsnpm publish only ships the files listed in "files" in package.json (currently dist/, start.js, README.md, TOOLS.md, LICENSE). Source TypeScript and node_modules are not published. Run npm publish --dry-run first to inspect the exact tarball contents.
License
MIT — see LICENSE.
