{
  "markdown": "<!-- kaidn-header -->\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/Kaidn-io/kaidn-mcp/main/.github/kaidn-banner-dark.png\">\n    <img src=\"https://raw.githubusercontent.com/Kaidn-io/kaidn-mcp/main/.github/kaidn-banner-light.png\" alt=\"Kaidn\" width=\"520\">\n  </picture>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@kaidn/mcp\"><img src=\"https://img.shields.io/npm/v/@kaidn/mcp?logo=npm&logoColor=white\" alt=\"npm version\"></a>\n  <a href=\"https://registry.modelcontextprotocol.io/v0/servers?search=io.kaidn/kaidn-mcp\"><img src=\"https://img.shields.io/badge/MCP%20registry-io.kaidn%2Fkaidn--mcp-6f42c1\" alt=\"MCP registry\"></a>\n  <a href=\"https://github.com/Kaidn-io/kaidn-mcp/blob/main/LICENSE\"><img src=\"https://img.shields.io/npm/l/@kaidn/mcp?color=blue\" alt=\"license MIT\"></a>\n  <a href=\"https://kaidn.io/docs\"><img src=\"https://img.shields.io/badge/docs-kaidn.io-FF4D00\" alt=\"docs\"></a>\n</p>\n<!-- /kaidn-header -->\n\n# Kaidn MCP\n\n**Model Context Protocol server for the [Kaidn](https://kaidn.io) fraud-scoring API.**\n\nInvestigate fraud in plain English — *\"why was this signup blocked?\"*, *\"what\nelse has this device touched?\"*, *\"what's in the review queue this morning?\"*\n\n- **Evidence, not just a score.** Every reason carries the raw numbers behind\n  it, so a model can explain a verdict rather than guess at it.\n- **Read-only by default.** Nothing changes your tenant unless you opt in.\n- **Quota-guarded.** An agent in a loop cannot spend your month in ten minutes.\n- **Any client.** MCP is an open protocol — stdio locally, Streamable HTTP for\n  remote and hosted agents.\n\n### Requirements\n\nNode.js 18 or newer, and an API key from your [Kaidn dashboard](https://kaidn.io).\n\n---\n\n## Getting started\n\nFirst, install the Kaidn MCP server with your client. Standard config works in\nmost of the tools:\n\n```json\n{\n  \"mcpServers\": {\n    \"kaidn\": {\n      \"command\": \"npx\",\n      \"args\": [\"@kaidn/mcp@latest\"],\n      \"env\": { \"KAIDN_API_KEY\": \"your_key\" }\n    }\n  }\n}\n```\n\n<details>\n<summary><b>Claude Code</b></summary>\n\n```bash\nclaude mcp add kaidn --env KAIDN_API_KEY=your_key -- npx @kaidn/mcp@latest\n```\n</details>\n\n<details>\n<summary><b>Claude Desktop</b></summary>\n\nAdd the standard config to `claude_desktop_config.json`, then restart Claude.\nSettings → Developer → Edit Config opens the file.\n</details>\n\n<details>\n<summary><b>Cursor</b></summary>\n\nSettings → MCP → Add new MCP Server, or add the standard config to\n`.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` for every project).\n</details>\n\n<details>\n<summary><b>VS Code</b></summary>\n\n```bash\ncode --add-mcp '{\"name\":\"kaidn\",\"command\":\"npx\",\"args\":[\"@kaidn/mcp@latest\"],\"env\":{\"KAIDN_API_KEY\":\"your_key\"}}'\n```\n</details>\n\n<details>\n<summary><b>Windsurf</b></summary>\n\nAdd the standard config to `~/.codeium/windsurf/mcp_config.json`.\n</details>\n\n<details>\n<summary><b>Cline</b></summary>\n\nAdd the standard config to `cline_mcp_settings.json` via the MCP Servers icon →\nConfigure MCP Servers.\n</details>\n\n<details>\n<summary><b>Zed</b></summary>\n\nAdd to `settings.json` under `context_servers`, using the same command, args and\nenv as the standard config.\n</details>\n\n<details>\n<summary><b>Anything else</b></summary>\n\nAny MCP client takes a **command**, **args** and an **env** block. Use the\nstandard config above. If the client can only reach the server over the network\nrather than spawning a process, see [Streamable HTTP](#streamable-http).\n</details>\n\n---\n\n## Configuration\n\n| Option | Environment variable | Default | Purpose |\n|---|---|---|---|\n| | `KAIDN_API_KEY` | *required* | Your secret key. Environment only — never a flag, never a tool argument. |\n| | `KAIDN_API_URL` | `https://api.kaidn.io` | API base URL |\n| `--allow-writes` | `KAIDN_MCP_ALLOW_WRITES=1` | off | Register the mutating tools |\n| | `KAIDN_MCP_MAX_QUOTA_CALLS` | `100` | Quota ceiling per process |\n| `--http` | `KAIDN_MCP_TRANSPORT=http` | `stdio` | Serve Streamable HTTP |\n| `--host <addr>` | `KAIDN_MCP_HOST` | `127.0.0.1` | HTTP bind address |\n| `--port <n>` | `KAIDN_MCP_PORT` | `8765` | HTTP port |\n| | `KAIDN_MCP_HTTP_TOKEN` | unset | Require `Authorization: Bearer` on HTTP |\n| `--help` | | | Show usage |\n| `--version` | | | Show the version |\n\n**Precedence:** CLI flags override environment variables.\n\nThe API key is deliberately env-only. A key passed as a flag leaks into process\nlistings and shell history.\n\n---\n\n## Transports\n\n| Transport | Use it for | Endpoint |\n|---|---|---|\n| **stdio** *(default)* | local clients that spawn a subprocess | — |\n| **Streamable HTTP** | remote agents, containers, anything off-machine | `POST /mcp` |\n\n`HTTP+SSE` is deliberately absent: deprecated in the 2025-03-26 spec and sunset\nin June 2026.\n\n### Streamable HTTP\n\n```bash\nnpx @kaidn/mcp@latest --http --port 8765\n```\n\nStateless — a fresh server per request, nothing shared between callers — so it\nsits behind a load balancer without surprises. `GET /health` is unauthenticated\nso an orchestrator can check liveness without holding the token.\n\n---\n\n## Docker\n\n```bash\ndocker build -t kaidn-mcp .\n```\n\n```bash\n# stdio — behaves like the npx invocation\ndocker run -i --rm -e KAIDN_API_KEY=your_key kaidn-mcp\n\n# HTTP — for remote agents\ndocker run --rm -p 8765:8765 \\\n  -e KAIDN_API_KEY=your_key \\\n  -e KAIDN_MCP_TRANSPORT=http \\\n  -e KAIDN_MCP_HOST=0.0.0.0 \\\n  -e KAIDN_MCP_HTTP_TOKEN=your_token \\\n  kaidn-mcp\n```\n\nMulti-stage build, runs as the unprivileged `node` user, with a healthcheck.\n\n---\n\n## Security\n\n**The server holds your API key.** Whoever can reach it can spend your quota, so\nthe defaults are conservative and the guards fail closed rather than warning.\n\n- **Binds `127.0.0.1`, and refuses to start on a wider interface** unless\n  `KAIDN_MCP_HTTP_TOKEN` is set. It stops with an explanation rather than\n  quietly exposing your account.\n- **Read-only by default.** `add_to_list` and `label_outcome` exist only with\n  `--allow-writes`.\n- **`set_config` and `forget_subject` are never exposed**, in any mode. One\n  silently changes the verdict on every future event; the other is irreversible\n  GDPR erasure. Both belong in the dashboard, in front of a human.\n- **Quota ceiling per process**, with remaining budget reported on every costing\n  response. A reservation that would overshoot is refused outright rather than\n  partially spent.\n- **The key never crosses the tool boundary** — not as a parameter, not in\n  output, not in an error.\n\n---\n\n## Tools\n\nTwo things govern every tool: whether it **spends quota**, and whether it\n**changes anything**.\n\n### Read-only — available by default\n\n| Tool | Cost | What it does |\n|---|---|---|\n| `get_stats` | free | Verdict, score and reason rollups over a rolling window. Start here. |\n| `list_events` | free | Scored events, newest first, filterable by verdict or type, searchable by fingerprint or user id |\n| `explain_event` | free | Every check that fired on one event, with the raw evidence |\n| `triage_queue` | free | Everything on `review`, highest score first |\n| `get_config` | free | Effective weights and thresholds for this tenant |\n| `investigate_entity` | 1 row¹ | Enrichment, network reputation and related events for one entity |\n| `check_email` | 1 row | Disposable domain, deliverability, fraud score, abuse history |\n| `check_ip` | 1 row | Proxy, VPN, Tor, datacenter ASN, geo, abuse history |\n| `check_phone` | 1 row | Validity, line type, carrier, fraud score |\n| `score_event` | 1 row | Score a new event (also records it) |\n\n¹ Free when the entity is a `device_id`; enrichment only costs on email or IP.\n\n### Mutating — require `--allow-writes`\n\n| Tool | What it does |\n|---|---|\n| `add_to_list` | Add an entity to the allow or block list |\n| `label_outcome` | Report a confirmed fraud / chargeback / legit outcome |\n\n---\n\n## Worked examples\n\nThe tools are designed to be chained. These are the flows they were built for.\n\n### Morning triage\n\n> **You:** What happened overnight, and what needs me?\n\nThe model calls `get_stats` for the shape of the last 24 hours, then\n`triage_queue` for the events sitting on `review`, then `explain_event` on the\nworst one. You get a ranked list with the reasoning attached, rather than a\ndashboard you still have to read.\n\n### \"Why was this customer blocked?\"\n\n> **You:** Event `evt_8f21c` — a customer says they were wrongly blocked.\n\n`explain_event` returns every check that fired with its raw evidence — the\ndatacenter ASN it matched, how many accounts shared the device, the velocity\ncount. Enough to answer the customer, or to conclude the rule was wrong and\nneeds tuning.\n\n### Working outward from one signal\n\n> **You:** Is `194.x.x.x` a one-off or part of a ring?\n\n`investigate_entity` returns enrichment and network reputation for the IP plus\nevery recent event it appears in. If the same device ids keep recurring, that is\na ring rather than a coincidence.\n\n### Checking a rule change before making it\n\n> **You:** If I dropped the velocity weight, what would stop being blocked?\n\n`get_config` reads the current weights; `list_events` with `verdict: \"block\"`\nshows what is currently caught. The model can tell you which of those hang on\nthe check you are about to weaken.\n\n---\n\n## Error handling\n\nFailures come back as tool errors with a readable message, not exceptions — the\nmodel can act on them.\n\n| You see | Meaning | Fix |\n|---|---|---|\n| `KAIDN_API_KEY is not set` | Server started without a key | Set it in the client's `env` block |\n| `Kaidn error: 401 …` | Key rejected | Rotate or re-copy it from the dashboard |\n| `Kaidn error: 429 …` | Rate limited | Slow down; per-key throttling is by the minute |\n| `Session quota ceiling reached (100/100 …)` | The guard stopped an expensive run | Raise `KAIDN_MCP_MAX_QUOTA_CALLS` deliberately, or restart |\n| `No event <id> in the most recent 200 events` | Event is older than the scan window | Page back with `list_events` using `offset` |\n| `Supply exactly one of email, ip or device_id` | Ambiguous investigation | Ask about one entity at a time |\n| `Refusing to bind <host> without authentication` | Non-loopback HTTP with no token | Set `KAIDN_MCP_HTTP_TOKEN`, or bind `127.0.0.1` |\n\nErrors never contain your API key.\n\n---\n\n## Troubleshooting\n\n**The client shows no tools.**\nCheck the client's MCP log for the startup line. `kaidn-mcp: ready (stdio, …)`\non stderr means the server is up and the problem is on the client side. Nothing\nat all usually means `npx` could not resolve the package or Node is older than 18.\n\n**It starts, then exits immediately.**\nAlmost always a missing `KAIDN_API_KEY`. The message says so on stderr; some\nclients hide stderr, so run it in a terminal to see it.\n\n**`add_to_list` and `label_outcome` are missing.**\nWorking as designed. They need `--allow-writes`.\n\n**`set_config` and `forget_subject` are missing.**\nAlso by design, and they are not available in any mode. See\n[SECURITY.md](SECURITY.md).\n\n**HTTP mode refuses to start.**\nYou bound something other than loopback without a bearer token. That is the\nguard working — the process holds your API key.\n\n**Everything is slow.**\nThe enrichment checks make live upstream calls. `get_stats`, `list_events`,\n`explain_event` and `triage_queue` are free and fast; prefer them when reading\nhistory.\n\n**Check the server independently of the client:**\n\n```bash\nnode dist/index.js --help                 # no key required\nKAIDN_API_KEY=your_key npm start          # should print a ready line\n```\n\n---\n\n## Support\n\n- **Bugs and feature requests:** [GitHub issues](https://github.com/Kaidn-io/kaidn-mcp/issues)\n- **Security:** security@kaidn.io — see [SECURITY.md](SECURITY.md)\n- **Privacy and data handling:** [PRIVACY.md](PRIVACY.md)\n- **The API itself:** [kaidn.io](https://kaidn.io)\n\n---\n\n## Run from source\n\n```bash\ngit clone https://github.com/Kaidn-io/kaidn-mcp.git\ncd kaidn-mcp\nnpm install\nnpm run build\nnpm test\n```\n\n```bash\nclaude mcp add kaidn --env KAIDN_API_KEY=your_key -- node /absolute/path/to/kaidn-mcp/dist/index.js\n```\n\nTo check it starts without a client:\n\n```bash\nKAIDN_API_KEY=your_key npm start\n```\n\nIt prints `kaidn-mcp: ready (stdio, read-only, quota ceiling 100)` to stderr and\nthen waits on stdin — that is the MCP transport, so the silence is correct.\n\n---\n\n## Why the evidence matters\n\nKaidn's engine is rules-first and explainable: every reason carries the raw\nnumbers behind it. A bare score gives a model nothing to reason about, while\n`checks[]` with evidence attached gives it something to explain. That is the\ndifference between `explain_event` being useful and being decorative.\n\n**Rules decide. The model narrates.**\n\n---\n\n## Project\n\n- [CONTRIBUTING.md](CONTRIBUTING.md) — what belongs here, and the guarantees a change must not break\n- [SECURITY.md](SECURITY.md) — reporting, threat model, known limitations\n- [PRIVACY.md](PRIVACY.md) — what passes through, what is stored, what is not\n- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)\n\n## Licence\n\nMIT\n",
  "bytes": 12993,
  "sha": "4b837a83d7581fb58b74358170221b3c825ae4fbad1dba85d39f97f6f46af9ea",
  "repo_slug": "kaidn-io/kaidn-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_kaidn_kaidn_mcp_a67f1d63/readme"
}