Skip to main content
Glama
ACSmith1337

Cisco Secure Access MCP Server

by ACSmith1337
README.md
# Cisco Secure Access MCP Server

A production-ready [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that wraps the Cisco Secure Access REST API. Enables AI agents to investigate threats, pull reports, manage security policies, and administer deployment infrastructure through a standardized interface.

## Summary

Cisco Secure Access (formerly Cisco Umbrella / Secure Web Gateway) provides DNS-layer security, web filtering, and Zero Trust network access. This MCP server bridges that platform to AI agents, enabling:

- **Threat Investigation** -- Look up domains, IPs, URLs, and user activity observed by Cisco's DNS resolvers
- **Security Reports** -- Pull activity, threat, and destination analytics with flexible time ranges
- **Policy Management** -- Create/update destination lists, access rules, security profiles, and DLP policies
- **Domain Blocking** -- One-tool block/unblock domains with automatic list management
- **VPN & Device Control** -- List active VPN sessions, disconnect users, revoke device certificates
- **Infrastructure Admin** -- Manage sites, networks, connectors, tunnel groups, and identity endpoints

The server implements the **Streamable HTTP** transport per the MCP specification, runs as a single Python process, and handles OAuth 2.0 token management automatically.

## Time Format (IMPORTANT)

**Cisco Secure Access API uses Unix epoch timestamps in milliseconds.** This server converts automatically.

All time-accepting tools support three input formats:

| Format | Example | Converted to |
|---|---|---|
| ISO 8601 | `2025-01-15T08:30:00Z` | `1736928600000` |
| Relative | `24h`, `7d`, `30m`, `900s` | epoch ms from now |
| Keyword | `now` | current epoch ms |
| Epoch ms | `1735689600000` | passed through |

## Architecture

```
+-------------+   JSON-RPC 2.0    +------------------+   OAuth2 + REST    +--------------------+
| MCP Client  | <----------------> | MCP Server       | <----------------> | Cisco Secure Access|
| (AI Agent)  |   Streamable HTTP | FastAPI +         |   api.sse.cisco.com| API                |
|             |   POST /mcp       | Tool Handlers     |                    |                    |
+-------------+                   +------------------+                    +--------------------+
                                      |
                                 +------------+
                                 | Token Cache|
                                 | per session|
                                 +------------+
```

## Quick Start

### Option 1: Docker Compose (Recommended)

```bash
cd cisco-secure-access-mcp

# Configure credentials
cp .env.example .env
# Edit .env with your Cisco API key and secret

# Build and run
docker compose up --build -d

# Check health
curl http://localhost:3000/health
```

### Option 2: Direct Docker

```bash
docker build -t cisco-mcp-server .

docker run -d --name cisco-mcp \
  -p 3000:3000 \
  -e CISCO_API_KEY=your_key_here \
  -e CISCO_API_SECRET=your_secret_here \
  cisco-mcp-server
```

### Option 3: Native Python

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

export CISCO_API_KEY=your_key_here
export CISCO_API_SECRET=your_secret_here
python server.py
```

## Configuration

| Variable | Required | Description | Default |
|---|---|---|---|
| `CISCO_API_KEY` | Yes | OAuth client ID from Cisco portal | -- |
| `CISCO_API_SECRET` | Yes | OAuth client secret | -- |
| `CISCO_ORG_ID` | No | Child org ID for multi-org setups | Parent org |
| `MCP_SERVER_PORT` | No | Listen port | `3000` |
| `MCP_SERVER_HOST` | No | Bind address | `0.0.0.0` |
| `LOG_LEVEL` | No | DEBUG, INFO, WARNING, ERROR | `INFO` |

Get credentials from the Cisco Secure Access portal: **Admin > API Keys > Create New Key**.

## Available Tools (18)

### Investigation & Forensics

| Tool | Description |
|---|---|
| `investigate_domain` | DNS query history, IPs, threats for a domain |
| `investigate_ip` | Domains, threats, geo-location for an IP address |
| `investigate_url` | Categorization, threat status for a specific URL |
| `investigate_user_activity` | Complete user history: DNS, web, blocks, threats |

### Reports & Analytics

| Tool | Description |
|---|---|
| `get_activity_report` | DNS queries, web requests, connection data |
| `get_top_threats` | Most frequent threat categories and sources |
| `get_top_destinations` | Most visited domains, IPs, and URLs |
| `get_top_identities` | Most active users by request volume |
| `get_top_ips` | Most contacted external IP addresses |
| `get_summary_report` | Aggregate traffic volume and threat counts |
| `get_threat_types` | Breakdown: malware, phishing, C2, etc. |
| `get_report` | Generic handler: app_discovery, metering, api_usage, dlp_events |

### Domain Blocking

| Tool | Description |
|---|---|
| `add_domain_to_block_list` | Block a domain (creates list if needed) |
| `remove_domain_from_block_list` | Unblock a domain from a list |

### VPN & Device Management

| Tool | Description |
|---|---|
| `list_vpn_connections` | Current VPN user sessions |
| `disconnect_vpn_user` | Terminate a specific VPN session |
| `revoke_device_certificates` | Revoke device access certificates |

### Generic Resource Management

| Tool | Description |
|---|---|
| `manage_resource` | CRUD for all Admin/Policies/Deployments resources |

`manage_resource` accepts:
- `domain`: `admin`, `policies`, `deployments`
- `resource`: resource slug (see lists below)
- `operation`: `list`, `create`, `update`, `delete`
- `resource_id`: required for update/delete
- `resource_params`: attributes dict for create/update
- `params`: query parameters for list operations

Available resources per domain:

**admin**: api_keys, user_devices, vpn_connections, integrations, alert_rules, tenants, threat_intel_feeds, tenant_controls, s3_bucket_key

**policies**: destination_lists, application_lists, access_rules, network_objects, security_feeds, security_profiles, content_categories, application_categories, ips_profiles, dlp_rules, do_not_decrypt, private_resources

**deployments**: tunnel_groups, tunnel_regions, connector_groups, connectors, roaming_computers, swg_device_settings, internal_domains, sites, networks, internal_networks, network_devices, dns_forwarders, identity_endpoints, virtual_appliances

## Usage Examples

### Investigate a suspicious domain
```json
{"tool": "investigate_domain", "arguments": {"domain": "suspicious-site.com", "limit": 20}}
```

### Get threat report for last 7 days
```json
{"tool": "get_top_threats", "arguments": {"start_time": "7d", "end_time": "now", "limit": 50}}
```

### List all deployment sites
```json
{"tool": "manage_resource", "arguments": {"domain": "deployments", "resource": "sites", "operation": "list"}}
```

### Block a malware domain
```json
{"tool": "add_domain_to_block_list", "arguments": {"domain": "malware.example.com"}}
```

### Create a new site definition
```json
{
  "tool": "manage_resource",
  "arguments": {
    "domain": "deployments",
    "resource": "sites",
    "operation": "create",
    "resource_params": {"name": "office-1", "ipAddresses": ["10.0.0.0/24"]}
  }
}
```

### Disconnect a compromised user VPN
```json
{"tool": "disconnect_vpn_user", "arguments": {"user_id": "compromised-user"}}
```

### Update a destination list
```json
{
  "tool": "manage_resource",
  "arguments": {
    "domain": "policies",
    "resource": "destination_lists",
    "operation": "update",
    "resource_id": "list-abc123",
    "resource_params": {"name": "blocked-sites", "entries": ["bad.com"]}
  }
}
```

## Error Handling

- **429 Rate Limited**: Automatic exponential backoff retry (up to 3 attempts)
- **5xx Server Errors**: Automatic retry with backoff (up to 3 attempts)
- **401/403 Auth Errors**: Returned immediately with clear diagnostic message
- **Token Expiry**: Automatic refresh 5 minutes before TTL expires
- **Connection Pooling**: httpx client with 20 max connections, 10 keepalive

## Best Practices

### Security

1. **Never hardcode credentials**. Always use `.env` files or environment injection via Docker secrets, Kubernetes secrets, or cloud secret managers.
2. **Rotate API keys regularly**. Use `manage_resource` with domain=`admin`, resource=`api_keys` to create/delete keys.
3. **Use scoped API keys**. Request only the permissions your use case needs.
4. **Run as non-root**. The Docker image uses a dedicated `appuser` account.
5. **Restrict network access**. Bind to `127.0.0.1` if the MCP client runs locally, or use a VPN/firewall for remote access.

### Deployment

1. **Use health checks**. The `/health` endpoint enables container orchestration liveness checks.
2. **Set resource limits**. In docker-compose, add `deploy.resources.limits` for CPU/memory.
3. **Enable structured logging**. Set `LOG_LEVEL=DEBUG` for troubleshooting, `INFO` for production.
4. **Monitor token usage**. API key creation tracks scopes and usage patterns via `get_report` with `report_type=api_usage`.

### Operational

1. **Prefer `manage_resource` for CRUD**. Covers 50+ endpoints with a single tool. Use dedicated tools only when convenience matters (e.g., `add_domain_to_block_list`).
2. **Use relative time offsets**. `24h`, `7d`, `30m` are simpler than ISO 8601 for most queries.
3. **Paginate large result sets**. Use `limit` and `offset` parameters for list operations.
4. **Handle rate limits gracefully**. The server retries automatically, but design your workflow to space out high-volume queries.
5. **Backup destination lists**. Before modifying block lists, `list` the current entries first.

### Multi-Org Support

For organizations with child orgs, set `CISCO_ORG_ID` to the child organization's ID. The server adds the `X-Umbrella-OrgId` header automatically to all API requests.

## Testing

```bash
pip install pytest pytest-asyncio
pytest test_server.py -v
```

Covers: epoch time conversion, tool registration, endpoint mapping, API retry logic, token caching, transport layer, and all 18 tool handlers.

## License

MIT