Skip to main content

Async Python client + MCP server for ProjectDiscovery's Interactsh OOB testing platform

Project description

interactsh-mcp

An MCP server that gives LLMs first-class access to ProjectDiscovery's Interactsh — the out-of-band (OOB) interaction collector behind tools like Nuclei.

Use it to let an agent validate OOB-style vulnerabilities end-to-end: blind SSRF, log4shell-style JNDI callbacks, blind XXE, blind SQLi exfil, DNS exfiltration, command-injection callbacks, blind XSS, etc. The agent gets a fresh payload URL, embeds it in a request to the target, and polls for the callback to confirm the bug.

Works against the public Interactsh service (the public oast.* rotation) out of the box, and against any self-hosted server — with or without a token.

The package ships two things in one install:

  1. An MCP server binary (interactsh-mcp) for Claude Code, Codex CLI, etc.
  2. An async Python library (interactsh_mcp) you can import directly if you'd rather drive Interactsh from your own code — see Use as a Python library below.

Install

From PyPI (recommended)

pip install interactsh-mcp
# or, with uv:
uv tool install interactsh-mcp

After install the interactsh-mcp command starts the MCP server on stdio.

From GitHub (latest, no PyPI release needed)

pip install git+https://github.com/rek7/interactsh-mcp.git
# or, run without installing:
uvx --from git+https://github.com/rek7/interactsh-mcp.git interactsh-mcp

From source

git clone https://github.com/rek7/interactsh-mcp.git
cd interactsh-mcp
pip install .

Docker

# Build locally
docker build -t interactsh-mcp .
docker run --rm -i interactsh-mcp     # smoke test; Ctrl-D to exit

The image runs as a non-root user and only needs outbound HTTPS to the Interactsh server.


Configure your client

Claude Code

Claude Code reads MCP servers from ~/.claude.json (or a project-local .mcp.json). Add an entry under "mcpServers":

Local Python install — public servers

{
  "mcpServers": {
    "interactsh": {
      "command": "interactsh-mcp"
    }
  }
}

Local Python install — self-hosted server

{
  "mcpServers": {
    "interactsh": {
      "command": "interactsh-mcp",
      "env": {
        "INTERACTSH_SERVER": "interact.example.com",
        "INTERACTSH_TOKEN": "your-server-token"
      }
    }
  }
}

Omit INTERACTSH_TOKEN if your self-hosted server was started without -auth.

Docker

{
  "mcpServers": {
    "interactsh": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-e", "INTERACTSH_SERVER",
        "-e", "INTERACTSH_TOKEN",
        "interactsh-mcp"
      ],
      "env": {
        "INTERACTSH_SERVER": "interact.example.com",
        "INTERACTSH_TOKEN": "your-server-token"
      }
    }
  }
}

You can also register the server with the CLI:

claude mcp add interactsh -- interactsh-mcp
# with env vars:
claude mcp add interactsh \
  -e INTERACTSH_SERVER=interact.example.com \
  -e INTERACTSH_TOKEN=your-server-token \
  -- interactsh-mcp

Codex CLI

