Are Magica's API and MCP available to everyone?

API Mode and MCP are in early access and available to premium users. The feature is still being shaped and behavior may change in future releases.

What is Magica's API Mode and MCP integration?

Magica embeds a small HTTP server that lets you read or write your data (vehicles, trips, refueling, services, statistics, costs) from any device on the same Wi-Fi. The same server exposes MCP (Model Context Protocol) tools for AI assistants like Claude or Codex. Everything stays on the local network, no cloud.

Key characteristics:

  • LAN-only — your data never leaves the phone
  • On-demand — the server runs only when you turn it on
  • Auto-stop — turns itself off after a configurable idle period
  • Secure — every client is authenticated with a Bearer JWT token
  • Scoped — choose Read-only or Read + Write per client

How do I turn on Magica's API server?

Open Magica and go to Account > API Mode, then tap the toggle in the top right. The server URL (e.g. http://192.168.1.42:8080) appears under "Listening on", with a countdown for auto-stop. The server is reachable only over your local Wi-Fi.

The steps:

  1. Open Magica and go to Account > API Mode
  2. Tap the toggle in the top right to start the server
  3. Read the URL shown under "Listening on" (for example http://192.168.1.42:8080)
  4. Below the URL you'll see a countdown showing when the server will auto-stop if idle
ℹ️The server binds to your local Wi-Fi only. It is never reachable from the internet.

How long does the API server stay on without requests?

You can choose how long the server stays on without authenticated requests: 5 minutes (default, for occasional polling), 10/20/30 minutes (development and debug), 1 hour (live coding with an AI via MCP) or "Never" (demos, watch your battery). The setting is persistent.

Available options:

  • 5 minutes (default) — Occasional polling, cron-style automations
  • 10 / 20 / 30 minutes — Development or debug sessions
  • 1 hour — Live coding with an AI assistant via MCP
  • Never — Demos or extended troubleshooting (watch your battery)

How do I authorize a new API client via pairing?

In API Mode tap "Pair a new client": the app shows a 6-digit code with a 60-second countdown. Enter it in the client (or call POST /auth/pair) then tap Approve, Read only or Reject. The resulting token is valid for 30 days.

The pairing flow:

  1. In API Mode tap "Pair a new client"
  2. A sheet appears with a 6-digit code and a 60-second countdown
  3. Enter the code in the client app (or call POST /auth/pair with the code)
  4. A card appears in the sheet with Approve / Read only / Reject buttons
  5. Tap one of the buttons to grant the client the requested access
💡A paired token is valid for 30 days. After that you have to pair the client again.

How do I generate a long-lived API token?

In API Mode tap "Generate manual token", choose a name, device label, mode (read or read+write) and validity (1 day to 1 year). Magica generates the token and shows it once — copy it immediately because it will never be shown again.

The steps:

  1. In API Mode tap "Generate manual token"
  2. Pick a name, device label, mode (read or read + write) and validity (1 day to 1 year)
  3. Tap Generate
  4. Copy the token immediately — it is shown only once
ℹ️Magica never stores a copy of the bearer token in clear text. If you lose it, revoke the old one and generate a new one.

What is the difference between Read and Read+Write API tokens?

A Read token can only list vehicles, trips, refueling, services, statistics and finance data. A Read+Write token adds creating, updating and deleting trips, refueling, services and tags. A Read token calling a write endpoint returns HTTP 403.

The two permission levels:

  • Read — Can list vehicles, trips, refueling, services, statistics, finance data
  • Read + Write — Read access plus creating, updating and deleting trips, refueling, services and tags
ℹ️A Read token calling a write endpoint returns HTTP 403. Always grant the minimum needed.

How do I revoke an API client's access?

The "Active sessions" section in API Mode lists every valid token with name, device, mode and expiry. To revoke a client, tap the red X next to its session and confirm: the revoke is immediate and persistent, even after the app restarts.

The steps:

  1. Tap the red X next to the session
  2. Confirm in the alert
ℹ️The revoke is immediate and persistent — the token cannot be reused even after the app restarts.

How do I use Magica with Claude or other AI assistants via MCP?

Magica speaks MCP, Anthropic's Model Context Protocol. Compatible assistants (Claude, Codex and others) can read your data or log new entries with natural language: "how much did I spend on fuel last month?", "add a refueling of 35 liters", "what maintenance is due in the next 30 days?".

Examples of what you can ask:

  • "How much did I spend on fuel last month?"
  • "Add a refueling of 35 liters at 60.50 euros, full tank"
  • "What maintenance is due in the next 30 days?"
  • "Show me my last 5 trips on a map"
💡Pair the AI client once with a Read token if you only want it to answer questions. Use Read + Write only if you want it to log data on your behalf.

Can I access my Magica data from outside my home network?

Yes, but you build the bridge to the outside yourself. Magica stays LAN-only by design: its server is never exposed to the internet on its own. To reach it from outside you use a third-party tool — a VPN like Tailscale or a tunnel like Cloudflare — that carries the traffic to the phone encrypted. This is a do-it-yourself scenario, not officially supported.

Two things to know before you start:

  • The server runs on the iPhone, and the iPhone goes to sleep — there is no always-on remote access. It works well per session: turn on API Mode, query from outside, turn it off. For a long session set Auto-stop to "Never" (but watch the battery)
  • Magica speaks plain HTTP — so the bridge must provide the encryption. Tailscale and Cloudflare both do. Never open port 8080 on your router with plain port forwarding: you would expose tokens and data in clear text on the internet
ℹ️All of this is set up outside Magica, with third-party tools. The app does not change: it keeps exposing the same local address — you are the one making it reachable from outside.

How do I access Magica remotely with Tailscale?

Tailscale builds an encrypted private network between your devices without touching the router. Install it on the iPhone and on the computer you connect from, signed into the same account: the phone gets a stable address (100.x.y.z) that you use instead of the local IP, reachable from any network, including 4G/5G. It is the simplest route for personal use.

The steps:

  1. Install the Tailscale app on the iPhone and on the device you want to connect from (Mac, PC), signed into the same account on both
  2. In the Tailscale app on the iPhone read the assigned address, like 100.101.102.103
  3. Turn on API Mode in Magica as usual
  4. From the client, use that address instead of the local IP
From the client terminal
export MAGICA="http://100.101.102.103:8080"
curl $MAGICA/health     # works from any network, including 4G/5G
💡To Magica you are "on the local network" even from the other side of the world: everything else in this guide works identically, and the traffic is encrypted by the Tailscale tunnel.

How do I expose Magica with a public HTTPS URL?

Cloudflare Tunnel gives you a public https:// address, handy for wiring up a Claude Desktop Custom Connector or sharing access. It needs an always-on computer on the same Wi-Fi as the phone (a Mac or a Raspberry Pi) to act as the bridge, because cloudflared does not run on iPhone: that computer forwards requests to the iPhone's local address.

Install cloudflared on the bridge computer and start a quick tunnel to the iPhone's local IP (you read it in API Mode under "Listening on"):

1. Install cloudflared (macOS)
brew install cloudflared
2. Quick tunnel to the iPhone (temporary URL, no account)
cloudflared tunnel --url http://192.168.1.45:8080
3. Try the public URL printed on screen
curl https://<random-name>.trycloudflare.com/health
# {"status":"ok"}
💡When remote, keep basic hygiene: read-only tokens when you can, short expiries, revoke as soon as you are done, and turn the server off. Check active sessions now and then to spot access you do not recognise.
ℹ️The trycloudflare.com URL is temporary and changes on every start: fine for testing. For a fixed address (e.g. magica.yourdomain.com) you need a free Cloudflare account and a domain, with cloudflared tunnel login / create / route dns / run. Both REST and MCP pass through the tunnel intact, including the HTTPS that unlocks the Claude Desktop Custom Connector.

What are the security guarantees of Magica's API?

The API is LAN-only — your data never leaves the phone. Every request requires a Bearer JWT; every client must be explicitly approved via pairing or manual token; revocation is immediate and persistent; idle auto-stop turns the server off when not in use.

Security guarantees:

  • LAN-only — your data never leaves the phone
  • Bearer JWT required on every request
  • Every client must be explicitly approved via pairing or manual token
  • Revocation is immediate and persistent
  • Idle auto-stop turns the server off automatically when not in use

Developer reference — REST & MCP

The rest of this page is a self-contained technical reference for integrating Magica into external systems (scripts, dashboards, CRMs, AI assistants). Magica embeds an HTTP server in the iOS app that exposes your vehicle data as a REST API and, over the same process, an MCP (Model Context Protocol) server. This reference is written in English regardless of the page language.

  • Base URL — http://<iphone-ip>:8080, shown under "Listening on" in API Mode (e.g. http://192.168.1.42:8080)
  • Base path — every REST route lives under /api/v1; the MCP endpoint is /api/v1/mcp
  • Encoding — JSON request/response, snake_case keys, dates as ISO 8601 in UTC
  • Authentication — every /api/v1 route requires the header Authorization: Bearer <jwt>
  • Transport — LAN-only, plain HTTP (no TLS), never exposed to the internet
Health check — no auth required
$ curl http://192.168.1.42:8080/health
{"status":"ok"}
ℹ️The server is offline-first and runs on the device. It auto-stops after an idle period (default 5 minutes, configurable up to 1 hour or Never in API Mode). Reinstalling Magica rotates the signing key and invalidates every previously issued token.

Authentication & authorization

Every route under /api/v1 requires a JWT bearer token. There are two ways to obtain one: interactive pairing (a 6-digit code shown in the app; yields a 30-day token) or a manual token generated in API Mode (duration 1 day to 1 year). Tokens are scoped to a mode chosen at issue time.

ModeAllowed
readAll GET routes (vehicles, trips, refills, services, stats, finance) + MCP read tools
readwriteread + POST/PATCH/DELETE on trips, refills, services and tags + MCP write tools
Interactive pairing — request a token (POST /auth/pair)
curl -X POST http://192.168.1.42:8080/auth/pair \
  -H "Content-Type: application/json" \
  -d '{
    "code": "932103",
    "client_id": "my.script",
    "client_name": "Backup Script",
    "device_label": "MacBook",
    "requested_mode": "read"
  }'

# 200 OK
{"pairing_id":"pair_xxx","status":"pending_approval","expires_in":58}
Poll for approval (GET /auth/pair/{pairing_id})
curl http://192.168.1.42:8080/auth/pair/pair_xxx

# 202 -> still pending      403 -> rejected      408 -> code expired (60s)
# 200 -> approved:
{"access_token":"eyJhbGciOiJI...","mode":"read","expires_in":2592000}
401 — missing / expired / revoked token (RFC 7807)
{
  "type": "about:blank",
  "title": "Authentication required",
  "status": 401,
  "detail": "Token has expired"
}
ℹ️A read token calling a write route returns HTTP 403. Paired tokens last 30 days; manual tokens last for the chosen duration. Revoking a session in API Mode invalidates its token immediately and permanently (a persistent blocklist that survives app restarts).

Vehicles

Query params: updated_since=<ISO8601> returns only vehicles changed after the given instant.

MethodPathModeDescription
GET/vehiclesreadList vehicles + metadata (brand, model, fuel, odometer, tank capacity)
GET /api/v1/vehicles
curl -H "Authorization: Bearer $TOKEN" \
  http://192.168.1.42:8080/api/v1/vehicles

{
  "items": [
    {
      "id": "veh_9462755a",
      "brand": "Nissan",
      "model": "Juke N-Connecta Hybrid",
      "fuel_type": "hybrid",
      "current_odometer": {"value": 10950.33, "unit": "km"},
      "fuel_capacity": {"value": 46, "unit": "L"}
    }
  ]
}
ℹ️Take the id (e.g. veh_9462755a) and reuse it as {id} in all per-vehicle routes below.

Trips

List query params: from, to, tag (repeatable, AND), analysis_state, min_distance_km, max_distance_km, updated_since, cursor, limit (1–200, default 50). Route params: cursor, limit (max 2000, default 500), simplify=<meters> for server-side Douglas–Peucker simplification.

MethodPathModeDescription
GET/vehicles/{id}/tripsreadList trips
GET/vehicles/{id}/trips/{trip_id}readTrip detail
GET/vehicles/{id}/trips/{trip_id}/routereadTrip GPS points
POST/vehicles/{id}/tripsreadwriteCreate a manual trip
PATCH/vehicles/{id}/trips/{trip_id}readwriteUpdate safe fields
DELETE/vehicles/{id}/trips/{trip_id}readwriteDelete a trip
POST /vehicles/{id}/trips — ManualTripInput
{
  "started_at": "2026-05-20T08:00:00Z",
  "start_location": {"latitude": 43.7228, "longitude": 10.4017, "label": "Home"},
  "end_location": {"latitude": 43.7711, "longitude": 11.2486, "label": "Florence office"},
  "odometer": {"value": 11020, "unit": "km"},
  "notes": "Client visit",
  "tags": ["work"]
}
PATCH /vehicles/{id}/trips/{trip_id} — editable fields (flat)
{
  "notes": "New note",
  "tags": ["work", "business-trip"],
  "toll_cost": 12.50,
  "parking_cost": 5.00,
  "driving_score": 87
}
ℹ️POST /trips only works when {id} is the app's active vehicle; other vehicles return HTTP 503. PATCH updates only safe fields (notes, tags, toll_cost, parking_cost, driving_score) — distance, speed and route are GPS-derived and immutable.

Refills

type is full or partial; a partial requires parent_refill_id (an existing full). amount.unit is "L" for liquid fuels or "kWh" for electric. cost.currency must match the vehicle base currency (EUR on iOS in v1). PATCH accepts a flat shape: amount_value, cost_value, notes, tags, is_excluded_from_stats, is_primary_fuel.

MethodPathModeDescription
GET/vehicles/{id}/refillsreadList refills
GET/vehicles/{id}/refills/{refill_id}readRefill detail
POST/vehicles/{id}/refillsreadwriteCreate a refill
PATCH/vehicles/{id}/refills/{refill_id}readwriteUpdate a refill
DELETE/vehicles/{id}/refills/{refill_id}readwriteDelete (cascade on partial children of a full)
POST /vehicles/{id}/refills
{
  "date": "2026-05-20T08:30:00Z",
  "type": "full",
  "amount": {"value": 35, "unit": "L"},
  "cost": {"value": 60.50, "currency": "EUR"},
  "odometer": {"value": 11020, "unit": "km"},
  "fuel_type": "gasoline",
  "is_excluded_from_stats": false,
  "notes": "Q8 motorway",
  "tags": ["motorway"]
}
ℹ️DELETE on a full refill cascades to its partial children. A refill created without a location is marked source: "manual"; with a location, "gps".

Services, deadlines & maintenance

List filters: category (cost | maintenance | extraordinary_maintenance, repeatable AND), done, from, to, has_expiration, cursor, limit. Deadlines params: within_days (default 30), include_expired (default true), urgency (low | medium | high | critical, repeatable).

  • category — cost | maintenance | extraordinary_maintenance
  • subtype — oil_change, tires_replacement, insurance, tax, revision, regular_check, traffic_fine, air_filter, cabin_air_filter, oil_filter, fuel_filter, brake_pads, coolant, battery, timing_belt, wheel_alignment, or other (with subtype_custom for iOS-only categories)
  • expiration.type — none | distance_single | distance_recursive | date_single | date_recursive_day | date_recursive_week | date_recursive_month | date_recursive_year
MethodPathModeDescription
GET/vehicles/{id}/servicesreadList events
GET/vehicles/{id}/services/{service_id}readEvent detail
GET/vehicles/{id}/deadlinesreadDeadlines (future projection + overdue)
POST/vehicles/{id}/servicesreadwriteCreate an event
PATCH/vehicles/{id}/services/{service_id}readwriteUpdate an event
DELETE/vehicles/{id}/services/{service_id}readwriteDelete an event
POST/vehicles/{id}/services/{service_id}/completereadwriteMark done + advance recurring deadline
POST /vehicles/{id}/services
{
  "name": "Road tax 2026",
  "category": "cost",
  "subtype": "tax",
  "cost": {"value": 120, "currency": "EUR"},
  "done": false,
  "expiration": {
    "enabled": true,
    "type": "date_recursive_year",
    "next_date": "2026-09-13T00:00:00Z"
  },
  "notes": "Annual road tax"
}
POST /vehicles/{id}/services/{service_id}/complete
{
  "execution_date": "2026-09-12T15:00:00Z",
  "execution_odometer": {"value": 12500, "unit": "km"},
  "cost": {"value": 120, "currency": "EUR"},
  "notes": "Paid at the office"
}
ℹ️complete advances expiration.next_date by one unit only for date_recursive_* / distance_recursive; for *_single types it only sets done=true. /deadlines hides done date_single records (they are history) and returns only events that still need attention.

Tags

  • color — red | blue | green | yellow | orange | purple
  • days_of_week — ISO weekday (1 = Monday … 7 = Sunday)
  • auto_apply — every property is optional; together they form an AND condition
MethodPathModeDescription
GET/vehicles/{id}/tagsreadList tags
GET/vehicles/{id}/tags/{tag_id}readDetail + auto-apply rules + usage
POST/vehicles/{id}/tagsreadwriteCreate a tag
PATCH/vehicles/{id}/tags/{tag_id}readwriteUpdate a tag
DELETE/vehicles/{id}/tags/{tag_id}readwriteDelete (auto-cleared on trips/refills/services)
POST /vehicles/{id}/tags
{
  "title": "Work",
  "color": "blue",
  "auto_apply": {
    "enabled": true,
    "days_of_week": [1, 2, 3, 4, 5],
    "time_range": {"start": "08:00", "end": "19:00"},
    "location": {
      "center": {"latitude": 43.77, "longitude": 11.25},
      "radius_meters": 500
    }
  }
}

Statistics & finance

Choose the period one way at a time: ?period=<key> where key is current_month, current_year, previous_month, previous_year, YYYY-MM, YYYY-Q1…YYYY-Q4, YYYY, last_30d, last_90d, last_365d or all. Alternatively pass an explicit range ?from=<ISO8601>&to=<ISO8601> (wins over period).

MethodPathModeDescription
GET/vehicles/{id}/stats/summaryreadDistance, trips, fuel consumed, CO₂, avg/max speed, driving score
GET/vehicles/{id}/finance/summaryreadTotal spend + 4-category breakdown (fuel/services/tolls/parking) + cost_per_km
GET/vehicles/{id}/finance/breakdownreadDetailed 8-category breakdown (sorted DESC)
GET stats summary for April 2026
curl -H "Authorization: Bearer $TOKEN" \
  "http://192.168.1.42:8080/api/v1/vehicles/$VEH/stats/summary?period=2026-04"

Error model (RFC 7807)

All error responses use Content-Type: application/problem+json with a consistent shape.

StatusWhen
400Malformed body or query (e.g. negative cost.value, type=partial without parent_refill_id)
401Missing / expired / wrongly signed / revoked token
403Valid token but insufficient scope (read token on a write route)
404Resource not found, or scoped to the wrong vehicle
503App in a state that cannot serve the request (e.g. POST /trips on a non-active vehicle)
Example — 400 Invalid request
{
  "type": "about:blank",
  "title": "Invalid request",
  "status": 400,
  "detail": "`amount.value` must be > 0"
}

MCP transport (Streamable HTTP)

The MCP server speaks JSON-RPC 2.0 over POST /api/v1/mcp and implements the MCP Streamable HTTP spec. It uses the same JWT as REST. Supported protocol versions (preferred → oldest): 2025-06-18 (default), 2025-03-26, 2024-11-05 — the server echoes the client's requested version when supported, otherwise falls back to the default. JSON-RPC errors return HTTP 200 with an error envelope; only transport/parse errors return HTTP 400.

  • Mcp-Session-Id — minted on initialize (response header) and echoed by the client on later requests; absent → accepted (curl/script friendly), present-but-unknown → HTTP 404 (signal to re-initialize)
  • OPTIONS /api/v1/mcp — CORS preflight, public (no auth), returns 204 + Access-Control-Allow-Origin: *
  • DELETE /api/v1/mcp — terminates the session (auth required, 204)
  • GET /api/v1/mcp — returns 405 (no server-initiated SSE stream in v1)
  • Notifications (no id field, e.g. notifications/initialized) — return HTTP 202 with an empty body
  • JSON-RPC error codes — -32602 invalid params, -32001 unauthorized, -32003 forbidden, -32004 resource not found, -32603 internal error

Verify the MCP handshake

Before configuring a client, confirm the full handshake works end to end:

OPTIONS → initialize → tools/list
# 1. CORS preflight (what a browser-based client does first)
curl -i -X OPTIONS http://192.168.1.42:8080/api/v1/mcp \
  -H "Origin: https://claude.ai" \
  -H "Access-Control-Request-Method: POST"
# -> HTTP/1.1 204 No Content + Access-Control-Allow-Origin: *

# 2. initialize — opens the session, returns an Mcp-Session-Id header
curl -i -X POST http://192.168.1.42:8080/api/v1/mcp \
  -H "Authorization: Bearer eyJ..." -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
# -> 200 OK + Mcp-Session-Id: <uuid> + JSON with serverInfo.name="Magica"

# 3. tools/list — verify the 11 tools
curl -X POST http://192.168.1.42:8080/api/v1/mcp \
  -H "Authorization: Bearer eyJ..." -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
ℹ️HTTP 401 → check the token. If OPTIONS returns 404, the installed app predates the Streamable HTTP conformance build — update Magica.

MCP tool catalog (11 tools)

8 read tools + 3 write tools. Write tools require a readwrite token and are declared destructive=false, idempotent=false. Tool names are semantic snake_case (not REST mirrors); currency on write tools is implicit (EUR on iOS).

ToolModeDescription
list_vehiclesreadList configured vehicles — call first to discover ids
get_vehicle_healthreadDaily-checkup snapshot: odometer, current month, top deadlines, overdue count
get_monthly_summaryreadMonthly summary + delta vs previous month
get_fuel_analysisreadRefill stats: min/max/avg unit cost, real vs official consumption
get_expenses_breakdownreadDetailed expenses by category, sorted by amount
find_tripsreadSearch trips by address/notes/tags (max 1000 scanned)
get_upcoming_deadlinesreadDeadlines projected forward (default 30 days)
compare_periodsreadA/B comparison with percentage delta
log_refillreadwriteRecord a refill (liters or kWh)
log_servicereadwriteRecord a service / maintenance / cost
update_trip_notesreadwriteUpdate a trip's notes and tags
Tool signatures (? = optional)
list_vehicles()
get_vehicle_health(vehicle)
get_monthly_summary(vehicle, month?)
get_fuel_analysis(vehicle, period?)
get_expenses_breakdown(vehicle, period?)
find_trips(vehicle, query, period?, limit?)
get_upcoming_deadlines(vehicle, days_ahead?, urgency?, include_expired?)
compare_periods(vehicle, period_a, period_b, metric?)

# readwrite
log_refill(vehicle, amount_liters|amount_kwh, total_cost, date?, type?,
           odometer_km?, notes?, tags?, parent_refill_id?)
log_service(vehicle, name, category, cost, subtype?, done?,
            execution_date?, notes?, tags?, expiration?)
update_trip_notes(vehicle, trip_id, notes?, tags?)

MCP resources (3)

Resources are static snapshots the assistant can read without a tool call. resources/list fans out per vehicle: N vehicles → 1 + 2N URIs. All require read mode.

URIContents
magica://vehiclesCompact vehicle list (same payload as list_vehicles)
magica://vehicles/{vehicle_id}/stats/current-monthMonth-to-date: km, trips, fuel cost, total cost, CO₂
magica://vehicles/{vehicle_id}/deadlines/upcomingTop 5 deadlines within 60 days + overdue count

Configure Claude Desktop

Claude Desktop talks MCP only over stdio, so a LAN HTTP server like Magica needs a stdio→HTTP bridge — the Custom Connector UI rejects http:// URLs (it requires HTTPS). Edit the config file at ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows), then fully restart Claude (⌘Q).

Variant A (recommended) — mcp-remote (npm, needs Node)
{
  "mcpServers": {
    "magica": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "http://192.168.1.42:8080/api/v1/mcp",
        "--allow-http",
        "--header",
        "Authorization: Bearer eyJhbGciOiJI..."
      ]
    }
  }
}
Variant B — mcp-proxy (Python: pipx install mcp-proxy)
{
  "mcpServers": {
    "magica": {
      "command": "mcp-proxy",
      "args": [
        "--transport=streamablehttp",
        "http://192.168.1.42:8080/api/v1/mcp"
      ],
      "env": {
        "API_ACCESS_TOKEN": "eyJhbGciOiJI..."
      }
    }
  }
}
ℹ️The --allow-http flag is mandatory: mcp-remote refuses non-HTTPS non-localhost URLs without it. If you see an OAuth 404 / "Unexpected end of JSON input" error, the bearer is almost always expired or signed with an old key (reinstalling Magica rotates the HMAC key) — regenerate a manual token and restart Claude.

