Cisco PSIRT OpenVuln MCP Server
by aldoleiva1
README.md
# Cisco PSIRT OpenVuln MCP Server
A production-quality [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for querying Cisco security advisories via the PSIRT OpenVuln API v2. Built with Python and FastMCP, it enables LLM-powered interfaces like Claude Desktop and Kiro to search, filter, and analyze Cisco vulnerability data.
## Features
- **15 MCP tools** covering all Cisco PSIRT OpenVuln API v2 endpoints
- **OAuth2 authentication** with automatic token caching and refresh (client_credentials flow)
- **Multi-tier rate limiting** — 5 calls/sec, 30 calls/min, 5,000 calls/day (client-side enforcement)
- **Server-side 429 retry** — automatic retry with Retry-After header support (up to 3 attempts)
- **Input validation** on all parameters with descriptive error messages
- **Pagination support** across all list endpoints (page_index, page_size)
- **LLM-optimized responses** — consistent structured envelope with summary truncation
- **Transport-agnostic** — supports stdio (default) and SSE transports
- **Structured error handling** — classified errors with user-friendly messages
## Prerequisites
- **Python 3.10+**
- **Cisco API Console credentials** (client_id and client_secret) — see [Getting Cisco API Credentials](#getting-cisco-api-credentials)
## Installation
1. **Clone the repository:**
```bash
git clone <repository-url>
cd openvuln-mcp-server
```
2. **Create a virtual environment:**
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. **Install dependencies:**
```bash
pip install -r requirements.txt
```
4. **Configure credentials:**
```bash
cp .env.example .env
```
Edit `.env` and fill in your Cisco API credentials:
```dotenv
CISCO_CLIENT_ID=your_client_id_here
CISCO_CLIENT_SECRET=your_client_secret_here
```
## Getting Cisco API Credentials
1. Go to the [Cisco API Console](https://apidocs-prod.cisco.com/) and sign in (or create an account)
2. Register a new application
3. Enable the **"Cisco PSIRT openVuln API"** for your application
4. Copy the generated `client_id` and `client_secret`
5. Add them to your `.env` file or pass them as environment variables
Each user's credentials are independent — rate limits are tracked per client_id on Cisco's side.
## Usage
### stdio mode (default)
Standard transport for local MCP integrations (Claude Desktop, Kiro):
```bash
python main.py
```
### SSE mode
HTTP-based Server-Sent Events transport for web or remote integrations:
```bash
python main.py --transport sse --port 8080
```
## MCP Client Configuration
### Claude Desktop / Kiro (stdio)
Add to your MCP client configuration file:
```json
{
"mcpServers": {
"cisco-openvuln": {
"command": "python",
"args": ["main.py"],
"cwd": "/path/to/openvuln-mcp-server",
"env": {
"CISCO_CLIENT_ID": "your_client_id",
"CISCO_CLIENT_SECRET": "your_client_secret"
}
}
}
}
```
### SSE client
```json
{
"mcpServers": {
"cisco-openvuln": {
"url": "http://localhost:8080/sse"
}
}
}
```
## Available Tools
| # | Tool | Description | Key Parameters |
|---|------|-------------|----------------|
| 1 | `get_all_advisories` | Retrieve all published advisories with pagination | `page_index`, `page_size` |
| 2 | `get_advisory_by_id` | Look up a specific advisory by Cisco advisory ID | `advisory_id` (max 100 chars) |
| 3 | `get_advisory_by_cve` | Find advisories by CVE identifier | `cve_id` (CVE-YYYY-NNNNN format) |
| 4 | `get_advisory_by_bug_id` | Find advisories by Cisco Bug ID | `bug_id` (CSCxxNNNNN format) |
| 5 | `get_latest_advisories` | Get the N most recently published advisories | `number` (1–100, default 5) |
| 6 | `get_advisories_by_severity` | Filter advisories by severity level | `severity` (critical/high/medium/low/informational) |
| 7 | `get_advisories_by_severity_first_published` | Filter by severity and first-published date range | `severity`, `start_date`, `end_date` |
| 8 | `get_advisories_by_severity_last_published` | Filter by severity and last-updated date range | `severity`, `start_date`, `end_date` |
| 9 | `get_advisories_by_first_published` | Find advisories first published in a date range | `start_date`, `end_date` (YYYY-MM-DD) |
| 10 | `get_advisories_by_last_published` | Find advisories last updated in a date range | `start_date`, `end_date` (YYYY-MM-DD) |
| 11 | `get_advisories_by_product` | Search advisories by product name | `product_name` |
| 12 | `get_advisories_by_year` | Get advisories published in a specific year | `year` (1995–current) |
| 13 | `get_advisories_by_os_version` | Find advisories for a specific OS type and version | `os_type`, `version`, `platform_alias` (optional) |
| 14 | `get_os_version_data` | Get available OS version metadata | `os_type` |
| 15 | `get_platform_aliases` | List platform aliases for an OS type | `os_type` (nxos/asa/ftd/fxos only) |
All list-returning tools support `page_index` (1–100) and `page_size` (1–100) pagination parameters.
## Running Tests
Run the full test suite:
```bash
pytest
```
Run with coverage:
```bash
pytest --cov=src --cov-report=term-missing
```
Run a specific test file:
```bash
pytest tests/test_validators.py
```
Run property-based tests (Hypothesis):
```bash
pytest tests/ -k "property"
```
## Rate Limits
The server enforces client-side rate limiting to stay within Cisco API quotas:
| Tier | Limit | Enforcement |
|------|-------|-------------|
| Per-second | 5 calls/sec | Minimum 200ms between requests |
| Per-minute | 30 calls/min | Rolling 60-second sliding window |
| Per-day | 5,000 calls/day | Calendar day counter, resets at 00:00 UTC |
When a rate limit is hit:
- **Per-second / per-minute**: The server automatically sleeps until the next request is permitted
- **Per-day**: Returns an error indicating the daily limit is exhausted, with seconds until reset
- **Server-side 429**: Retries up to 3 times using the `Retry-After` header (default 60s if absent)
## Project Structure
```
openvuln-mcp-server/
├── main.py # Entry point: loads env, parses args, starts server
├── src/
│ ├── __init__.py
│ ├── server.py # FastMCP server and 15 tool registrations
│ ├── oauth2_client.py # OAuth2 client_credentials with token caching
│ ├── rate_limiter.py # Multi-tier rate limiter
│ ├── api_client.py # HTTP client with auth, rate limiting, retry
│ ├── validators.py # Input validation functions
│ ├── response_formatter.py # LLM-optimized response formatting
│ ├── constants.py # URLs, limits, validation rules
│ └── exceptions.py # Custom exception hierarchy
├── tests/
│ ├── test_validators.py
│ ├── test_oauth2_client.py
│ ├── test_rate_limiter.py
│ ├── test_api_client.py
│ ├── test_response_formatter.py
│ └── test_tools.py
├── .env.example # Credentials template
├── requirements.txt # Python dependencies
└── README.md
```
## License
This project is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues