Skip to main content
Glama

SpiderFoot MCP Server

index-http.ts6.91 kB
import 'dotenv/config'; import express, { Request, Response } from 'express'; import { randomUUID } from 'node:crypto'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { makeSpiderfootClientFromEnv } from './spiderfootClient.js'; // Build a new server instance with tools registered (same tools as stdio index) function buildServer() { const server = new McpServer({ name: 'spiderfoot-mcp-server', version: '0.1.0' }); const sf = makeSpiderfootClientFromEnv(); const StartScanSchema = z.object({ scanname: z.string().min(1), scantarget: z.string().min(1), modulelist: z.string().optional(), typelist: z.string().optional(), usecase: z.enum(['all', 'investigate', 'passive', 'footprint']).optional(), }); server.registerTool( 'spiderfoot_ping', { title: 'Ping', description: 'Ping SpiderFoot server to verify it is responding.', inputSchema: {} }, async () => ({ content: [{ type: 'text', text: JSON.stringify(await sf.ping()) }] }) ); server.registerTool( 'spiderfoot_modules', { title: 'Modules', description: 'List available SpiderFoot modules.', inputSchema: {} }, async () => ({ content: [{ type: 'text', text: JSON.stringify(await sf.modules()) }] }) ); server.registerTool( 'spiderfoot_event_types', { title: 'Event Types', description: 'List available SpiderFoot event types.', inputSchema: {} }, async () => ({ content: [{ type: 'text', text: JSON.stringify(await sf.eventTypes()) }] }) ); server.registerTool( 'spiderfoot_scans', { title: 'Scans', description: 'List all scans (past and present).', inputSchema: {} }, async () => ({ content: [{ type: 'text', text: JSON.stringify(await sf.scans()) }] }) ); server.registerTool( 'spiderfoot_scan_info', { title: 'Scan Info', description: 'Retrieve scan metadata/config for a scan ID.', inputSchema: { id: z.string() } }, async ({ id }) => ({ content: [{ type: 'text', text: JSON.stringify(await sf.scanInfo(id)) }] }) ); server.registerTool( 'spiderfoot_start_scan', { title: 'Start Scan', description: 'Start a new scan against a target.', inputSchema: StartScanSchema.shape }, async (input) => { const allow = (process.env.ALLOW_START_SCAN ?? 'true').toLowerCase(); if (!(allow === 'true' || allow === '1' || allow === 'yes')) { return { content: [{ type: 'text', text: 'Starting scans is disabled by configuration (ALLOW_START_SCAN=false).' }], isError: true }; } const res = await sf.startScan(input as any); return { content: [{ type: 'text', text: JSON.stringify(res) }] }; } ); server.registerTool( 'spiderfoot_scan_data', { title: 'Scan Data', description: 'Fetch scan event results for a scan ID.', inputSchema: { id: z.string(), eventType: z.string().optional() } }, async ({ id, eventType }) => ({ content: [{ type: 'text', text: JSON.stringify(await sf.scanEventResults({ id, eventType })) }] }) ); server.registerTool( 'spiderfoot_scan_data_unique', { title: 'Scan Data Unique', description: 'Fetch unique scan event results.', inputSchema: { id: z.string(), eventType: z.string().optional() } }, async ({ id, eventType }) => ({ content: [{ type: 'text', text: JSON.stringify(await sf.scanEventResultsUnique({ id, eventType })) }] }) ); server.registerTool( 'spiderfoot_scan_logs', { title: 'Scan Logs', description: 'Fetch/poll scan logs for a given scan ID.', inputSchema: { id: z.string(), limit: z.number().optional(), reverse: z.enum(['0','1']).optional(), rowId: z.number().optional() } }, async ({ id, limit, reverse, rowId }) => ({ content: [{ type: 'text', text: JSON.stringify(await sf.scanLogs({ id, limit, reverse, rowId })) }] }) ); server.registerTool( 'spiderfoot_export_json', { title: 'Export JSON', description: 'Export scan results in JSON for CSV of IDs.', inputSchema: { ids: z.string() } }, async ({ ids }) => ({ content: [{ type: 'text', text: JSON.stringify(await sf.exportJson(ids)) }] }) ); return server; } async function main() { const app = express(); app.use(express.json()); // Session map const transports: Record<string, StreamableHTTPServerTransport> = {}; app.post('/mcp', async (req: Request, res: Response) => { try { const sessionId = req.headers['mcp-session-id'] as string | undefined; let transport: StreamableHTTPServerTransport; if (sessionId && transports[sessionId]) { transport = transports[sessionId]; } else if (!sessionId && isInitializeRequest(req.body)) { transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), // For local usage, consider enabling DNS rebinding protection and allowedHosts // enableDnsRebindingProtection: true, // allowedHosts: ['127.0.0.1', 'localhost'] }); transport.onclose = () => { if (transport.sessionId) delete transports[transport.sessionId]; }; const server = buildServer(); await server.connect(transport); if (transport.sessionId) transports[transport.sessionId] = transport; } else { res.status(400).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: No valid session ID provided' }, id: null, }); return; } await transport.handleRequest(req, res, req.body); } catch (err) { // eslint-disable-next-line no-console console.error('HTTP /mcp error', err); if (!res.headersSent) res.status(500).json({ error: 'Internal server error' }); } }); app.get('/mcp', async (req: Request, res: Response) => { const sessionId = req.headers['mcp-session-id'] as string | undefined; if (!sessionId || !transports[sessionId]) { res.status(400).send('Invalid or missing session ID'); return; } await transports[sessionId].handleRequest(req, res); }); app.delete('/mcp', async (req: Request, res: Response) => { const sessionId = req.headers['mcp-session-id'] as string | undefined; if (!sessionId || !transports[sessionId]) { res.status(400).send('Invalid or missing session ID'); return; } await transports[sessionId].handleRequest(req, res); }); const port = Number(process.env.MCP_HTTP_PORT || 3000); app.listen(port, () => { // eslint-disable-next-line no-console console.log(`[spiderfoot-mcp-server:http] listening on http://0.0.0.0:${port}/mcp`); }); } main().catch((err) => { // eslint-disable-next-line no-console console.error('Failed to start HTTP MCP server:', err); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/CorbettCajun/Spiderfoot-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server