MCP Decoy Server
Provides tools for searching repositories, code, and build artifacts across Bitbucket workspaces, enabling AI agents to interact with Bitbucket's code hosting and CI/CD platform.
Provides tools for retrieving Confluence pages and performing CQL searches, enabling AI agents to access and search documentation and knowledge base content in Confluence.
Provides tools for listing indices, searching logs, and retrieving cluster information, enabling AI agents to interact with Elasticsearch data and monitoring capabilities.
Provides tools for searching repositories and code, listing commits, and retrieving pull request comments, enabling AI agents to interact with GitHub's code hosting and collaboration features.
Provides tools for searching repositories and code, listing commits, and retrieving merge request comments, enabling AI agents to interact with GitLab's code hosting and CI/CD platform.
Provides tools for searching build logs and retrieving SCM configuration, enabling AI agents to interact with Jenkins CI/CD jobs and pipeline information.
Provides tools for searching issues with JQL and retrieving detailed issue information, enabling AI agents to interact with Jira project management and issue tracking.
Provides tools for listing databases, executing SQL queries, and retrieving server information, enabling AI agents to interact with PostgreSQL database systems.
Provides tools for executing SOQL queries, listing reports, retrieving report data, and accessing account details, enabling AI agents to interact with Salesforce CRM data and analytics.
Provides tools for retrieving user information, searching messages across channels, and listing channels, enabling AI agents to interact with Slack workspace communication and user data.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP Decoy Serverlist all available database connections"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP Decoy Server
An Express.js server that impersonates a legitimate enterprise MCP (Model Context Protocol) integration platform. Every interaction is logged in forensic detail and optionally forwarded to a SIEM via RFC 5424 syslog. Designed for deception-based threat detection against AI-enabled attackers.
Overview
Enterprise AI tooling has become a high-value attack target. Threat actors compromise MCP servers to exfiltrate credentials, source code, and business data by calling tools that connect LLM clients to internal services.
This server presents itself as enterprise-integrations — a plausible MCP hub for developer tooling — and responds to every tool call with convincing fake data. Simultaneously, it records the source IP, requested tool, arguments, and full request context, and forwards each event to your SIEM.
It implements all 10 service categories from Zscaler's deception MCP product, across 38 tools total.
Threat model addressed: An attacker who has obtained an MCP endpoint URL (e.g. via credential theft, supply chain compromise, or internal reconnaissance) and connects an LLM client to enumerate available tools and exfiltrate data.
Architecture
┌─────────────────────────────────────────────────────────┐
│ MCP Client / LLM Agent │
└────────────┬────────────────────────┬───────────────────┘
│ POST /mcp │ GET /sse
│ (Streamable HTTP) │ POST /messages
▼ ▼ (SSE transport)
┌─────────────────────────────────────────────────────────┐
│ index.js │
│ Express 5 · JSON-RPC 2.0 · MCP 2024-11-05 │
│ │
│ handleRpc() ──► tools.js (38 tool dispatchers) │
│ │ └── fake data generators │
│ │ │
│ ▼ │
│ store.js (LogStore, circular buffer, EventEmitter) │
│ │ │
│ ├──► syslog.js (RFC 5424, UDP / TCP) │
│ └──► /api/events (SSE to dashboard) │
└──────────────────┬──────────────────────────────────────┘
│ GET /api/*
▼
┌─────────────────────────────────────────────────────────┐
│ dashboard/ (Vue 3 + Vite) │
│ Pinia store · Chart.js · Real-time SSE feed │
└─────────────────────────────────────────────────────────┘Components:
index.js— Express server, MCP protocol handling (both transports), dashboard API, and access logging middleware.tools.js— All 38 tool definitions (MCPinputSchema) and their fake-data response generators.store.js— In-memory circular log buffer (10,000 entries). SingletonEventEmitterthat pushes each new entry to dashboard SSE subscribers.syslog.js— RFC 5424 syslog forwarder. Supports UDP (fire-and-forget) and TCP (persistent connection with reconnect buffer).dashboard/— Vue 3 SPA with Pinia for state, Chart.js for timeline graphs, and a live SSE feed from/api/events.
Quick Start
git clone <repo-url> mcpdecoy
cd mcpdecoy
npm install
npm startThe server listens on port 3000 by default. Verify it is up:
curl http://localhost:3000/health
# {"status":"ok","server":"enterprise-integrations","version":"1.0.0"}For development with auto-restart:
npm run devConfiguration
All configuration is via environment variables. The server runs with safe defaults and requires no configuration file.
Variable | Default | Description |
|
| TCP port the Express server binds to |
|
| MCP |
| (unset) | Syslog destination hostname or IP. Syslog forwarding is disabled when unset |
|
| Syslog destination port |
|
| Transport: |
|
| RFC 5424 facility code (16 = local0) |
|
| RFC 5424 severity code (5 = notice) |
|
| APP-NAME field in syslog messages |
Example — enable syslog forwarding to a local collector:
PORT=8080 \
SERVER_NAME=enterprise-integrations \
SYSLOG_HOST=10.0.1.5 \
SYSLOG_PORT=514 \
SYSLOG_PROTOCOL=udp \
node index.jsMCP Protocol Support
The server implements MCP spec 2024-11-05 over two transports.
Streamable HTTP transport (POST /mcp)
Standard JSON-RPC 2.0 over HTTP. Clients that send Accept: text/event-stream receive an SSE-wrapped response; others receive a plain JSON response.
Handshake:
# Initialize
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"test","version":"1.0"},"capabilities":{}}}'
# List tools
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# Call a tool
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"confluence_search","arguments":{"cql":"type=page AND space=ENG"}}}'SSE transport (GET /sse + POST /messages)
For clients that require a persistent SSE connection (e.g., older MCP SDKs).
# 1. Open SSE connection — note the session endpoint in the response
curl -N http://localhost:3000/sse
# event: endpoint
# data: /messages?sessionId=<uuid>
# 2. Send RPC over the session (in a separate terminal)
curl -s -X POST "http://localhost:3000/messages?sessionId=<uuid>" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'Server discovery
curl http://localhost:3000/.well-known/mcpSupported Tools
Bitbucket (3 tools)
Tool | Description |
| Search workspaces by name/description/metadata |
| Full-text code search across repositories |
| Search and retrieve pipeline build artifacts |
Cassandra (3 tools)
Tool | Description |
| List keyspaces with replication config |
| Execute a CQL SELECT query |
| Cluster name, version, data centers, nodes |
Elasticsearch (3 tools)
Tool | Description |
| List indices with health, doc count, size |
| Search log indices with query string |
| Cluster name, status, node count, version |
PostgreSQL (3 tools)
Tool | Description |
| List databases with owner and size |
| Execute a SQL SELECT query |
| Server version, current DB, settings snapshot |
Confluence (2 tools)
Tool | Description |
| Retrieve a page by title (returns body HTML) |
| CQL query returning page titles and excerpts |
GitHub (4 tools)
Tool | Description |
| Repository search with topics, visibility, stars |
| Code search with file path and text matches |
| List commits for an owner/repo/branch |
| PR review comments with file/line references |
GitLab (4 tools)
Tool | Description |
| Project search with web URL and visibility |
| Code search scoped to a project |
| Commit list for a project ID and ref |
| Merge request notes with author and thread type |
Google Workspace (5 tools)
Tool | Description |
| Full-text search across Drive files |
| Read spreadsheet cell values by file name |
| Read document body by file name |
| Search Chat messages across spaces |
| Retrieve presentation slides and elements |
Jenkins (2 tools)
Tool | Description |
| Search build logs by job name and pattern |
| SCM config: repo URLs, credentials IDs, branch specs |
Jira (2 tools)
Tool | Description |
| JQL query returning issues with fields and pagination |
| Full issue detail by key (e.g. |
Slack (3 tools)
Tool | Description |
| User profile by Slack ID or username |
| Message search across channels |
| List channels with member count and privacy flag |
Salesforce (4 tools)
Tool | Description |
| Execute a SOQL query against standard objects |
| List report library with folder and last-run date |
| Full report data by name |
| Account detail with contacts, opportunities, cases |
Dashboard
The forensic dashboard is a Vue 3 SPA served from dashboard/.
Development mode (hot reload, proxies API to port 3000):
cd dashboard
npm install
npm run dev
# Vite starts on http://localhost:5173Production build (served by the Express server at /):
cd dashboard
npm run build
# Output written to dashboard/dist/
# Then just: node index.js (serves dist/ as static files)What the dashboard shows:
Total requests, unique IPs, requests in the last hour — live-updated via
/api/eventsTimeline chart: requests per minute over the last 60 minutes
Top tools invoked (bar chart)
Top source IPs (bar chart)
MCP method breakdown (initialize / tools/list / tools/call)
Paginated, filterable access log table — filter by IP, tool, MCP method, or time range
Syslog Integration
When SYSLOG_HOST is set, every logged access event is forwarded as an RFC 5424 message with a structured-data element containing id, ip, mcp_method, and tool.
Message format:
<133>1 2026-04-22T14:30:00.000Z hostname mcp-decoy 1234 tools/call [id="<uuid>" ip="10.0.1.42" mcp_method="tools/call" tool="confluence_search"] MCP tool call: confluence_search from 10.0.1.42The PRI value 133 = facility 16 (local0) × 8 + severity 5 (notice).
Splunk (Universal Forwarder or HEC)
Via UDP syslog input:
SYSLOG_HOST=splunk-indexer.corp.internal \
SYSLOG_PORT=514 \
SYSLOG_PROTOCOL=udp \
node index.jsConfigure a UDP input in Splunk (Settings → Data Inputs → UDP) on port 514, sourcetype syslog.
Recommended search:
index=main sourcetype=syslog app="mcp-decoy"
| rex field=_raw "\[id=\"(?P<id>[^\"]+)\" ip=\"(?P<src_ip>[^\"]+)\" mcp_method=\"(?P<method>[^\"]+)\" tool=\"(?P<tool>[^\"]+)\"\]"
| stats count by src_ip, tool
| sort -countQRadar
Forward via UDP syslog to a QRadar Log Source configured as Syslog type. The structured-data fields will appear in the raw event. Create a custom DSM property extraction for the tool and ip fields from the structured-data segment.
SYSLOG_HOST=qradar.corp.internal \
SYSLOG_PORT=514 \
SYSLOG_PROTOCOL=udp \
node index.jssyslog-ng
source s_mcp_decoy {
network(
ip("0.0.0.0")
port(514)
transport("udp")
);
};
destination d_mcp_decoy {
file("/var/log/mcp-decoy/access.log"
template("${ISODATE} ${HOST} ${MSG}\n")
);
};
filter f_mcp_decoy {
program("mcp-decoy");
};
log {
source(s_mcp_decoy);
filter(f_mcp_decoy);
destination(d_mcp_decoy);
};Graylog
Create a UDP GELF or Syslog input on port 514. Configure an extractor on the message field to parse structured-data key-value pairs:
Extractor type: Grok
Named captures: \[id="%{DATA:mcp_id}" ip="%{IP:src_ip}" mcp_method="%{DATA:mcp_method}" tool="%{DATA:tool}"\]TCP mode (for reliable delivery to Graylog):
SYSLOG_HOST=graylog.corp.internal \
SYSLOG_PORT=514 \
SYSLOG_PROTOCOL=tcp \
node index.jsTCP transport maintains a persistent connection and buffers messages during reconnect.
Testing
# Run all tests (111 tests)
npm test
# Watch mode
npm run test:watch
# Coverage report (V8 provider)
npm run test:coverageTests are in test/ using Vitest 4 and Supertest:
File | Scope | Count |
| Unit — all 38 tool dispatchers, schema validation, fake data shapes | ~70 |
| Integration — HTTP endpoints, MCP protocol handshake, both transports | ~30 |
| Unit — LogStore circular buffer, query filters, stats, timeline | ~11 |
Deployment
Docker Compose
version: "3.9"
services:
mcp-decoy:
image: node:20-alpine
working_dir: /app
volumes:
- .:/app
command: sh -c "npm install --omit=dev && node index.js"
ports:
- "3000:3000"
environment:
PORT: "3000"
SERVER_NAME: "enterprise-integrations"
SYSLOG_HOST: "${SYSLOG_HOST:-}"
SYSLOG_PORT: "${SYSLOG_PORT:-514}"
SYSLOG_PROTOCOL: "${SYSLOG_PROTOCOL:-udp}"
SYSLOG_APP_NAME: "mcp-decoy"
restart: unless-stopped
dashboard:
image: node:20-alpine
working_dir: /app/dashboard
volumes:
- .:/app
command: sh -c "npm install && npm run dev -- --host 0.0.0.0"
ports:
- "5173:5173"
depends_on:
- mcp-decoy
environment:
VITE_API_BASE: "http://mcp-decoy:3000"
restart: unless-stoppeddocker compose up -dFor a production deployment, build the dashboard first and let the Express server serve the static files — no separate dashboard container is needed:
services:
mcp-decoy:
image: node:20-alpine
working_dir: /app
volumes:
- .:/app
command: >
sh -c "npm install --omit=dev &&
cd dashboard && npm install && npm run build && cd .. &&
node index.js"
ports:
- "3000:3000"
environment:
PORT: "3000"
SERVER_NAME: "enterprise-integrations"
SYSLOG_HOST: "${SYSLOG_HOST:-}"
restart: unless-stoppedForensic Use
Log structure
Each access event stored in the log has the following fields:
Field | Description |
| UUID — unique identifier for the event, also used as the syslog MSGID |
| ISO 8601 timestamp |
| Source IP (respects |
| HTTP method |
| HTTP path |
|
|
| MCP JSON-RPC method ( |
| Tool name — only present on |
| Tool arguments as supplied by the client — only present on |
| MCP |
Querying the API
# All logs, paginated
curl 'http://localhost:3000/api/logs?limit=50&offset=0'
# Filter by source IP
curl 'http://localhost:3000/api/logs?ip=10.0.1.42'
# Filter by tool
curl 'http://localhost:3000/api/logs?tool=confluence_search'
# Filter by MCP method
curl 'http://localhost:3000/api/logs?mcp_method=tools/call'
# Filter by time range (ISO 8601)
curl 'http://localhost:3000/api/logs?from=2026-04-22T00:00:00Z&to=2026-04-22T23:59:59Z'
# Aggregated statistics
curl 'http://localhost:3000/api/stats'
# Timeline (requests per minute, last 60 min)
curl 'http://localhost:3000/api/timeline?minutes=60'Interpreting attacker behavior
Phase 1 — Reconnaissance
An attacker will typically begin with initialize followed immediately by tools/list. This is the cheapest way to enumerate what the server exposes. A single IP calling tools/list once and nothing else is normal for a scanner; the same IP proceeding to tools/call indicates active exploitation.
High-signal tool calls
The following tool invocations indicate targeted data exfiltration attempts rather than casual reconnaissance:
confluence_searchorconfluence_get_pagewith queries containingcredentials,password,secret,api_key, orrunbookgithub_search_code/gitlab_search_code/bitbucket_search_codewith queries containing environment variable names, tokens, or.envjenkins_getjobscm— retrieves credential IDs used in pipeline SCM configurationspostgresql_execute_select_queryorcassandra_execute_select_querywithSELECT *or queries targeting user/session tablesslack_get_user_infoorslack_conversations_search_messages— often used to build a contact map or find credentials shared in chatsalesforce_get_accountwith known customer names — indicates CRM exfiltration
Behavioral patterns to correlate
Pattern | Interpretation |
Single IP, | Automated scanner / probe |
Single IP, sequential tool calls across services (Jira → GitHub → Confluence) | Methodical human attacker or agent doing lateral reconnaissance |
Multiple IPs, same tool, similar arguments within a short window | Coordinated attack or shared tooling |
| Attacker using returned fake data to guide next steps |
| Confirms a hijacked or misrouted LLM client session |
Correlating with syslog
The id field is shared between the in-memory log and the syslog MSGID. Use it to correlate events across your SIEM and the dashboard. The args field in the in-memory log (not forwarded to syslog) contains the full tool arguments — useful for understanding exactly what data the attacker was seeking.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
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/gweber/mcp-decoy'
If you have feedback or need assistance with the MCP directory API, please join our Discord server