01Threat intelligence for Python & MCP
Know what you’re talking to.
sec-mcp checks domains, URLs and IP addresses against ten live blacklist feeds — in process, in microseconds, with no API key. Try the MCP server right here: the console answers from a demo dataset until you connect your own.
Python 3.11+Library, CLI and MCP serverstdio + streamable HTTP
sec-mcpplayground
Simulation
Results appear here. Try the sample values above, or pick a playbook below.
Test your own server
- 01
Install and index the feeds
$pip install sec-mcp && sec-mcp update - 02
Serve MCP over HTTP
$sec-mcp-server --http - 03
Press Connect in the console. It defaults to port
8000on the host serving this page. On the public site, that host is127.0.0.1.
First-run checklist
- MCP server reachable — not connected yet
- Blacklists indexed — unknown until connected
- Threat sources
- 10feeds
- Domain lookup
- 0.006ms
- 450K entries in memory
- ~45MB
- Open source
- Apache-2.0
02Playbooks
one click · live or simulated
Start from the job, not the API.
Six things people use sec-mcp for. Run any of them in the console above, or say the prompt to an assistant with the MCP server attached.
01
Triage a phishing email
Pull every link out of a suspicious message and check them in one call.
“Are any of the links in this email on a blacklist?”
check_batch(values=[3])02
Vet outbound connections
Screen the destination IPs from a firewall or proxy log before you escalate.
“Which of these destination IPs are known-bad?”
check_batch(values=[3])03
Guard an agent’s clicks
Have an agent verify a URL before it fetches, downloads or recommends it.
“Before you download that file, check the URL with sec-mcp.”
check_batch(values=[3])04
Check feed freshness
Entry counts per source and when the blacklists were last refreshed.
“How fresh are your blacklists, and how many entries does each feed hold?”
get_status()05
Health-check the server
Database reachable, scheduler alive, last update recorded — in one answer.
“Run a health check on the sec-mcp server.”
get_diagnostics(mode="health")06
Sample the blacklist
Pull a handful of real entries to see what the feeds actually contain.
“Show me five random entries from the blacklist.”
get_diagnostics(mode="sample", sample_count=5)
03Capabilities
Six things it does well
A blacklist engine that behaves like a local function call.
No hosted service sits between you and an answer. The feeds are downloaded, normalised and indexed on your machine, and every check after that is a memory read.
Ten feeds, one index
OpenPhish, PhishTank, PhishStats, URLhaus, Spamhaus DROP, DShield, CINS, Emerging Threats, Feodo Tracker and Blocklist.de — merged into a single lookup.
Microsecond lookups
One O(1) in-memory index per entry type. A domain resolves in 0.006 ms and a URL in 0.0007 ms once in-memory storage is enabled.
Three ways in
Import it as a Python library, drive it from the terminal, or run it as an MCP server so an assistant can check a link before it recommends one.
CIDR-aware IP matching
IPv4 addresses are stored as integers and matched against whole networks, not just literal addresses — so a single range covers what it should.
Refreshed on a schedule
Feeds update daily at 00:00 from a background scheduler, and sec-mcp update forces a refresh whenever you need one.
Safe under concurrency
SQLite in WAL mode behind an in-memory cache, so parallel checks from threads or a busy MCP client stay consistent.
04Sources
Refreshed daily at 00:00
Ten public feeds, normalised into one register.
Each feed has its own format and its own idea of what an entry looks like. sec-mcp parses them per source, normalises URLs, stores IPv4 as integers, and hands you one answer.
- 01OpenPhishLive phishing URLsURL
- 02PhishStatsScored phishing URLsURL
- 03URLhausMalware distribution URLsURL
- 04PhishTankCommunity-verified phishingURL
- 05Spamhaus DROPHijacked and rogue netblocksCIDR
- 06DShieldMost-attacking networksCIDR
- 07CINS ScorePoorly-reputed addressesIP
- 08Emerging ThreatsCompromised hostsIP
- 09Feodo TrackerBotnet command & controlIP
- 10Blocklist.deReported attacking hostsIP
Need a source of your own? Add entries directly with add_entry or point the config at another feed.
05Model Context Protocol
stdio · streamable HTTP
Give your assistant a way to check before it recommends.
Run sec-mcp as an MCP server and six tools appear in your client. The model can verify a link mid-conversation instead of guessing at it.
Client configuration
Point your MCP client at the interpreter inside the virtual environment where sec-mcp is installed.
{
"mcpServers": {
"sec-mcp": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "sec_mcp.start_server"]
}
}
}Or serve it over streamable HTTP for remote clients and the playground above. It binds to 127.0.0.1 unless you pass --host.
sec-mcp-server --http --port 8000
# → http://127.0.0.1:8000/mcp
# optional: allow another web origin, require a token
export SEC_MCP_CORS_ORIGINS=https://your.site
export SEC_MCP_HTTP_AUTH_TOKEN=change-meBefore you start
- Use the absolute path to the venv’s Python. A bare
pythonwill start the wrong interpreter. - Run
sec-mcp updateonce first, thensec-mcp statusto confirm the database is populated. - Over stdio the server starts and stops with your MCP client; over HTTP it runs until you stop it.
Full steps in the install guide.
Exposed tools(6)
check_batchcheck_batch(values: List[str])- Check multiple domains/URLs/IPs in one call. Returns list of {value, is_safe, verdict, explanation}.
get_statusget_status()- Get blacklist status including entry counts and sources. Returns {entry_count, last_update, sources, server_status, source_counts}.
update_blacklistsupdate_blacklists()- Force immediate update of all blacklists. Returns {updated: bool}.
get_diagnosticsget_diagnostics(mode: str = "summary", sample_count: int = 10)- Get diagnostic information. Mode options: 'summary' (default), 'full', 'health', 'performance', 'sample'.
add_entryadd_entry(url, ip, date, score, source)- Add a manual blacklist entry.
remove_entryremove_entry(value: str)- Remove a blacklist entry by URL or IP.
In conversation
User: "Is example.com safe to visit?"
AI: Let me check that URL for you.
[Uses sec-mcp check_batch tool]
I've checked example.com against our security database.
The domain is not found in any blacklists and
appears to be safe to visit.06Install
~5 minutes
From pip install to a first verdict.
Four commands to a working install. The only part people trip on is step three — the database is empty until you populate it.
Create a virtual environment
Python 3.11 or newer. The MCP config later needs this path, so keep it somewhere stable.
step 1 python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activateInstall the package
step 2 pip install sec-mcp # Verify installation sec-mcp --versionPopulate the database
The first download pulls all ten feeds and takes a few minutes. After that a scheduler refreshes them daily at 00:00.
step 3 # Download and index every configured feed sec-mcp update # Entry counts, last update, per-source breakdown sec-mcp statusRun a check
Prints "Status: Safe" or "Status: Blacklisted" with an explanation. Add --json for machine-readable output.
step 4 sec-mcp check example.com sec-mcp check 8.8.8.8 sec-mcp check https://example.com/path --json
Or import it
Constructing SecMCP() is what creates the database and starts the scheduler — importing the package on its own does nothing.
from sec_mcp import SecMCP
# Default database location
client = SecMCP()
# Or point it at your own file
client = SecMCP(db_path="/srv/sec-mcp/blacklist.db")
result = client.check("https://example.com/login")
if result.blacklisted:
# e.g. "Blacklisted URL by OpenPhish"
print(f"Blocked: {result.explanation}")
else:
print("Clean")values = [
"example.com",
"192.168.1.1",
"https://example.org/login",
]
# Results come back in input order; CheckResult carries
# the verdict, so zip it against the values you sent.
for value, result in zip(values, client.check_batch(values)):
verdict = "BLACKLISTED" if result.blacklisted else "clean"
print(f"{value:<28} {verdict:<12} {result.explanation}")Every method is listed in the API reference.
07Python API
from sec_mcp import SecMCP
Nine methods and two dataclasses. That is the whole surface.
Every check returns a CheckResult: a boolean and a sentence explaining it. There is nothing else to learn.
Methods(9)
check(value: str) -> CheckResult- Check a domain, URL or IP. Infers which of the three it is.
check_domain(domain: str) -> CheckResult- Check a domain and each of its parent domains.
check_url(url: str) -> CheckResult- Check a URL exactly, then fall back to its domain.
check_ip(ip: str) -> CheckResult- Check an address, including the networks that contain it.
check_batch(values: List[str]) -> List[CheckResult]- Check many values at once. Results come back in input order.
get_status() -> StatusInfo- Entry count, last update time, active sources and server state.
update() -> dict- Force a refresh of every feed. Rate limited; returns {"updated": bool}.
sample(count: int = 10) -> List[str]- Return a random sample of indexed entries.
scheduler_alive() -> bool- Report whether the background update thread is still running.
Return types
CheckResult
- blacklistedbool
- True when the value matched a feed.
- explanationstr
- "Blacklisted URL by OpenPhish", or "Not blacklisted".
StatusInfo
- entry_countint
- Total indexed entries.
- last_updatedatetime
- When the feeds last refreshed.
- sourcesList[str]
- Active feed names.
- server_statusstr
- Current server state.
Putting it together
from sec_mcp import SecMCP
client = SecMCP()
status = client.get_status()
print(f"{status.entry_count} entries "
f"from {len(status.sources)} sources")
print(f"last update: {status.last_update}")
result = client.check_ip("185.0.0.1")
print(result.blacklisted, result.explanation)
# Force a refresh (rate limited)
print(client.update()) # {'updated': True}