Kaidn

npm version MCP registry license MIT docs

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