Give Your Agent an IP Risk Tool in One Line: IP99's Free MCP Endpoint
If your agent already speaks Model Context Protocol, you do not need to scrape a docs page or invent a custom HTTP wrapper to check whether an IP looks like a proxy exit. IP99 (by ThreatHunter) exposes a remote MCP serve
If your agent already speaks Model Context Protocol, you do not need to scrape a docs page or invent a custom HTTP wrapper to check whether an IP looks like a proxy exit. IP99 (by ThreatHunter) exposes a remote MCP server that returns the same risk and geolocation fields as the free REST lookup.
That matters because agents are starting to make network-facing decisions: enrich a signup IP, decide whether a login needs a second factor, or label a scrape target as shared egress. Hard-coding curl paths inside prompt text works until the path changes. An MCP tool keeps the contract in one place: the client discovers ip_risk_lookup, calls it, and gets JSON.
Connect once
Anonymous Streamable HTTP (no session id, no signup):
# Claude Code example
claude mcp add --transport http ip99 https://ip99.com/mcp
Or point any MCP client at https://ip99.com/mcp (POST only; GET returns 405). The endpoint is stateless: no Mcp-Session-Id is issued and none is required. The free tier needs no auth.
For the signed-in quota (5,000 lookups/day), connect the OAuth endpoint instead:
claude mcp add --transport http ip99 https://ip99.com/en/mcp/account
# then run /mcp inside Claude Code and pick Authenticate
That opens the IP99 login in the client and charges lookups to your account. Tokens last about an hour with silent refresh for 30 days. You can also send X-API-Key on the anonymous endpoint if you already have a key from the console. Claude.ai, ChatGPT custom connectors, Cursor, and VS Code can use the same account URL with no headers to paste.
One tool
tools/list currently exposes a single tool: ip_risk_lookup.
| Argument | Required | Notes |
|---|---|---|
ip |
yes | IPv4 or IPv6, e.g. 8.8.8.8 or 2001:4860:4860::8888
|
A live call on 14 Oct 2026 for 8.8.8.8 returned (shape stable; values move as evidence ages):
{
"ip": "8.8.8.8",
"computed_at": "2026-10-14T13:05:54Z",
"evidence_state": "none",
"risk": {
"score": 0,
"level": "none",
"signals": ["hosting"]
},
"network": {
"asn": 15169,
"usage_type": "IDC"
},
"geo": {
"continent": "NA",
"country": "US",
"region": "California",
"city": "Mountain View",
"lat": 37.386051,
"lon": -122.083847,
"tz": "America/Los_Angeles"
}
}
Same fields as GET https://ip99.com/v1/ip/{ip}. The MCP server instructions are explicit: the score measures freshness and strength of verifiable risk evidence, not the probability that an address is an attacker. evidence_state: none with score 0 means IP99 holds no current evidence, not that the address is safe. hosting here is a network property (Google's resolver sits in a datacenter), not a proxy verdict. Do not treat hosting or mobile as block signals on their own — that is how you lock out whole clouds and carrier ranges.
How to read the answer in an agent loop
A useful agent policy is boring and ordered:
- Branch on
evidence_statefirst (active/stale/none). - Then look at
risk.score/risk.level. - Then inspect
risk.signalsfor proxy-class hits (proxy,vpn,dialup_pool,hijacked_proxy,cloud_phone,cloud_service) versus network properties (hosting,mobile). - Use
network.asnandnetwork.usage_typeas prior context, not as a person. - Prefer step-up friction (captcha, rate limit, extra verify) over hard bans when the signal is shared egress — especially on residential (
DYN) and mobile (MOB) space.
hijacked_proxy is a caution flag: the address may be a compromised home device. The holder is often a victim. Blocking the account behind that IP without a human path is usually the wrong move.
The same lookup is available without MCP:
curl https://ip99.com/v1/ip/8.8.8.8
Omit the path segment to ask about the caller's own egress:
curl https://ip99.com/v1/ip
Anonymous quota is 500 lookups/day per client IP and 60/minute. Account key or OAuth raises the daily cap (see pricing). Paid plans buy volume; they do not buy a different score for the same IP.
Quotas and errors
A failed tool call still returns HTTP 200 with isError: true (MCP convention). content[0].text is a JSON error in the same shape as the REST API, including retry_after / resets_at / next_step when you hit a limit. That keeps agents from treating a quota wall as a transport failure and retrying forever.
When this is useful
- Fraud / abuse agents that need a second opinion on signup or login IPs without hard-coding REST paths
- OSINT workflows that already chain MCP tools and want geo + usage type + proxy-class signals in one call
- Local coding agents (Claude Code, Cursor, VS Code MCP, ChatGPT connectors) that should not scrape HTML
- Humans opening Claude or Codex through a VPN or shared proxy: the same
ip_risk_lookup(or a quickcurl https://ip99.com/v1/ip) is useful as egress hygiene — check the current risk score and proxy-class signals before you log in. That does not promise an unban; it only helps you avoid known shared or dirty exits
It is not a Tor detector (IP99 has no Tor-specific signal), not a full CT archive, and not a phone-risk tool. Those live elsewhere on IP99 (CT and pDNS free trial lookups; phone risk is enterprise). For IP risk at decision time, the MCP path is the same honest model as the REST API: evidence that ages out, and an explicit none / stale / active state.
Try it
curl -X POST https://ip99.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ip_risk_lookup","arguments":{"ip":"8.8.8.8"}}}'
Docs: ip99.com/api#mcp. OpenAPI: ip99.com/openapi.json. Full English overview: ip99.com.
IP99 is built by ThreatHunter. Every answer is a judgement that expires, not a permanent label — whether your agent called MCP or you typed curl.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.