# Speedmap Connect (MCP server)

> The first identity MCP server designed so a NIN never enters an AI context window. Verify from Claude, ChatGPT, Cursor and VS Code.

Speedmap Connect is a [Model Context Protocol](https://modelcontextprotocol.io) server that lets AI assistants and agents run Speedmap checks on your behalf: a compliance officer reconciling KYC without leaving the chat, a developer wiring verification into an agent from the editor, a bulk job dispatched conversationally.

It uses the same API keys, entitlements, consent rules and billing as the REST API. What changes is what comes back: **assertions only**. Tools return `verified`, `match`, masked names and risk scores, never the NIN, BVN or identity record, so personal data does not land in a model's context, logs or training data.

## Connect

The remote server speaks **Streamable HTTP** and is stateless: every request is a `POST` carrying your key, and no session is kept. Nothing to install.

`POST http://localhost:4000/mcp`

Authenticate with your Speedmap key as a bearer token, `Authorization: Bearer sk_test_...`. Use a sandbox key while you evaluate: test data, no billing. Requests count against the same per-key rate limit as the REST API.

For clients that only run local (stdio) servers, the [`@speedmap/mcp`](https://www.npmjs.com/package/@speedmap/mcp) package bridges to the remote server. It needs Node.js 20 or later and reads your key from `SPEEDMAP_API_KEY`:

```bash
SPEEDMAP_API_KEY=sk_test_... \
SPEEDMAP_MCP_URL=http://localhost:4000/mcp \
npx -y @speedmap/mcp
```

### Claude Code

```bash
claude mcp add --transport http speedmap http://localhost:4000/mcp \
  --header "Authorization: Bearer sk_test_..."
```

### Claude Desktop

Add the server to `claude_desktop_config.json` (Settings → Developer → Edit config), then restart Claude Desktop.

**claude_desktop_config.json**

```json
{
  "mcpServers": {
    "speedmap": {
      "command": "npx",
      "args": [
        "-y",
        "@speedmap/mcp"
      ],
      "env": {
        "SPEEDMAP_API_KEY": "sk_test_...",
        "SPEEDMAP_MCP_URL": "http://localhost:4000/mcp"
      }
    }
  }
}
```

### ChatGPT

Turn on developer mode (Settings → Connectors → Advanced), then add a custom connector:

1. **Server URL:** `http://localhost:4000/mcp`
2. **Authentication:** API key, sent as a bearer token. Paste your `sk_test_` key.
3. Enable the connector in a chat and ask it to verify a test identity.

### Cursor

Add the server to `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` in one project.

**~/.cursor/mcp.json**

```json
{
  "mcpServers": {
    "speedmap": {
      "url": "http://localhost:4000/mcp",
      "headers": {
        "Authorization": "Bearer sk_test_..."
      }
    }
  }
}
```

### VS Code

Add `.vscode/mcp.json` to your workspace. The `inputs` entry makes VS Code prompt for the key and store it securely instead of keeping it in the file.

**.vscode/mcp.json**

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "speedmap-key",
      "description": "Speedmap API key",
      "password": true
    }
  ],
  "servers": {
    "speedmap": {
      "type": "http",
      "url": "http://localhost:4000/mcp",
      "headers": {
        "Authorization": "Bearer ${input:speedmap-key}"
      }
    }
  }
}
```

### Any other client

Any client that supports remote MCP servers over Streamable HTTP works: point it at the URL above with the `Authorization` header. To check your setup from a terminal:

```bash
curl -X POST http://localhost:4000/mcp \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

## Guarantees

1. **Assertions only.** No NIN, BVN or identity record is returned to the model. Results carry a `dashboard_url` where authorized staff can see more.
2. **Purpose is constrained.** `reason` is an enum of the 38 NIMC purpose codes in every tool schema. An agent cannot invent one, and the server instructions tell it to ask the user when the purpose is unclear.
3. **Consent first.** Tools that touch personal data require `consent_obtained: true`, which the assistant may set only after the user confirms consent. Consent is checked before any provider is called, exactly as on the REST API.
4. **Bulk never returns rows.** `bulk_verify` returns a job id and a dashboard link. A developer cannot pull 10,000 records into a chat.
5. **Registry text is data, not instructions.** Text from registries (company names, provider messages) is stripped of control and invisible characters and length-capped before it reaches the model.
6. **Errors do not leak.** Tool errors carry Speedmap's own error code and a scrubbed message, never provider internals or identifiers.
7. **Sandbox and live are separate.** `sk_test_` keys use test data and are free; `sk_live_` keys reach the live registries (NIMC and NIBSS) and are billed.

## Tools

Twelve tools. Verification tools return a short sentence for the model plus the same fields as `structuredContent`.

### verify_nin

**Verify a Nigerian NIN.** Checks an NIN against NIMC. Never returns the identity record.

