Skip to main content
Glama
EVSOCH

evsoch-mcp

by EVSOCH
README.md
# evsoch-mcp

Secured [MCP](https://modelcontextprotocol.io) bridge for EVSocho vehicle ingest.

This process is a **thin client**. It never talks to Supabase directly and must **never** be given `SUPABASE_SERVICE_ROLE_KEY` or other DB credentials. All privileged work (token verification, rate limits, Zod validation, `upsert_vehicle_graph`) stays on the EVSocho Next.js server.

```
Cursor / agent
   │ stdio (MCP)
   ▼
evsoch-mcp  ── HTTPS + Bearer daily token ──►  EVSocho /api/ingest/*
                                                   │
                                                   ▼
                                         server-side auth + DB
```

## Auth model

| Who | Credential | Can |
|-----|------------|-----|
| MCP / partners | **MCP Daily API Key** from admin dashboard (`evs_…`, rotates at midnight IST) | Call `/api/ingest/*` |
| EVSocho server | `SUPABASE_SERVICE_ROLE_KEY` (server env only) | Validate + Draft upsert/delete in DB |
| Admin humans | Supabase Auth + roles | Copy today’s key, review Drafts, **Make Live** in UI |

Never put Supabase service-role keys in MCP env or share them with agents.

Each morning (IST), open **Admin → dashboard** (or API Keys), copy the current MCP Daily API Key into Cursor MCP env as `EVSOCH_INGEST_TOKEN`.

## Tools

| Tool | Purpose |
|------|---------|
| `health_check` | Reachability + API key acceptance |
| `list_brands` | Resolve `brand_id` |
| `create_brand` | Create brand if missing (idempotent by name; research online first) |
| `validate_vehicle_draft` | Server schema check, no write |
| `upsert_vehicle_draft` | Create/update **Draft** only (server DB write) |
| `delete_vehicle_draft` | Soft-delete **Draft** only (server DB write) |
| `get_ingest_schema_notes` | Local contract notes |

`publish_status` is always forced to `Draft`. Live/Verified vehicles cannot be updated or deleted via ingest — use the admin UI.

Failed upserts/deletes return structured fields: `reason`, `code`, `details`, `hint`, `resolution`, and `existing` (when a slug/id conflict exists). MCP surfaces these to the agent — do not treat a bare “Could not save” as complete.

Slug conflicts:

- **Live/Verified** owns slug → keep it; create Draft under a new slug (e.g. `slug-draft`).
- **Draft** owns slug → update with `vehicle_id`, or soft-delete only if that Draft was a mistake.

## Setup

```bash
export PATH="/usr/local/bin:/opt/homebrew/bin:$PATH"
cd /path/to/evsoch-mcp
npm install
cp .env.example .env   # optional; prefer Cursor MCP env
```

Required env:

- `EVSOCH_API_BASE_URL` — HTTPS app origin (http only for localhost)
- `EVSOCH_INGEST_TOKEN` — daily token from admin panel (min length enforced)

## Cursor MCP config

Add to your Cursor MCP settings (example):

```json
{
  "mcpServers": {
    "evsoch": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/evsoch-mcp/src/index.ts"],
      "env": {
        "EVSOCH_API_BASE_URL": "https://www.evsoch.com",
        "EVSOCH_INGEST_TOKEN": "paste-daily-token-here"
      }
    }
  }
}
```

Do not commit tokens. Prefer the Cursor env UI over checking `.env` into git.

## Local run

```bash
export EVSOCH_API_BASE_URL=http://localhost:3000
export EVSOCH_INGEST_TOKEN=your-token
npm start
```

Use stderr for logs; stdout is reserved for MCP JSON-RPC.

## Security model

See [SECURITY.md](./SECURITY.md).

## License

Private — EVSOCH.