"""HTTP server wrapper for OSINT MCP tools."""
import asyncio
import os
from flask import Flask, request, jsonify, render_template_string
from functools import wraps
from osint_mcp.config.settings import get_settings
from osint_mcp.clients import (
CensysClient,
ShodanClient,
HunterClient,
ApolloClient,
LinkedInClient,
PerplexityClient,
HIBPClient,
SherlockClient,
HoleheCl,
DNSClient,
WHOISClient,
)
app = Flask(__name__)
# Global clients
_clients = {}
def get_clients():
"""Initialize clients on first use."""
global _clients
if _clients:
return _clients
settings = get_settings()
_clients = {
"censys": CensysClient(api_token=settings.censys_api_token),
"shodan": ShodanClient(api_key=settings.shodan_api_key),
"hunter": HunterClient(api_key=settings.hunter_api_key),
"apollo": ApolloClient(api_key=settings.apollo_api_key),
"linkedin": LinkedInClient(rapidapi_key=settings.rapidapi_key),
"perplexity": PerplexityClient(api_key=settings.perplexity_api_key),
"hibp": HIBPClient(api_key=settings.hibp_api_key),
"sherlock": SherlockClient(),
"holehe": HoleheCl(),
"dns": DNSClient(),
"whois": WHOISClient(),
}
return _clients
def async_route(f):
"""Wrapper to run async functions in Flask."""
@wraps(f)
def wrapper(*args, **kwargs):
return asyncio.run(f(*args, **kwargs))
return wrapper
# Dashboard HTML
DASHBOARD_HTML = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>OSINT MCP</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #0d1117 0%, #161b22 100%);
color: #c9d1d9;
min-height: 100vh;
padding: 20px;
}
.container { max-width: 1200px; margin: 0 auto; }
h1 {
font-size: 2.5em;
margin-bottom: 10px;
background: linear-gradient(90deg, #58a6ff, #a371f7);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle { color: #8b949e; margin-bottom: 30px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
.card {
background: #161b22;
border: 1px solid #30363d;
border-radius: 12px;
padding: 20px;
}
.card h3 { color: #58a6ff; margin-bottom: 15px; }
input, select {
width: 100%;
padding: 10px;
margin-bottom: 10px;
background: #0d1117;
border: 1px solid #30363d;
border-radius: 6px;
color: #c9d1d9;
}
button {
width: 100%;
padding: 10px;
background: #238636;
border: none;
border-radius: 6px;
color: white;
cursor: pointer;
font-weight: 600;
}
button:hover { background: #2ea043; }
.result {
margin-top: 15px;
padding: 15px;
background: #0d1117;
border-radius: 6px;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
}
.status { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; }
.status-item {
padding: 5px 10px;
border-radius: 20px;
font-size: 12px;
background: #21262d;
}
.status-ok { color: #3fb950; }
.status-miss { color: #8b949e; }
.back-link { color: #58a6ff; text-decoration: none; display: block; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container">
<a href="/" class="back-link">β Back to Chaos Lab</a>
<h1>π OSINT MCP</h1>
<p class="subtitle">Unified Intelligence Gathering</p>
<div class="status">
{% for name, available in apis.items() %}
<span class="status-item {{ 'status-ok' if available else 'status-miss' }}">
{{ 'β' if available else 'β' }} {{ name }}
</span>
{% endfor %}
</div>
<div class="grid">
<div class="card">
<h3>π Infrastructure</h3>
<input type="text" id="infra-target" placeholder="IP or domain">
<select id="infra-type">
<option value="censys">Censys Host</option>
<option value="shodan">Shodan Host</option>
<option value="dns">DNS Lookup</option>
<option value="whois">WHOIS</option>
</select>
<button onclick="runInfra()">Lookup</button>
<div id="infra-result" class="result" style="display:none;"></div>
</div>
<div class="card">
<h3>π§ Email/Identity</h3>
<input type="text" id="email-target" placeholder="Email or domain">
<select id="email-type">
<option value="domain">Find Company Emails</option>
<option value="verify">Verify Email</option>
<option value="breach">Breach Check</option>
<option value="services">Email Services (Holehe)</option>
</select>
<button onclick="runEmail()">Search</button>
<div id="email-result" class="result" style="display:none;"></div>
</div>
<div class="card">
<h3>π€ Person</h3>
<input type="text" id="person-name" placeholder="Full name">
<input type="text" id="person-company" placeholder="Company (optional)">
<select id="person-type">
<option value="enrich">Apollo Enrich</option>
<option value="linkedin">LinkedIn Find</option>
<option value="research">AI Research</option>
</select>
<button onclick="runPerson()">Search</button>
<div id="person-result" class="result" style="display:none;"></div>
</div>
<div class="card">
<h3>π’ Company</h3>
<input type="text" id="company-domain" placeholder="Company domain">
<select id="company-type">
<option value="enrich">Apollo Enrich</option>
<option value="emails">Find Emails</option>
<option value="research">AI Research</option>
</select>
<button onclick="runCompany()">Search</button>
<div id="company-result" class="result" style="display:none;"></div>
</div>
<div class="card">
<h3>π Username</h3>
<input type="text" id="username" placeholder="Username">
<button onclick="runUsername()">Search (Sherlock)</button>
<div id="username-result" class="result" style="display:none;"></div>
</div>
<div class="card">
<h3>π€ AI Query</h3>
<input type="text" id="ai-query" placeholder="Research question">
<button onclick="runAI()">Ask Perplexity</button>
<div id="ai-result" class="result" style="display:none;"></div>
</div>
</div>
</div>
<script>
async function api(endpoint, params) {
const url = new URL(endpoint, window.location.origin);
Object.entries(params).forEach(([k, v]) => v && url.searchParams.set(k, v));
const resp = await fetch(url);
return await resp.json();
}
async function runInfra() {
const target = document.getElementById('infra-target').value;
const type = document.getElementById('infra-type').value;
const result = document.getElementById('infra-result');
result.style.display = 'block';
result.textContent = 'Loading...';
const data = await api(`/api/${type}`, {target});
result.textContent = JSON.stringify(data, null, 2);
}
async function runEmail() {
const target = document.getElementById('email-target').value;
const type = document.getElementById('email-type').value;
const result = document.getElementById('email-result');
result.style.display = 'block';
result.textContent = 'Loading...';
const data = await api(`/api/email/${type}`, {target});
result.textContent = JSON.stringify(data, null, 2);
}
async function runPerson() {
const name = document.getElementById('person-name').value;
const company = document.getElementById('person-company').value;
const type = document.getElementById('person-type').value;
const result = document.getElementById('person-result');
result.style.display = 'block';
result.textContent = 'Loading...';
const data = await api(`/api/person/${type}`, {name, company});
result.textContent = JSON.stringify(data, null, 2);
}
async function runCompany() {
const domain = document.getElementById('company-domain').value;
const type = document.getElementById('company-type').value;
const result = document.getElementById('company-result');
result.style.display = 'block';
result.textContent = 'Loading...';
const data = await api(`/api/company/${type}`, {domain});
result.textContent = JSON.stringify(data, null, 2);
}
async function runUsername() {
const username = document.getElementById('username').value;
const result = document.getElementById('username-result');
result.style.display = 'block';
result.textContent = 'Loading... (this may take a minute)';
const data = await api('/api/username', {username});
result.textContent = JSON.stringify(data, null, 2);
}
async function runAI() {
const query = document.getElementById('ai-query').value;
const result = document.getElementById('ai-result');
result.style.display = 'block';
result.textContent = 'Loading...';
const data = await api('/api/ai', {query});
result.textContent = JSON.stringify(data, null, 2);
}
</script>
</body>
</html>
'''
@app.route('/')
def index():
settings = get_settings()
apis = settings.get_available_services()
return render_template_string(DASHBOARD_HTML, apis=apis)
@app.route('/api/status')
def api_status():
settings = get_settings()
return jsonify(settings.get_available_services())
# Infrastructure endpoints
@app.route('/api/censys')
@async_route
async def api_censys():
target = request.args.get('target', '')
clients = get_clients()
await clients['censys'].start()
return jsonify(await clients['censys'].host_lookup(target))
@app.route('/api/shodan')
@async_route
async def api_shodan():
target = request.args.get('target', '')
clients = get_clients()
await clients['shodan'].start()
return jsonify(await clients['shodan'].host_lookup(target))
@app.route('/api/dns')
@async_route
async def api_dns():
target = request.args.get('target', '')
clients = get_clients()
return jsonify(await clients['dns'].lookup(target))
@app.route('/api/whois')
@async_route
async def api_whois():
target = request.args.get('target', '')
clients = get_clients()
return jsonify(await clients['whois'].lookup(target))
# Email endpoints
@app.route('/api/email/domain')
@async_route
async def api_email_domain():
target = request.args.get('target', '')
clients = get_clients()
await clients['hunter'].start()
return jsonify(await clients['hunter'].domain_search(target))
@app.route('/api/email/verify')
@async_route
async def api_email_verify():
target = request.args.get('target', '')
clients = get_clients()
await clients['hunter'].start()
return jsonify(await clients['hunter'].email_verify(target))
@app.route('/api/email/breach')
@async_route
async def api_email_breach():
target = request.args.get('target', '')
clients = get_clients()
await clients['hibp'].start()
return jsonify(await clients['hibp'].check_breaches(target))
@app.route('/api/email/services')
@async_route
async def api_email_services():
target = request.args.get('target', '')
clients = get_clients()
return jsonify(await clients['holehe'].check(target))
# Person endpoints
@app.route('/api/person/enrich')
@async_route
async def api_person_enrich():
name = request.args.get('name', '')
company = request.args.get('company', '')
parts = name.split(' ', 1)
first = parts[0] if parts else ''
last = parts[1] if len(parts) > 1 else ''
clients = get_clients()
await clients['apollo'].start()
return jsonify(await clients['apollo'].find_person(first, last, company or None))
@app.route('/api/person/linkedin')
@async_route
async def api_person_linkedin():
name = request.args.get('name', '')
company = request.args.get('company', '')
clients = get_clients()
await clients['linkedin'].start()
return jsonify(await clients['linkedin'].find_profile(name, company or None))
@app.route('/api/person/research')
@async_route
async def api_person_research():
name = request.args.get('name', '')
company = request.args.get('company', '')
clients = get_clients()
await clients['perplexity'].start()
return jsonify(await clients['perplexity'].research_person(name, company or None))
# Company endpoints
@app.route('/api/company/enrich')
@async_route
async def api_company_enrich():
domain = request.args.get('domain', '')
clients = get_clients()
await clients['apollo'].start()
return jsonify(await clients['apollo'].enrich_company(domain))
@app.route('/api/company/emails')
@async_route
async def api_company_emails():
domain = request.args.get('domain', '')
clients = get_clients()
await clients['hunter'].start()
return jsonify(await clients['hunter'].domain_search(domain))
@app.route('/api/company/research')
@async_route
async def api_company_research():
domain = request.args.get('domain', '')
clients = get_clients()
await clients['perplexity'].start()
return jsonify(await clients['perplexity'].research_company(domain.split('.')[0], domain))
# Username endpoint
@app.route('/api/username')
@async_route
async def api_username():
username = request.args.get('username', '')
clients = get_clients()
return jsonify(await clients['sherlock'].search(username))
# AI endpoint
@app.route('/api/ai')
@async_route
async def api_ai():
query = request.args.get('query', '')
clients = get_clients()
await clients['perplexity'].start()
return jsonify(await clients['perplexity'].query(query))
def main():
"""Run HTTP server."""
port = int(os.environ.get('PORT', 5005))
app.run(host='0.0.0.0', port=port, debug=False)
if __name__ == '__main__':
main()