Enrichment MCP Server
# Enrichment MCP Server
This project is a Model Context Protocol (MCP) server for performing enrichment given a provided observable. The combination of configured services and the provided observable(s) will determine which enrichment services to call.
This tool provides a simple MCP server implementation to perform third-party enrichment using common services (e.g. VirusTotal, Hybrid Analysis, etc.) utilizing the [security-cli](https://github.com/MSAdministrator/security-cli) python package to perform enrichment/communicate with different services.
## MCP Server
This implementation of the `enrichment-mcp` MCP server exposes the following [tools](https://modelcontextprotocol.io/docs/concepts/tools).
* lookup-observable - A generic endpoint which examines and routes the given observable to the correct tool.
## Supported Services
The following services and observable types are currently supported:
> If you have any suggestions or believe another service should be implemented, please create an issue or pull request!
| Name | API Key Required | Supports IP | Supports Domain | Supports URL | Supports Email |
|------|------------------|-------------|-----------------|--------------|----------------|
| VirusTotal | Yes | Yes | Yes | Yes | No |
| HybridAnalysis | Yes | Yes | Yes | Yes | No |
| AlienVault | Yes | Yes | Yes | Yes | No |
| Shodan | Yes | Yes | Yes | Yes | No |
| Urlscan.io | Yes | Yes | Yes | Yes | No |
| AbuseIPDB | Yes | Yes | No | No | No |
| HaveIBeenPwned | Yes | No | No | No | Yes |
## Requirements
This MCP service uses `security-cli` and a custom [config.yaml.example](./config.yaml.example) file to determine which third-party enrichment services are supported for observable lookups.
The easiest way to run this on a local mac/system is:
```bash
uv run --env-file .env server.py
```
This requires that you use the provided template [.env.example](.env.example) and create a new `.env` file with your secrets.
> NOTE: Please review the documentation for [security-cli](https://github.com/MSAdministrator/security-cli) for information on configuring different services.
> The default will suffice for most use cases.
### Environmental Variables
> NOTE: It is highly recommended to set secrets as environmental variables when implementing this service. Stop storing secrets silly goose.
In order for the `security-cli` package to discover these variables, they must be in a specific format. Below is the list of currently supported variables:
* ENRICHMENT_MCP_VIRUSTOTAL_KEY
* ENRICHMENT_MCP_HYBRIDANALYSIS_KEY
* ENRICHMENT_MCP_ALIENVAULT_KEY
* ENRICHMENT_MCP_SHODAN_KEY
* ENRICHMENT_MCP_URLSCAN_KEY
* ENRICHMENT_MCP_ABUSEIPDB_KEY
* ENRICHMENT_MCP_HIBP_KEY
### Enrichments Configuration
Each enrichment service is defined in the `securiy-cli` config file. Additionally, I have broken out the different types of enrichment that can be performed. This means, in the current implementation, we have a single action type called `enrich` but in the future this can be expanded for things like `scans` or `queries` etc.
Underneath these high-level actions, we list out the observable type followed by a list of services that support that type. The currently supported observable types are:
* ipaddress - ipv4 addresses
* domain - A domain or netloc
* url - A fully qualified URL with schema, etc.
* email - A standard email address
We also support these types but they are currently not implemented:
* md5 - A file MD5 hash
* sha1 - A file SHA1 hash
* sha256 - A file SHA256 hash
Each service must have a `name` and a `template`. The `apikey` field can be provided but we recommend to use environmental variables.
### Prompt Templates
Each service and observable type can have it's own response template. These reside in the `security-cli` [templates](./templates/) directory and all templates are expected to exist here.
Each service defined has a prompt template using jinja2 templates. You can modify these are needed, but the format of the filename must remain the same.
These files have the following filename pattern.
```bash
{service.name}.{enrichment.type}.jinja2
```
> Ensure that the response object has the correct fields in the template itself or you will receive an error.
Below is an example output for a prompt of `Enrich this IP 91.195.240.94` with some errors mixed in:
```python
{
"virustotal": "error occurred looking up ip 91.195.240.94 in virustotal",
"alienvault": "Service: alienvault\nIPAddress: \nReputation Score: 0\nTotal Votes: ",
"shodan": "Service: shodan\nIPAddress: 91.195.240.94\nLast Analysis Results: 2025-04-25T21:02:52.644602\n\nTags\n\n\nAdditional information includes:\n\n* Latitude: 48.13743\n* Longitude: 11.57549\n* ASN: AS47846\n* Domains: ["servervps.net"]",
"hybridanalysis": "error occurred looking up ip 91.195.240.94 in hybridanalysis",
"urlscan": "Service: urlscan\nResult: https://urlscan.io/api/v1/result/01966efe-c8fa-74a4-bfc0-1ed479838e85/\n\nStats\n\n* uniqIPs - 6\n\n* uniqCountries - 2\n\n* dataLength - 432561\n\n* encodedDataLength - 218606\n\n* requests - 14\n\n\nPage\n* country - DE\n* server - Parking/1.0\n* ip - 91.195.240.94\n* mimeType - text/html\n* title - wearab.org\xa0-\xa0Informationen zum Thema wearab.\n* url - https://login.wearab.org/\n* tlsValidDays - 364\n* tlsAgeDays - 0\n* tlsValidFrom - 2025-04-25T00:00:00.000Z\n* domain - login.wearab.org\n* apexDomain - wearab.org\n* asnname - SEDO-AS SEDO GmbH, DE\n* asn - AS47846\n* tlsIssuer - Encryption Everywhere DV TLS CA - G2\n* status - 200\n",
"abuseipdb": "Service: abuseripdb\nIPAddress: 91.195.240.94\nLast Analysis Result: 2025-03-30T14:04:45+00:00\nScore: 7\nUsage: Data Center/Web Hosting/Transit\nIs Tor: False\nIs Whitelisted: False\nISP: Sedo Domain Parking"
}
```
## MCP Server Usage
For using a pre-built server, instructions from here: https://modelcontextprotocol.io/quickstart/user
* Download Claude for Desktop
* Install uv
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
* Download this repo and add to Claude for Desktop config
* Claude for Desktop > Settings > Developer > Edit Config
> You can copy the provided [.desktop_config.example.json](.desktop_config.example.json) file
If you want to create it yourself, these are the paths for Claude Desktop.
```bash
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
```
Open up the configuration file in any text editor. Replace the file contents with this:
```json
{
"mcpServers": {
"enrichment-mcp": {
"command": "/ABSOLUTE/PATH/TO/PARENT/FOLDER/uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/CLONED/REPOSITORY/enrichment-mcp",
"run",
"server.py"
]
}
}
}
```
4. Relaunch Claude for Desktop
You should now see two icons in the chat bar, a hammer which shows the tools available and a connection icon which shows the prompt defined and the input required.
## Contributing
Contributions are welcome! Please feel free to submit pull requests.
TDQS
Scored across 1 tool
With only one tool, there is no possibility of ambiguity or overlap between tools. The single tool 'lookup-observable' has a clear, distinct purpose of routing observables to appropriate handlers, leaving no room for confusion or misselection.
The naming is trivially consistent as there is only one tool. The tool name 'lookup-observable' follows a clear verb_noun pattern, and with no other tools to compare, there is no inconsistency in naming conventions.
A single tool is too few for a server named 'Enrichment MCP Server', which suggests a purpose of enriching various observables. This minimal toolset feels thin and inadequate for the implied scope, as it relies on internal routing rather than exposing a comprehensive set of enrichment operations directly.
The tool surface is severely incomplete for an enrichment server. There are obvious gaps, such as no direct tools for specific observable types (e.g., IP addresses, domains, files) or enrichment actions (e.g., threat intelligence lookup, geolocation). The single generic tool creates a dead end for agents, lacking the necessary coverage for the domain.