# MCP server

> Reference for the SFW API MCP server. The hosted endpoint and the local package, the analyze_url, analyze_file and get_account tools, their inputs, results, errors and cost.

Rendered page: https://sfwapi.dev/docs/mcp · All docs in one file: https://sfwapi.dev/llms-full.txt

The MCP server gives an AI agent the API as tools: it passes a URL or a file and gets the content level back. This page is the reference. For copy-and-paste setup per client (Claude Code, Cursor, VS Code, Codex, Claude Desktop) see [the MCP server page](https://sfwapi.dev/mcp).

Use it when the agent itself should check media, for example while triaging reports or reviewing a folder of assets. To have an agent write the API into your own application, use [the skill and the Markdown docs](https://sfwapi.dev/docs/ai-agents) instead.

## Two forms

| | Hosted endpoint | Local package |
| --- | --- | --- |
| Address | `https://api.sfwapi.dev/mcp` | `npx -y @sfwapi/mcp` |
| Transport | Streamable HTTP, stateless | stdio |
| Key | `Authorization: Bearer <key>` header | `SFWAPI_API_KEY` environment variable |
| Tools | `analyze_url`, `get_account` | `analyze_url`, `analyze_file`, `get_account` |
| Needs | A client that can send a header | Node.js 20 or newer |

Both use the same key and the same credits as the HTTP API. `analyze_file` exists only locally, because a hosted server cannot read your disk.

### Hosted

```bash
claude mcp add --transport http sfwapi https://api.sfwapi.dev/mcp --header "Authorization: Bearer $SFWAPI_API_KEY"
```

Any other client: point it at the URL and send the header. Each request is independent; there is no session to keep alive. The endpoint has no OAuth sign-in, so clients that can only connect through OAuth cannot use it.

### Local

```bash
claude mcp add sfwapi -e SFWAPI_API_KEY=$SFWAPI_API_KEY -- npx -y @sfwapi/mcp
```

Clients with a JSON config use the same command:

```json title="mcpServers entry"
{
  "mcpServers": {
    "sfwapi": {
      "command": "npx",
      "args": ["-y", "@sfwapi/mcp"],
      "env": { "SFWAPI_API_KEY": "sk_live_..." }
    }
  }
}
```

## Tools

### analyze_url

Analyse an image, GIF or video at a public `http(s)` URL. Our servers download it; nothing is stored.

| Input | Type | |
| --- | --- | --- |
| `url` | string, required | Public URL of the media, up to 2048 characters |
| `block_level` | 1, 2 or 3 | Apply a policy: block at this level or above. The result then has `policy.blocked` |
| `block_sex_objects` | boolean | With `block_level`: also block adult toys and objects |
| `art` | string | With `block_level`: how drawn content is treated. `same_as_photos` (default), `allow`, `block_from_1`, `block_from_2`, `block_from_3` |
| `max_frames` | 1 to 32 | Frames sampled from a GIF or video. Default 5 for a GIF, 10 for a video |
| `include_frames` | boolean | Return the scores of each frame |

### analyze_file

Local package only. Analyse a file on the machine the server runs on. The file is uploaded to the API without its name, analysed in memory and discarded.

| Input | Type | |
| --- | --- | --- |
| `path` | string, required | Path of the file, absolute or relative to the working directory |
| `block_level`, `block_sex_objects`, `art`, `max_frames`, `include_frames` | | As for `analyze_url` |

### get_account

No input. Returns the plan, its limits, the credit balance and this month's usage. Free.

## Results

Every tool answers with one readable line and the full result as structured content.

```text title="Text"
Level 2 (Implied Nudity). Scores 0-100: revealing 96.2, implied nudity 71.5, explicit 8.1. Blocked by the policy (level). 1 credit used, 499 left.
```

The structured content of `analyze_url` and `analyze_file` is the response of [`POST /v1/analyze`](https://sfwapi.dev/docs/analyze), field for field:

```json title="Structured content"
{
  "id": "an_01JD4Q0K7X3Y9Z8W2V1U0T9S8R",
  "media": { "kind": "image", "width": 1200, "height": 800, "frames_analyzed": 1, "duration_ms": null },
  "nudity": { "level": 2, "scores": { "revealing": 96.2, "implied_nudity": 71.5, "explicit": 8.1 } },
  "flags": { "art": { "score": 3.4, "detected": false }, "sex_object": { "score": 0.9, "detected": false } },
  "policy": { "blocked": true, "reason": "level", "matched_level": 2 },
  "usage": { "credits": 1, "balance": 499 },
  "timing_ms": { "upstream": 41, "total": 58 }
}
```

[Levels](https://sfwapi.dev/docs/levels) are 0 Everyday, 1 Revealing, 2 Implied nudity and 3 Explicit. Scores run from 0 to 100 and count above 50. `policy` is present only when `block_level` was passed; see [Policies](https://sfwapi.dev/docs/policies).

`get_account` returns `tier`, `limits`, `balance` (`free`, `paid`, `total`, `free_resets_at`) and `month` (`from`, `requests`, `credits`, `bytes_in`).

## Errors

A failed call comes back as a tool error (`isError: true`) whose text is the API's [error code](https://sfwapi.dev/docs/errors), its message, a link to the docs and the request id:

```text
download_failed: The URL returned 404
Docs: https://sfwapi.dev/docs/errors#download_failed
Request id: 4f1c2a9e7b5d4c3fa1e0b9d8c7f6e5a4
```

A missing or wrong key is different on the hosted endpoint: the HTTP request itself is answered with `401` and the usual error body, so the client reports that it could not connect. The local package refuses to start without `SFWAPI_API_KEY`.

## Cost and limits

- One credit per image, or one per analysed frame of a GIF or video, plus the [size surcharge](https://sfwapi.dev/docs/rate-limits-and-credits) above 10 MB. The same as the HTTP API.
- Connecting, listing the tools, `get_account` and every failed call are free.
- The [rate limits](https://sfwapi.dev/docs/rate-limits-and-credits) of your plan apply to the analyses. An agent that fans out too widely gets `rate_limited` with the time to wait.
- Video needs a paid account.

## The key

Keep the key in `SFWAPI_API_KEY` or in your client's secret prompt, not in a file you commit. Anyone with the key can spend your credits; rotate or revoke it in the [console](https://sfwapi.dev/console). The tool result never contains the key or the media.

## Environment variables of the local package

| Variable | |
| --- | --- |
| `SFWAPI_API_KEY` | Required. Your API key |
| `SFWAPI_BASE_URL` | Optional. API origin, default `https://api.sfwapi.dev` |
