abusend MCP server
abusend runs a Model Context Protocol server. AI clients such as Claude Code, Claude Desktop, Cursor or VS Code can use it to look at your projects, investigate verdicts and flows, and draft, replay and tune rules with your permissions. Drafted rules are always saved monitoring; enforcing stays a deliberate step.
- URL:
https://<your panel host>/mcp(for examplehttps://abusend.k3s.abuse.ltd/mcp). It is served on the panel host, not on the edge. - Transport: Streamable HTTP, stateless, with JSON responses. Clients send
POST.GETreturns405, because the server keeps no event stream open. - Auth: a personal access token in
Authorization: Bearer abp_…. Without a valid token the server answers401withWWW-Authenticate: Bearer realm="abusend".
1. Create an access token
In the panel, go to Account & security → API & MCP → Personal access tokens → New token and choose:
| Field | Meaning |
|---|---|
| Name | For your own reference, for example "Claude Code laptop". |
| Organization | Limit the token to one organization, or leave it empty to cover all your organizations. |
| Max role | viewer can only read. analyst can also change actions, rules, lists and attributes. admin can additionally apply project-setting fixes (for example allowing an origin). The token never gets more than your own role in an organization. |
| Expires in | 1–365 days (default 90). |
The token (abp_…) is shown once. Store it in your client's secret store or in an
environment variable, never in a repository. You can revoke it at any time on the same page.
Changing your password (or an operator resetting it) revokes all your tokens: create new
ones afterwards.
The same token works for the panel API (/api/panel/v1) with
Authorization: Bearer abp_…, and no CSRF header is needed.
2. Connect a client
Replace https://abusend.example.com/mcp with your panel URL plus /mcp, and abp_… with your token.
Claude Code
claude mcp add --transport http abusend https://abusend.example.com/mcp \
--header "Authorization: Bearer abp_…"
Then run claude mcp list to check the connection. Inside Claude Code, /mcp shows the tools.
Claude Desktop
Claude Desktop's built-in remote connectors expect OAuth. To use an access token, use the
mcp-remote bridge (Node ≥ 18) in claude_desktop_config.json
(Settings → Developer → Edit config):
{
"mcpServers": {
"abusend": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://abusend.example.com/mcp", "--header", "Authorization:${ABUSEND_AUTH}"],
"env": { "ABUSEND_AUTH": "Bearer abp_…" }
}
}
}
The header value goes through an environment variable. Some platforms break args
entries that contain spaces, and the variable avoids that. Restart Claude Desktop afterwards.
Cursor
~/.cursor/mcp.json (global) or .cursor/mcp.json (project):
{
"mcpServers": {
"abusend": {
"url": "https://abusend.example.com/mcp",
"headers": { "Authorization": "Bearer abp_…" }
}
}
}
VS Code (GitHub Copilot agent mode)
.vscode/mcp.json:
{
"servers": {
"abusend": {
"type": "http",
"url": "https://abusend.example.com/mcp",
"headers": { "Authorization": "Bearer abp_…" }
}
}
}
To avoid committing the token, use an input variable. Put
"inputs": [{"type": "promptString", "id": "abusend-token", "password": true}] next to
servers and write "Authorization": "Bearer ${input:abusend-token}".
Any other client
Configure a Streamable HTTP server with the URL and the Authorization header. To check a
token from the command line:
curl -s https://abusend.example.com/mcp \
-H "Authorization: Bearer abp_…" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
3. Tools
Every tool takes project as a project id or its exact name. It can be left out when you
have exactly one project. Results are compact JSON with ids, and long lists are cut
(truncated: true). The tools run the same panel API as the panel, with your role.
| Tool | What it does | Kind |
|---|---|---|
list_projects |
Projects you can access, with organization and role | read |
get_project_status |
Environment, to-dos, last-24h decisions with decided_by, the friction funnel, top rules and signals |
read |
list_actions |
Actions (labels; they have no mode) with token requirement, 24 h outcomes, verification counts and what "Your app decides" does there | read |
get_action |
One action: token settings and the rules in scope with their state (Monitoring / Enforcing / Off) | read |
set_action_token |
Set an action's token requirement (required / optional) and token_missing (review / deny); needs confirmation |
write, idempotent |
search_verdicts |
Flows and verdicts filtered by action, decision, decided_by, rule, check, reason, user, IP, device, risk level, since; paginated. Verdicts show would_decision and would_challenge ({mode, checks}) |
read |
get_flow |
One flow as a timeline: signals (hints), matched rules, verifications with status and time, final decision | read |
get_user |
End user: attributes, list status, devices, recent flows | read |
list_events |
Events your server reported (POST /v1/events), newest first, filtered by user and/or type; paginated. With a user, also totals: per type what event_count / event_sum / event_last give a rule now (1h, 24h, 7d, 30d; last_at) |
read |
get_device |
Device: users, list status, verifications, recent flows | read |
list_networks |
Where traffic comes from: the window's verdicts grouped by client IP, autonomous system or country, with IP data (country, ASN, org, flags — hints), flows, users, devices, decisions, risk and, for IPs, list status; paginated | read |
get_ip |
One client IP: IP data, first/last verdict, the window's decisions, top users and devices, actions, and the allow/block entries naming it (address or CIDR) | read |
list_rules / get_rule |
Rules in evaluation order: condition, outcome and check group (checks: {mode, items: [{check_id, check, weight?}]}), action scope, state (Off / Monitoring / Enforcing), origin (watch for a Monitoring rule Watch created alone), hits, unseen_attributes (attributes the condition reads that were never received, and event.<type> for event types never reported) |
read |
list_rule_templates |
Rule templates, their parameters and the attributes they declare (including many top-ups in a short time and high total in 24 hours on reported events) | read |
list_rule_fields |
Fields and helpers for conditions, including your declared attributes, the event functions event_count(type, window), event_sum(type, window), event_last(type) and your known event types (scope event); filterable |
read |
simulate_rule |
Replay a new, changed or deleted rule on the first verdicts of recent flows: matched, changed decisions, users per day per check (a new "one of these" group is shared out by weight: expected counts), missing-field warnings; items carry challenge: {mode, checks}; tier none · small (< 200: report counts, not shares) · full |
read |
create_rule |
Create a rule from a template or an expression; always saved monitoring; declares the template's attributes and lists unseen_attributes |
write |
update_rule |
Change a rule's name, condition, outcome, check group or scope (its state stays as it is) | write, idempotent |
set_rule_mode |
Set a rule to monitor (Monitoring) or enforce (Enforcing) — the only monitor/enforce switch; enforcing shows the replay and needs confirmation |
write |
set_rule_enabled |
Enable or disable a rule | write, idempotent |
delete_rule |
Delete a rule | write, destructive |
list_checks |
Connected checks with kind, provider, health, on_* settings and 7-day stats |
read |
list_lists |
Allow and block lists | read |
add_to_list |
Add a user, IP/CIDR, device or value, with optional expiry in days and a note | write, idempotent |
remove_from_list |
Remove a value from a list | write, destructive |
list_attributes |
User and context attributes with scope, detected or configured type, and wrong-type counts | read |
set_attribute_type |
Set an attribute's type (for example a timestamp in ms); declaring works before the server sends it | write, idempotent |
list_counters |
Shared counters: definition (source, action or event type, filter, count or sum, group_by), totals for the last hour and 24 hours, the largest groups, the rules reading them | read |
save_counter |
Create a shared counter, or replace the one with this key (counting something else starts it from zero); rules read it with counter("key", "1h") |
write, idempotent |
get_stats |
Decision time series and totals (1h / 24h / 7d / 30d) | read |
get_risk |
Risk thresholds (where medium, high and critical start; defaults 25 / 50 / 75), signal weights (default, current, enabled), how recent verdicts spread over risk scores and levels, and the signals seen most (24h / 7d / 30d). Risk is a hint: the levels only name score ranges that rules read | read |
set_risk_thresholds |
Set where the risk levels start (medium, high, critical; 1 ≤ medium < high < critical ≤ 100). Changes risk.level for every rule that reads it from the next verdict; stored verdicts keep their level. The confirmation shows the preview (verdicts changing level, what the rules reading risk would match); needs confirmation |
write, idempotent |
get_integration_status |
Last assessment and verdict, origins seen and refused | read |
apply_todo_fix |
Apply a to-do's server-side fix (allow an origin, require the token, use a widget check for high risk, block unfinished verifications) | write |
Check groups. create_rule, update_rule and simulate_rule take the checks of an
outcome challenge either as check (one check, key or id) or as checks, a group of 1–3
distinct checks — not both:
{"outcome": "challenge",
"checks": {"mode": "any", "items": [{"check": "captcha", "weight": 70}, {"check": "hcaptcha", "weight": 30}]}}
mode is any (one of these, picked at random by weight when the verification is
issued; weight 0–100, default 1, 0 = fallback only, at least one above 0), sequence
(all, one after another) or parallel (all at once; weights are dropped for these two).
An invalid group is refused with invalid_rule. Compact rule output shows checks; see
RULES.md §2.3 for how groups decide.
Each tool carries annotations (readOnlyHint, destructiveHint, idempotentHint), so
clients can ask for confirmation before writes. Most clients do this by default. There are
no tools to connect checks or store provider secrets: use the panel for those.
Drafting rules with an agent. Rules an agent creates are always saved in
Monitoring: they record what they would have done ("would have: Verify with SMS") and
never change a decision. Run simulate_rule before create_rule; move a rule to
Enforcing with set_rule_mode only after its monitoring hits look right and its
unseen_attributes is empty. A rule's state is the only monitor/enforce switch — actions
have no mode, and an enforcing rule applies on every action of its scope, new ones
included. Events the customer's server reports (POST /v1/events) are read with
event_count("topup", "1h"), event_sum("topup", "24h") and
days_since(event_last("topup")), and the user's attempts at an action with
attempts("topup", "5m") (every verdict call, a retry after a verification not counted
again); a window is minutes, hours or days from "1m" to "30d" and the arguments are
quoted literals (anything else does not compile). Totals across users — every payment
from one country, everything hotmail users bought — are shared counters: define one with
save_counter and read counter("key", "15m") (window at most 24h: the total of the
request's group); it counts from when it is created.
Enforcing a rule is the step that changes live decisions (with the token
settings of set_action_token); the in-panel assistant asks you to confirm it with the
replay summary, and MCP clients should do the same.
4. Security notes
- Least privilege. Tools run the panel API in-process as the token's user. Validation,
role checks, rate limits and audit are the same as in the panel. Give agents a
viewertoken unless they need to change things. Tokens can be limited to one organization, and superadmin powers are never granted through a token. - Not allowed with tokens: managing tokens, changing passwords or two-factor settings,
invitations and membership changes, and creating or deleting organizations. These return
403 token_not_allowed. - Revocation on password change. A password change in the panel and an operator
password reset (
abusend reset-password) revoke every access token of the user, so a leaked token does not outlive the credentials that created it. - Audit. Every change is recorded in the organization's audit log as your user with
via: "mcp"(panel API calls with a token recordvia: "token:abp_…", the prefix only). - Data flow. Tool results, including verdict and flow details such as IP addresses, user ids and device ids, go to the AI client you connected and to its model provider. That transfer is yours to control; see Privacy.
- Brute force. Invalid tokens are counted per client address. After 20 failures in
5 minutes, the address gets
429for the rest of the window. - Storage. Tokens are stored only as SHA-256 hashes. Their last use is recorded (at most once a minute) and shown in the panel. Revoke tokens you no longer use.
- Prompt injection. Values that come from your site's traffic (user agents, attribute values, list notes) are data. Treat an agent's suggested writes like any other change and review them before approving.