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.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing