Skip to content

MCP Server, specs and documentation#21315

Merged
smcintyre-r7 merged 5 commits intorapid7:masterfrom
cdelafuente-r7:mcp-server
Apr 30, 2026
Merged

MCP Server, specs and documentation#21315
smcintyre-r7 merged 5 commits intorapid7:masterfrom
cdelafuente-r7:mcp-server

Conversation

@cdelafuente-r7
Copy link
Copy Markdown
Contributor

The Metasploit MCP Server (msfmcpd) provides AI applications with secure, structured access to Metasploit Framework data through the Model Context Protocol (MCP). It acts as a middleware layer between AI clients (such as Claude, Cursor, or custom agents) and Metasploit, exposing 8 standardized tools for querying reconnaissance data and searching modules. The implementation relies on the official Ruby SDK for MCP (here).

This initial implementation is read-only. Only tools that query data (modules, hosts, services, vulnerabilities, etc.) are available. Tools for module execution, session interaction, and database modifications will be added in a future iteration.

All the details can be found in the documentation here.

@cdelafuente-r7 cdelafuente-r7 added docs feature rn-enhancement release notes enhancement labels Apr 16, 2026
@cdelafuente-r7 cdelafuente-r7 marked this pull request as draft April 16, 2026 16:18
@cdelafuente-r7 cdelafuente-r7 marked this pull request as ready for review April 16, 2026 17:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an initial read-only Metasploit MCP Server (msfmcpd) implementation that exposes Metasploit data via MCP tools, plus supporting config/loading, RPC auto-start, logging, and a broad test suite (unit + integration).

Changes:

  • Introduces MCP server wrapper + 8 MCP tools (module search/info, DB hosts/services/vulns/notes/creds/loot).
  • Adds Metasploit RPC clients (MessagePack + JSON-RPC), response transformation, rate limiting, config loading/validation, and optional sanitized file logging.
  • Adds extensive RSpec coverage, WebMock-based integration tests, config fixtures/examples, and wires new dependencies (mcp, webmock).

Reviewed changes

Copilot reviewed 60 out of 61 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
spec/lib/msfmcp/security/rate_limiter_spec.rb Unit tests for token-bucket rate limiter + concurrency behavior.
spec/lib/msfmcp/metasploit/client_spec.rb Tests for protocol selection and delegation in client facade.
spec/lib/msfmcp/mcp/tools/vulnerability_info_spec.rb Tests MCP tool contract/behavior for vulnerability queries.
spec/lib/msfmcp/mcp/tools/service_info_spec.rb Tests MCP tool contract/behavior for service queries.
spec/lib/msfmcp/mcp/tools/search_modules_spec.rb Tests MCP tool contract/behavior for module searching + pagination.
spec/lib/msfmcp/mcp/tools/note_info_spec.rb Tests MCP tool contract/behavior for note queries.
spec/lib/msfmcp/mcp/tools/module_info_spec.rb Tests MCP tool contract/behavior for module details.
spec/lib/msfmcp/mcp/tools/loot_info_spec.rb Tests MCP tool contract/behavior for loot queries.
spec/lib/msfmcp/mcp/tools/host_info_spec.rb Tests MCP tool contract/behavior for host queries.
spec/lib/msfmcp/mcp/tools/credential_info_spec.rb Tests MCP tool contract/behavior for credential queries.
spec/lib/msfmcp/logging/logger_spec.rb Tests redaction + log-level filtering for file logger.
spec/lib/msfmcp/errors_spec.rb Tests inheritance/structure of new error classes.
spec/integration/msfmcpd/tool_execution_search_spec.rb End-to-end tool execution through HTTP stubs (module search).
spec/integration/msfmcpd/tool_execution_db_spec.rb End-to-end tool execution through HTTP stubs (DB tools).
spec/integration/msfmcpd/rate_limiting_spec.rb Integration coverage for rate limiting across tool calls.
spec/integration/msfmcpd/messagepack_auth_flow_spec.rb Integration coverage for MessagePack auth + token reuse.
spec/integration/msfmcpd/jsonrpc_auth_flow_spec.rb Integration coverage for JSON-RPC bearer auth behavior.
spec/integration/msfmcpd/error_handling_spec.rb Integration coverage for network/auth/API error conversions.
spec/integration/msfmcpd/config_loading_spec.rb Integration coverage for config defaults + env/CLI overrides.
spec/file_fixtures/config_files/msfmcpd/valid_messagepack.yaml Fixture config for MessagePack-mode integration tests.
spec/file_fixtures/config_files/msfmcpd/valid_jsonrpc.yaml Fixture config for JSON-RPC-mode integration tests.
msfrpcd Allows RPC credentials to be provided via environment variables.
msfmcpd Adds executable wrapper to run the MCP server application.
metasploit-framework.gemspec Adds runtime dependency on the MCP Ruby SDK gem.
lib/msfmcp/security/rate_limiter.rb Implements token-bucket rate limiter with mutex for concurrency.
lib/msfmcp/security/input_validator.rb Adds input validation helpers (query/ports/protocol/pagination/etc.).
lib/msfmcp/rpc_manager.rb Adds auto-start management for local msfrpcd lifecycle.
lib/msfmcp/metasploit/response_transformer.rb Converts RPC responses into MCP-friendly structured output.
lib/msfmcp/metasploit/messagepack_client.rb Implements MessagePack RPC client with retry + logging hooks.
lib/msfmcp/metasploit/jsonrpc_client.rb Implements JSON-RPC client using bearer token auth.
lib/msfmcp/metasploit/client.rb Facade that selects MessagePack vs JSON-RPC implementation.
lib/msfmcp/mcp/tools/vulnerability_info.rb MCP tool implementation for vulnerabilities query.
lib/msfmcp/mcp/tools/service_info.rb MCP tool implementation for services query.
lib/msfmcp/mcp/tools/search_modules.rb MCP tool implementation for module searching.
lib/msfmcp/mcp/tools/note_info.rb MCP tool implementation for notes query.
lib/msfmcp/mcp/tools/module_info.rb MCP tool implementation for module details.
lib/msfmcp/mcp/tools/loot_info.rb MCP tool implementation for loot query.
lib/msfmcp/mcp/tools/host_info.rb MCP tool implementation for hosts query.
lib/msfmcp/mcp/tools/credential_info.rb MCP tool implementation for credentials query.
lib/msfmcp/mcp/server.rb Wraps MCP server startup and stdio/http transports with logging hooks.
lib/msfmcp/logging/logger.rb Adds opt-in JSONL file logger with recursive sanitization.
lib/msfmcp/errors.rb Defines error taxonomy across config/security/metasploit/mcp layers.
lib/msfmcp/config/validator.rb Adds config validation for core msf_api + mcp settings.
lib/msfmcp/config/loader.rb Loads YAML config, applies defaults, supports env overrides.
lib/msfmcp.rb Main library entrypoint requiring all MCP/server components.
docs/navigation.rb Adds MCP server doc page to documentation navigation.
config/mcp_config_jsonrpc.yaml.example Example JSON-RPC config for MCP server.
config/mcp_config.yaml.example Example MessagePack config for MCP server.
Gemfile.lock Adds mcp and test deps (webmock and transitive gems).
Gemfile Adds webmock to test group for HTTP stubbing in specs.

Comment thread lib/msf/core/mcp/metasploit/messagepack_client.rb
Comment thread lib/msf/core/mcp/metasploit/jsonrpc_client.rb
Comment thread lib/msfmcp/security/input_validator.rb Outdated
Comment thread spec/lib/msfmcp/security/rate_limiter_spec.rb Outdated
Comment thread spec/lib/msf/core/mcp/security/rate_limiter_spec.rb
Comment thread lib/msfmcp/metasploit/response_transformer.rb Outdated
Comment thread lib/msfmcp/metasploit/response_transformer.rb Outdated
Comment thread lib/msf/core/mcp/config/validator.rb
Comment thread spec/lib/msfmcp/security/rate_limiter_spec.rb Outdated
Comment thread spec/integration/msfmcpd/rate_limiting_spec.rb
Copy link
Copy Markdown
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to hook it up to a running Metasploit instance, and it's possible I'm doing something wrong but I couldn't get it to connect, it just hangs.

Image

Comment thread lib/msfmcp/config/loader.rb Outdated
Comment thread lib/msf/core/mcp/logging/logger.rb Outdated
Comment on lines +15 to +27
class Logger
SENSITIVE_PATTERNS = {
password: /password[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
token_keyval: /token[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
token_header: /token\s+[a-zA-Z0-9_\-\.]+/i,
api_key: /api[_-]?key[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
secret: /secret[_-]?key[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
credential: /credential[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
auth: /auth[\"']?\s*[:=]\s*[\"']?[^\"',\s}]+/i,
bearer: /bearer\s+[a-zA-Z0-9_\-\.]+/i
}.freeze

REDACTED = '[REDACTED]'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably needs to stay here for now though I could definitely see it being helpful in the future. The rest of this module though should use Rex::Logging now that it's available. That'll let us swap sinks and use the same logging subsystem as the rest of MSF.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Framework is not loaded in the MCP server at this point. This is not necessary since there is no dependency; the MCP server communicated via RPC only. I tried to load all the individual files related to logging, but it is really messy and complex. Using Rex::Logging would require a big refactoring of the MCP server, with probably many design changes. Do you think it is worth the effort? What would be the benefit of using Rex::Logging in this context?

If you still think it's a real blocker, I will spend time trying to redesign the whole logging system.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I have two concerns about leaving it as it is:

  1. It might be copied into new new code either by a human or an LLM thinking it's the correct pattern moving forward
  2. While the MCP server is not loading Framework, once Support starting the MCP server from within msfconsole #21297 is implemented, it's likely that Framework will need to load the MCP server at which point it will be helpful to have a unified log interface.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would something like this work? cdelafuente-r7#5 I still need to test it but I think it's working pretty well. It's mapping the original log levels to the rex names/numbers but we could change that so it's Rex::Logging first and foremost if we wanted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your PR @smcintyre-r7, I redesigned the logging capability using Rex::Logging and Rack middleware. I wanted to keep the JSON-based logs and the way the MCP server filters the logging level. Rex::Logging use a log level mechanism for this. Therefore, I mapped the the debug type to the log level (e.g. DEBUG -> LVL3, INFO -> LVL2, etc.). This way, it is still possible to filter the kind of log we want to be logged (e.g. DEBUG) and abstracting the Rex::Logging log level concept to the user. Please, let me know what you think.

Comment thread msfmcpd Outdated
Comment thread msfmcpd Outdated
@github-project-automation github-project-automation Bot moved this from Todo to Waiting on Contributor in Metasploit Kanban Apr 16, 2026
@cdelafuente-r7
Copy link
Copy Markdown
Contributor Author

Thank you @smcintyre-r7 for your review and testing. The hang you observed seems to be related to the MSF RPC daemon itself. The connection times out and I could reproduce the same issue using the msfrpc client:

./msfrpc -a 127.0.0.1 -p 55552 -U msf -P DNBN6QQp
[nothing happens]

I didn't know there were a msfconsole plugin for RPC and didn't test it. I always started it with msfrpcd or letting the MCP server start it.

Also, I'm looking into implementing your suggestions right now.

- add the `--mcp-transport` option
- prefix the MCP env. variable with `MSF_`
- move the code under `lib/msf/core/mcp/`
- move specs under `spec/lib/msf/core/mcp/`
- change the namespace from `MsfMcp` to `Msf::RPC`
- update the `lib/msf_autoload.rb` to exclude the mcp-related files
- add missing validation for the `mcp`, `rate_limit and `logging` sections in the config file
- remove duplicate error exception classes
- fix an error in the transformers related to the `created_at` field
- fix a small issue in the input validator when regex are used
- update the way error is reported for MCP Tools to be compatible with the changes in the new `mcp` gem
- update and add specs
@cdelafuente-r7
Copy link
Copy Markdown
Contributor Author

I implemented all the suggested changes, except the one related to Rex::Logging (see my comment). Thanks!

smcintyre-r7 and others added 3 commits April 20, 2026 18:14
- remove the original Logger
- use Rex::Logging with helper methods (dlog, ilog, etc.)
- add `sanitize` configuration option
- create Sanitizing, JsonFlatfile and JsonStream sinks for JSON logging format
- minor updates in apply_default (Loader)
- update the re-authentication logic (fix a specific usecase)
- add a Rack middleware that logs MCP HTTP request/response
- use Rex::Socket::Tcp instead of TcpSocket
- update the ensure_rpc_available for better validation
- use around_request instead of the deprecated SDK instrumentation for logging
- update and add specs
Copy link
Copy Markdown
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New logging looks good. I setup my config like this so I could watch it while testing:

# Logging (optional - defaults shown)
logging:
  enabled: true
  level: DEBUG  # DEBUG, INFO, WARN, ERROR
  log_file: /dev/stdout
  sanitize: false

After loading it up and testing all 8 different methods everything looks pretty good. I noticed a single issue in msf_host_info for which I'll open a dedicated ticket as to not postpone this further.

Testing Output
claude
╭─── Claude Code v2.1.123 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                    │ Tips for getting started                                                                                                                                                          │
│                Welcome back Spencer!               │ Run /init to create a CLAUDE.md file with instructions for Claude                                                                                                                 │
│                                                    │ Note: You have launched claude in your home directory. For the best experience, launch it in a project directory instead.                                                         │
│                       ▐▛███▜▌                      │ ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │
│                      ▝▜█████▛▘                     │ What's new                                                                                                                                                                        │
│                        ▘▘ ▝▝                       │ Fixed OAuth authentication failing with a 401 retry loop when `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1` is set                                                                   │
│   Opus 4.7 (1M context) with xh… · Claude Max ·    │ Added `ANTHROPIC_BEDROCK_SERVICE_TIER` environment variable to select a Bedrock service tier (`default`, `flex`, or `priority`), sent as the `X-Amzn-Bedrock-Service-Tier` header │
│   zerosteiner@gmail.com's Organization             │ Pasting a PR URL into the `/resume` search box now finds the session that created that PR (GitHub, GitHub Enterprise, GitLab, and Bitbucket)                                      │
│                  /home/smcintyre                   │ /release-notes for more                                                                                                                                                           │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

❯ I need to test my Metasploit MCP server. Can you please help me with that? You are connected to it.

● I'll load the Metasploit MCP tool schemas and run some tests against them.

● I'll exercise each tool in parallel — searching modules, fetching module details, and querying the database for hosts/services/vulns/creds/loot/notes.

  Called metasploit 8 times (ctrl+o to expand)

● All 8 Metasploit MCP tools responded successfully. Quick summary of what came back:

  Module tools
  - msf_search_modules — "eternalblue" returned 5 modules (auxiliary + exploits) with rank/disclosure date
  - msf_module_info — full details for exploit/windows/smb/ms17_010_eternalblue (description, options, targets, references, CVEs)

  Database tools (workspace: default)
  - msf_host_info — 3 hosts: 192.168.159.10, 192.168.159.132 (ubuntu2404), 192.168.159.136
  - msf_service_info — 9 services (smb/dcerpc/ldap/http on ports 139/389/445/8080)
  - msf_vulnerability_info — 3 vulns (SSH user code exec, LDAP login, cve_2026_31431_copy_fail)
  - msf_credential_info — 5 creds (LDAP password + NTLM/NetNTLM hashes for smcintyre)
  - msf_loot_info — 0 entries (empty, returned cleanly)
  - msf_note_info — 2 notes (SMB domain lookupsid, host OS fingerprint)

  Each response includes consistent metadata (query_time, total_items, returned_items, limit, offset) and a data array — pagination contract looks solid, and the empty-result case (loot) returns an empty array rather than erroring.

  Want me to also exercise the filter parameters (e.g. host/ports/protocol/names/only_up/addresses CIDR), pagination via offset, and error paths (invalid workspace, bad module name)?

✻ Cooked for 26s

❯ Yes please

● Running filter, pagination, and error-path tests in parallel.

  Called metasploit 11 times (ctrl+o to expand)

● Most paths work cleanly, but I spotted something suspicious: the CIDR 192.168.159.0/24 with only_up=true returned 0 hosts even though all 3 known hosts are in that range and marked state: "alive". Let me isolate whether it's the CIDR filter or only_up.

  Called metasploit 3 times (ctrl+o to expand)

● Here are the consolidated results from the second batch:

  Filters — working
  - msf_service_info host + ports range + protocol: 4 services on 192.168.159.10:389-445/tcp ✅
  - msf_service_info names=smb,ldap: 4 services, http/dcerpc/tcp correctly excluded ✅
  - msf_vulnerability_info host + names array: 1 result for "SSH User Code Execution" on 192.168.159.132 ✅
  - msf_note_info host + type: 1 result for smb.domain.lookupsid on 192.168.159.10 ✅
  - msf_host_info single IP: 1 result ✅
  - msf_host_info only_up=true alone: 3 results ✅

  Pagination
  - msf_search_modules query=smb: total_items=240, offset=0 and offset=3 returned 3 distinct results each — page boundary clean ✅

  Error paths
  - Invalid workspace → Metasploit API error: Invalid workspace ✅
  - Invalid module name → Metasploit API error: Invalid Module ✅
  - Empty result set (zzzzznoresults...) → total_items=0 with empty data array, no error ✅

  🐛 Bug — CIDR filtering in msf_host_info

  The addresses parameter does not expand CIDR ranges:
  - addresses=192.168.159.10 (single IP) → 1 result ✅
  - addresses=192.168.159.0/24 (CIDR) → 0 results ❌ — should return all 3 hosts (192.168.159.10, .132, .136)
  - addresses=192.168.159.0/24 + only_up=true → 0 results (same root cause)

  The CIDR is documented in the parameter description ("e.g., \"192.168.1.100\" or \"192.168.1.0/24\""), so this is a contract violation rather than a missing feature. Likely the server is doing an exact-string address match instead of subnet containment. Worth checking the host
  query handler for IPAddr/Subnet parsing on the addresses argument.

  Everything else looks solid — 8/8 tools functional, consistent metadata envelope, sane error messages.

✻ Cooked for 49s

@github-project-automation github-project-automation Bot moved this from Waiting on Contributor to In Progress in Metasploit Kanban Apr 30, 2026
@smcintyre-r7 smcintyre-r7 merged commit a0c5b9a into rapid7:master Apr 30, 2026
60 of 61 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Metasploit Kanban Apr 30, 2026
@smcintyre-r7 smcintyre-r7 added this to the Version 6.5 milestone Apr 30, 2026
@smcintyre-r7
Copy link
Copy Markdown
Contributor

Release Notes

This adds a read-only MCP server for Metasploit capable of retrieving information from the loaded modules and database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs feature rn-enhancement release notes enhancement

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Add a read-only MCP server to Metasploit

3 participants