Configure Codex, Cursor, Continue & generic clients

Clients with native Streamable HTTP support connect directly with a URL + Authorization header. If your client version only supports stdio, reuse the mcp-remote / mcp-proxy bridge shown for Claude Desktop.

  • URL — http://<iphone-ip>:8080/api/v1/mcp
  • Required headers — Authorization: Bearer <token>, Content-Type: application/json
  • Optional headers — Mcp-Session-Id (echo the value returned by initialize), Accept: application/json, text/event-stream
  • Least privilege — issue a separate manual token per client; use a read token for query-only assistants
Codex CLI — ~/.codex/config.json
{
  "mcpServers": {
    "magica": {
      "url": "http://192.168.1.42:8080/api/v1/mcp",
      "headers": { "Authorization": "Bearer eyJhbGciOiJI..." }
    }
  }
}
Cursor (≥ 0.40) — ~/.cursor/mcp.json
{
  "mcpServers": {
    "magica": {
      "url": "http://192.168.1.42:8080/api/v1/mcp",
      "headers": { "Authorization": "Bearer eyJhbGciOiJI..." }
    }
  }
}
Continue (VS Code) — ~/.continue/config.json
{
  "experimental": {
    "modelContextProtocolServers": [
      {
        "transport": {
          "type": "streamable-http",
          "url": "http://192.168.1.42:8080/api/v1/mcp",
          "requestOptions": {
            "headers": { "Authorization": "Bearer eyJhbGciOiJI..." }
          }
        }
      }
    ]
  }
}