Codex CLI (OpenAI's terminal coding agent) reads MCP servers from ~/.codex/config.toml:

Public servers

[mcp_servers.interactsh]
command = "interactsh-mcp"

Self-hosted with token

[mcp_servers.interactsh]
command = "interactsh-mcp"
env = { INTERACTSH_SERVER = "interact.example.com", INTERACTSH_TOKEN = "your-server-token" }

Docker

[mcp_servers.interactsh]
command = "docker"
args = ["run", "--rm", "-i", "-e", "INTERACTSH_SERVER", "-e", "INTERACTSH_TOKEN", "interactsh-mcp"]
env = { INTERACTSH_SERVER = "interact.example.com", INTERACTSH_TOKEN = "your-server-token" }

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows. Same shape as the Claude Code example above.


Environment variables

Variable Purpose
INTERACTSH_SERVER Default hostname (e.g. interact.example.com). If unset, a random host from the public oast.* rotation is used. Per-call server argument overrides.
INTERACTSH_TOKEN Default Authorization token for -auth-protected servers. Per-call token argument overrides.
INTERACTSH_LOG_LEVEL Python log level (default WARNING).

Tools the LLM sees

Tool What it does
create_session Registers a new Interactsh session, starts background polling, and returns session_id + payload_url. Optional args: server, token, poll_interval, scheme, correlation_id_length.
list_sessions Lists active sessions with their buffer counts.
poll_interactions Drains buffered interactions for a session. wait (seconds) blocks until the first event arrives; clear=false peeks without consuming.
new_payload Vends an additional payload URL on an existing session (different nonce, same correlation-id — all routed back to the same session).
destroy_session Deregisters and frees the session.
get_default_servers Returns the public oast.* host list.

Typical agent workflow

  1. Agent calls create_session → gets payload_url e.g. c7lci09s8mts0o3og0g0abc12.oast.pro.
  2. Agent crafts an exploit that uses payload_url — a JNDI string in a User-Agent header, a ?url=http://payload_url/ SSRF, a DNS subdomain in a SQL payload, etc.
  3. Agent sends the request to the target.
  4. Agent calls poll_interactions(session_id, wait=10) and, if interactions come back, reports the vuln as confirmed (with the source IP / raw request from the callback as evidence).
  5. Agent calls destroy_session when done.

For multiple test variants in one conversation the agent can either call create_session per variant or call new_payload to get distinct URLs that share a session.


Use as a Python library

The package exports an async client you can import directly — no MCP, no LLM, just interactsh_mcp:

import asyncio
from interactsh_mcp import InteractshClient

async def main():
    # No args → random public oast.* server. Pass server=/token= to target a
    # self-hosted instance (token only needed if it was started with -auth).
    async with InteractshClient() as client:
        payload_url = client.generate_payload()
        print(f"send something to: {payload_url}")

        # ... fire your exploit / trigger the target with that URL ...

        for event in await client.poll():
            print(f"{event.protocol} from {event.remote_address}")

asyncio.run(main())

For longer-lived setups (multiple payloads, background polling, buffered events), use the session manager:

from interactsh_mcp import SessionManager

mgr = SessionManager()
session = await mgr.create(poll_interval=2.0)

url_a = mgr.new_payload(session)   # different nonce, same correlation-id
url_b = mgr.new_payload(session)
# ... trigger both ...

events = await session.drain(wait=15)   # blocks until first event or timeout
await mgr.aclose()

Exports at the package root:

Name What it is
InteractshClient Async client. Context-manage it; register() / poll() / deregister() / generate_payload().
Interaction Dataclass for a single decoded event: protocol, unique_id, full_id, q_type, raw_request, raw_response, remote_address, timestamp, raw (full JSON).
InteractshError Raised on registration/poll failures.
Session / SessionManager Background-polling multi-session helpers.
DEFAULT_SERVERS Tuple of public oast.* hostnames.

The package ships type stubs (py.typed), so mypy / pyright pick the types up automatically. If you need to call from sync code, wrap with asyncio.run(...) — see examples/04_sync_wrapper.py.

More runnable examples: examples/.


Self-hosting Interactsh

Quick reminder of how to stand up a server you can point this MCP at:

# Public, no auth:
interactsh-server -domain interact.example.com

# Token-protected (server prints a random token; pass it as INTERACTSH_TOKEN):
interactsh-server -domain interact.example.com -auth

See the interactsh server docs for DNS, TLS, and infra setup.


Development

git clone https://github.com/rek7/interactsh-mcp.git
cd interactsh-mcp
python -m venv .venv && source .venv/bin/activate
pip install -e '.[test]'
pytest

Live integration tests (DNS-callback against a real server) are opt-in:

INTERACTSH_LIVE=1 pytest tests/test_live.py
# or against a self-hosted server:
INTERACTSH_LIVE=1 INTERACTSH_SERVER=interact.example.com INTERACTSH_TOKEN=... pytest tests/test_live.py

The mocked test suite exercises the full register → poll → decrypt → drain path against an in-process fake server, so the crypto and HTTP wiring are covered without network access.


License

MIT. Interactsh itself is MIT-licensed and developed by ProjectDiscovery.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

interactsh_mcp-0.1.1.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

interactsh_mcp-0.1.1-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file interactsh_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: interactsh_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 19.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for interactsh_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ea00e6635a7fb790451ff0e5596aefb1fb784fd0450781f592ac0fbb951c68ef
MD5 f632f6d0e19c3be16744bfa38e41ae63
BLAKE2b-256 75ac0b456e2e1e3c44ccf3a422709ca70a3338380a20e82473c27c03b77cb406

See more details on using hashes here.

Provenance

The following attestation bundles were made for interactsh_mcp-0.1.1.tar.gz:

Publisher: release.yml on rek7/interactsh-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file interactsh_mcp-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: interactsh_mcp-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 14.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for interactsh_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 73ebee1fd12f4e899bd87e103246eb0c3f123b283c138253cdbcfbbd94e9a9c2
MD5 4e9efa09d1be1cf79625df0adc458fd3
BLAKE2b-256 5ba3d93b3119b46f7ac65ae8d9980f00f87476c432487c6dc9a4dc43d58299f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for interactsh_mcp-0.1.1-py3-none-any.whl:

Publisher: release.yml on rek7/interactsh-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page