**Returns:** `reference_id`, `product`, `status`, `match`, `field_matches`, `field_confidence`, masked `subject` and `summary`, `mode`, `dashboard_url`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `method` | string | No | `nin` (raw NIN, financial institutions and government only) or `share_code`. Defaults to `nin`. |
| `nin` | string | No | 11-digit NIN, for method `nin`. |
| `share_code` | string | No | NINAuth share code, for method `share_code`. |
| `reason` | string | Yes | One of the 38 [purpose codes](https://speedmap-delta.vercel.app/docs/compliance#purpose-codes). The assistant must ask if unsure. |
| `consent_obtained` | true | Yes | Set only after the user confirms the data subject consented. |
| `first_name` | string | No | Expected first name. |
| `last_name` | string | No | Expected last name. |
| `date_of_birth` | string | No | Expected date of birth, `YYYY-MM-DD`. |

### verify_bvn

**Verify a BVN.** Match / no-match through NIBSS. No PII is returned.

**Returns:** `reference_id`, `product`, `status`, `match`, `field_matches`, `field_confidence`, masked `subject` and `summary`, `mode`, `dashboard_url`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bvn` | string | Yes | 11-digit BVN. |
| `reason` | string | Yes | One of the 38 [purpose codes](https://speedmap-delta.vercel.app/docs/compliance#purpose-codes). The assistant must ask if unsure. |
| `consent_obtained` | true | Yes | Set only after the user confirms the data subject consented. |
| `first_name` | string | No | Expected first name. |
| `last_name` | string | No | Expected last name. |
| `date_of_birth` | string | No | Expected date of birth, `YYYY-MM-DD`. |

### verify_business

**Verify a company (CAC / KYB).** Looks up a CAC-registered company by RC number, TIN or exact name.

**Returns:** Company name, status, type and registration date in `summary`, plus the fields above. Directors and shareholders need the REST API.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `rc_number` | string | No | RC or BN number, e.g. `RC1234567`. |
| `tin` | string | No | Tax identification number. |
| `company_name` | string | No | Exact registered name. |
| `expected_name` | string | No | Name to check against the register. |

### verify_account

**Verify a bank account name.** NIBSS name enquiry on a 10-digit NUBAN.

**Returns:** Masked account name, bank name and `name_match`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `account_number` | string | Yes | 10-digit NUBAN. |
| `bank_code` | string | Yes | CBN bank code, e.g. `058`. See `list_banks`. |
| `expected_name` | string | No | Name you expect on the account. |

### face_match

**Face liveness and match.** Passive liveness on a selfie, optionally compared with the BVN photo.

**Returns:** Pass/fail and confidence scores only.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `image_url` | string | Yes | HTTPS URL of the selfie. |
| `bvn` | string | No | Compare against the photo on this BVN. |
| `reason` | string | Yes | One of the 38 [purpose codes](https://speedmap-delta.vercel.app/docs/compliance#purpose-codes). The assistant must ask if unsure. |

### check_signals

**Fraud risk signals.** One risk score from SIM swap, BVN changes and the watchlist.

**Returns:** `risk_score`, `risk_level` and `factors`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `phone` | string | No | Mobile number. |
| `bvn` | string | No | 11-digit BVN. |
| `reason` | string | Yes | One of the 38 [purpose codes](https://speedmap-delta.vercel.app/docs/compliance#purpose-codes). The assistant must ask if unsure. |
| `consent_obtained` | true | Yes | Set only after the user confirms the data subject consented. |

### bulk_verify

**Start a bulk job.** Queues many checks of one product. Results are never returned into the conversation.

**Returns:** `job_id`, `status`, `total` and `dashboard_url`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `product` | string | Yes | `nin`, `bvn`, `phone`, `business`, `account` or `signals`. |
| `items` | array | Yes | 1 to 10,000 request bodies, as for the REST API. |
| `name` | string | No | Job label. |

### get_bulk_job

**Bulk job progress.** Progress and outcome counts. Never rows.

**Returns:** The bulk job: `status`, `processed`, `total`, `progress`, `counts`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `job_id` | string | Yes | From `bulk_verify`. |

### get_verification

**Get a verification.** Fetches an earlier check, e.g. to resolve a pending one.

**Returns:** `reference_id`, `product`, `status`, `match`, masked `subject` and `summary`, `dashboard_url`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reference_id` | string | Yes | Starts with `spm_`. |

### request_consent

**Request consent.** Creates a consent request the person approves by link, email or USSD.

**Returns:** `consent_id`, `status`, `authorization_url`, `ussd_code`, `expires_at`.

**Input**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `identifier_type` | string | Yes | `bvn` or `nin`. |
| `identifier` | string | Yes | 11-digit BVN or NIN. |
| `categories` | string | No | Y/N mask, default `YYNN`. |
| `channel` | string | No | `redirect`, `email` or `ussd`. Default `redirect`. |
| `email` | string | No | For the email channel. |
| `reason` | string | Yes | One of the 38 [purpose codes](https://speedmap-delta.vercel.app/docs/compliance#purpose-codes). The assistant must ask if unsure. |

### list_reasons

**List purpose codes.** The 38 NIMC purpose codes accepted as `reason`.

**Returns:** One `key: label` line per code.

_No input._

### list_banks

**List bank codes.** Bank names and CBN codes for `verify_account`.

**Returns:** One `code: name` line per bank.

_No input._

## Example prompts

With a sandbox key, try:

- "Verify NIN share code SPM001 for a loan application and check the surname is Griffin."
- "Is RC1234567 an active company?"
- "Check whether account 0123456789 at GTBank belongs to Clarke Griffin before we pay her."
- "Run a fraud check on BVN 22222222225 with phone 08099990000."

The machine-readable manifest is at [`/.well-known/mcp.json`](https://speedmap-delta.vercel.app/.well-known/mcp.json).

---

Source: https://speedmap-delta.vercel.app/docs/mcp
