A Model Context Protocol (MCP) server that exposes Cisco Secure Access API operations as agent-callable tools over Streamable HTTP.
Repository: https://github.com/CiscoDevNet/secure-access-mcp-community
This server provides Cisco Secure Access tools for destination list management, domain investigation, access policy inspection, reports, activity, and infrastructure inventory. MCP-compatible clients can discover the tools, inspect their schemas, and call them from natural language prompts.
Protocol: Streamable HTTP
Default URL: http://127.0.0.1:8000/mcp
Python: 3.10+ (Python 3.11 or newer recommended)
| Tool | Description |
|---|---|
list_destination_lists |
List all destination lists in the organization |
get_destination_list |
Get details of a single destination list by ID |
get_destinations_in_list |
Get all destinations (domains, URLs, IPs) in a list |
search_domain_across_lists |
Search for a domain, URL, or IP across all destination lists |
find_repeating_destinations |
Find destinations that appear in multiple destination lists |
audit_stale_lists |
Audit destination lists for stale, empty, or low-count lists |
destination_usage_summary |
Show org-wide usage against the 250,000 destination limit |
| Tool | Description |
|---|---|
create_destination_list |
Create a new destination list with optional initial destinations |
update_destination_list |
Rename a destination list |
delete_destination_list |
Delete a destination list |
add_destinations_to_list |
Add domains, IPs, or URLs to a list, max 500 per request |
remove_destinations_from_list |
Remove destinations from a list by destination ID, max 500 per request |
| Tool | Description |
|---|---|
investigate_domain |
Get security risk information for a domain |
get_domain_categorization |
Get content or security categorization for a domain |
| Tool | Description |
|---|---|
list_access_rules |
List access policy rules |
get_access_rule_detail |
Get full details of an access rule |
create_access_rule |
Create an allow or block rule by categories or destination lists |
| Tool | Description |
|---|---|
get_security_summary |
Security summary for total, blocked, and allowed requests |
get_security_summary_by_type |
Security summary filtered by traffic type |
get_total_requests |
Total request counts across all traffic types |
get_total_requests_by_type |
Total request counts for a specific traffic type |
| Tool | Description |
|---|---|
get_top_destinations |
Top accessed destinations |
get_top_destinations_by_type |
Top destinations filtered by traffic type |
get_top_urls |
Top accessed URLs |
get_top_identities |
Top identities by request count |
get_top_identities_by_type |
Top identities filtered by traffic type |
get_top_categories |
Top content categories |
get_top_categories_by_type |
Top categories filtered by traffic type |
get_top_threats |
Top threats |
get_top_threats_by_type |
Top threats filtered by traffic type |
get_top_threat_types |
Top threat types |
get_top_threat_types_by_type |
Top threat types filtered by traffic type |
get_top_event_types |
Top event types |
get_top_dns_query_types |
Top DNS query types |
get_top_ips |
Top IP addresses |
get_top_ips_internal |
Top internal IP addresses |
get_top_files |
Top files seen in proxy traffic |
get_top_resources |
Top private resources |
get_top_resources_by_type |
Top private resources filtered by type |
| Tool | Description |
|---|---|
get_summaries_by_category |
Request summaries grouped by content category |
get_summaries_by_category_type |
Category summaries filtered by traffic type |
get_summaries_by_destination |
Request summaries grouped by destination |
get_summaries_by_destination_type |
Destination summaries filtered by traffic type |
get_summaries_by_rule_hitcount |
Access rule hit counts for specific rule IDs |
get_summaries_by_rule_firewall_hitcount |
Firewall rule hit counts |
get_summaries_by_rule_intrusion |
Intrusion prevention summaries grouped by rule |
| Tool | Description |
|---|---|
get_requests_by_hour |
Request counts aggregated by hour |
get_requests_by_hour_type |
Hourly request counts filtered by traffic type |
get_requests_by_timerange |
Request counts aggregated by time range |
get_requests_by_timerange_type |
Time-range request counts filtered by traffic type |
get_categories_by_hour |
Category counts aggregated by hour |
get_categories_by_hour_type |
Hourly category counts filtered by traffic type |
get_categories_by_timerange |
Category counts aggregated by time range |
get_categories_by_timerange_type |
Time-range category counts filtered by traffic type |
get_identity_distribution |
Identity distribution across requests |
get_identity_distribution_by_type |
Identity distribution filtered by traffic type |
get_bandwidth_by_hour |
Bandwidth usage by hour |
get_bandwidth_by_timerange |
Bandwidth usage by time range |
| Tool | Description |
|---|---|
get_activity |
Recent activity and security events |
get_activity_dns |
DNS-layer activity |
get_activity_proxy |
Web proxy activity |
get_activity_firewall |
Firewall activity |
get_activity_ip |
IP-layer activity |
get_activity_intrusion |
Intrusion prevention activity |
get_activity_amp |
AMP retrospective activity |
get_activity_ztna |
ZTNA activity |
get_activity_decryption |
SSL/TLS decryption activity |
get_rules_activity |
Policy rule activity |
get_unique_resources |
Count of unique private resources accessed |
| Tool | Description |
|---|---|
list_roaming_computers |
List roaming computers |
list_internal_networks |
List internal networks |
list_network_tunnels |
List network tunnel groups |
secure-access-mcp-community/
├── cisco_secure_access_mcp/
│ ├── __init__.py
│ ├── __main__.py # Entry point: python -m cisco_secure_access_mcp
│ ├── auth.py # OAuth2 client credentials token manager (cache, refresh, retry)
│ ├── client.py # Async Cisco Secure Access REST client (pool, retries, paginate, audit logs)
│ ├── logging_config.py # Structured JSON logging + secret redaction
│ ├── security.py # Auth, rate limit, payload limit, request IDs (ASGI middleware)
│ ├── server.py # FastMCP Streamable HTTP server + security wiring
│ └── tools/
│ ├── all_tools.py # MCP tool definitions
│ └── destination_lists.py
├── README.md
├── requirements.txt
├── .env.example
└── mcp_config.example.json
uv is a fast Python package and project manager. If you don't have it yet:
# macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh # or: brew install uv / pipx install uv
Clone the repository, create a Python 3.11 environment, and install dependencies:
git clone https://github.com/CiscoDevNet/secure-access-mcp-community.git cd secure-access-mcp-community # Create a virtual environment pinned to a supported interpreter. # uv downloads Python for you if it is not installed locally. uv venv --python 3.11 # Install dependencies from requirements.txt into the environment. uv pip install -r requirements.txt
Run the server without manually activating the venv:
uv run python -m cisco_secure_access_mcp
uv run automatically uses the project's .venv, so you do not need to source anything. You can still activate it the traditional way (source .venv/bin/activate) if you prefer.
git clone https://github.com/CiscoDevNet/secure-access-mcp-community.git cd secure-access-mcp-community python3.11 -m venv venv source venv/bin/activate python -m pip install --upgrade pip python -m pip install -r requirements.txt
If python3.11 is not available, use any Python 3.10+ interpreter. The mcp package is not available for Python 3.9, so python3 -m venv venv can fail on systems where python3 still points to Python 3.9.
Copy the example environment file and fill in your credentials:
cp .env.example .env
| Variable | Required | Default | Description |
|---|---|---|---|
SECURE_ACCESS_API_KEY |
Yes | - | Cisco Secure Access OAuth client ID |
SECURE_ACCESS_API_SECRET |
Yes | - | Cisco Secure Access OAuth client secret |
TOKEN_URL |
No | https://api.sse.cisco.com/auth/v2/token |
OAuth token endpoint |
HOST |
No | 127.0.0.1 |
Streamable HTTP bind host |
PORT |
No | 8000 |
Streamable HTTP bind port |
SECURE_ACCESS_USER_AGENT |
No | secure-access-mcp-community/<version> (+<repo-url>) |
User-Agent sent on every API call so the Cisco backend can attribute traffic to this MCP server |
MCP_AUTH_TOKEN |
Yes* | - | Bearer token MCP clients must send (Authorization: Bearer <token>). *Required unless MCP_ALLOW_NO_AUTH=true |
MCP_ALLOW_NO_AUTH |
No | false |
Run with no client authentication (testing only, not recommended; loopback host only) |
MCP_MAX_REQUEST_BYTES |
No | 1048576 |
Max inbound request body size (large-payload DoS protection) |
MCP_RATE_LIMIT_RPM |
No | 120 |
Per-client-IP rate limit (requests/min); 0 disables |
MCP_ALLOWED_HOSTS |
No | bound host + loopback | Comma-separated Host allowlist for DNS-rebinding protection |
MCP_ALLOWED_ORIGINS |
No | (none) | Comma-separated Origin allowlist |
MCP_DISABLE_DNS_REBINDING_PROTECTION |
No | false |
Disable Host/Origin validation (testing only) |
SECURE_ACCESS_REQUIRE_CONFIRMATION |
No | true |
Require confirm=true for destructive tools (two-stage commit) |
SECURE_ACCESS_REDACT_PII |
No | false |
Redact PII (identities, IPs, emails) from report/activity outputs |
LOG_LEVEL |
No | INFO |
Level for structured JSON logs (written to stderr) |
Generate a strong client token, then run the server (authentication is required by default):
export SECURE_ACCESS_API_KEY="your-api-key" export SECURE_ACCESS_API_SECRET="your-api-secret" export MCP_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" python -m cisco_secure_access_mcp
MCP clients must then send Authorization: Bearer $MCP_AUTH_TOKEN on every request.
With uv (no manual venv activation needed):
export SECURE_ACCESS_API_KEY="your-api-key" export SECURE_ACCESS_API_SECRET="your-api-secret" export MCP_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" uv run python -m cisco_secure_access_mcp
For isolated local experimentation only, you can start the server without client→server authentication. The server refuses to start without either MCP_AUTH_TOKEN or this flag, and refuses the no-auth mode on any non-loopback host:
# NOT RECOMMENDED: anyone who can reach the port can use your Cisco credentials.
MCP_ALLOW_NO_AUTH=true python -m cisco_secure_access_mcpA prominent warning is printed and logged at startup while in this mode.
The server listens at:
http://127.0.0.1:8000/mcp
To use a different host or port:
HOST=0.0.0.0 PORT=8080 python -m cisco_secure_access_mcp
Use 127.0.0.1 for local-only access. Use 0.0.0.0 only when you intentionally need LAN or remote access, preferably behind HTTPS and access controls.
Start the server first, then configure your MCP client to connect to the Streamable HTTP URL.
For Streamable HTTP, the recommended setup is to keep Cisco credentials in the server process environment or .env file and keep MCP client configuration URL-only. Clients must also present the bearer token from MCP_AUTH_TOKEN in the Authorization header.
Add to Cursor MCP settings (replace YOUR_MCP_AUTH_TOKEN with the value of MCP_AUTH_TOKEN):
{
"mcpServers": {
"cisco-secure-access": {
"url": "http://127.0.0.1:8000/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_AUTH_TOKEN"
}
}
}
}Add to .vscode/mcp.json or user MCP settings:
{
"servers": {
"cisco-secure-access": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp",
"headers": {
"Authorization": "Bearer YOUR_MCP_AUTH_TOKEN"
}
}
}
}npx @modelcontextprotocol/inspector http://127.0.0.1:8000/mcp # Set an "Authorization: Bearer <MCP_AUTH_TOKEN>" header in the Inspector UI, # or start the server with MCP_ALLOW_NO_AUTH=true for local-only testing.
Once connected, ask an MCP-compatible agent questions like:
| Query | Tools Used |
|---|---|
| "Show all destination lists" | list_destination_lists |
| "What is in destination list 12345?" | get_destinations_in_list |
| "Is evil.com present in any list?" | search_domain_across_lists |
| "Find duplicate destinations" | find_repeating_destinations |
| "Which lists are stale or empty?" | audit_stale_lists |
| "Create a block list called Malware Domains" | create_destination_list |
| "Add evil.com to list 12345" | add_destinations_to_list |
| "What category is example.com?" | get_domain_categorization |
| "Get the domain risk score for suspicious.example" | investigate_domain |
| "List access rules" | list_access_rules |
| "Show security summary for the past week" | get_security_summary |
| "Top threats in DNS traffic" | get_top_threats_by_type |
| "Show firewall activity from the last 24 hours" | get_activity_firewall |
| "How much of the destination limit is used?" | destination_usage_summary |
MCP client
|
| Streamable HTTP + Authorization: Bearer <MCP_AUTH_TOKEN>
v
Security middleware (auth, rate limit, payload limit, request IDs, access logs)
|
v
FastMCP server (DNS-rebinding / Host + Origin validation)
|
v
72 MCP tools (input validation, destructive-action confirmation, optional PII redaction)
|
v
Async Secure Access client (token cache/refresh, pooling, retries, audit logs)
|
v
Cisco Secure Access API
Authorization: Bearer <MCP_AUTH_TOKEN>; the token is compared in constant time. The server refuses to start without either MCP_AUTH_TOKEN or the explicit MCP_ALLOW_NO_AUTH=true testing flag.MCP_ALLOW_NO_AUTH=true runs without authentication for isolated local testing only; it is rejected on non-loopback hosts and prints/logs a prominent warning.transport_security, defaulting to the bound host plus loopback names (MCP_ALLOWED_HOSTS / MCP_ALLOWED_ORIGINS).MCP_MAX_REQUEST_BYTES) and per-client-IP rate limiting (MCP_RATE_LIMIT_RPM).X-Request-ID) for transport requests and outbound Cisco API calls. A redaction filter keeps credentials/tokens out of logs.delete_destination_list, remove_destinations_from_list) use two-stage commit (confirm=true) and carry destructiveHint annotations; write tools validate inputs server-side (allowlists, length/count caps). Optional PII redaction for report/activity outputs via SECURE_ACCESS_REDACT_PII.Still operator-owned (recommended for production): terminate TLS/mTLS at a reverse proxy in front of this server, prefer a secret manager over .env, and run the process sandboxed/least-privilege. Rate limiting is per-process — enforce limits at the gateway when running multiple workers.
transport="streamable-http".auth.py caches a single client-credentials token for its full lifetime (≈1 request/hour) and refreshes it ~60s before expiry, with exponential backoff retries on 429/5xx/network errors.client.py keeps one pooled httpx.AsyncClient for the whole server lifetime instead of opening a new connection per request, and closes it on shutdown.429) responses are retried for any method (honoring Retry-After); 5xx and transient network failures are retried only for idempotent reads.SecureAccessClient.paginate() walks every page (page/limit, capped at 100/page) until a short page, the reported total, an optional max_items cap, or the page-count safety cap is reached.User-Agent (see get_user_agent() in cisco_secure_access_mcp/__init__.py), overridable via SECURE_ACCESS_USER_AGENT, so Cisco can attribute backend traffic to this MCP server..env, not source code.| Package | Purpose |
|---|---|
mcp[cli] |
Model Context Protocol SDK |
httpx |
Async HTTP client |
pydantic |
Tool input models in the destination-list module |
python-dotenv |
Load .env files |
See LICENSE file for details.
Owner
Contributors
Categories
SecurityProducts
Cisco Secure AccessAI
MCP ServersDeploy Type
HTTP/StreamFeatures / Capabilities
ToolsLicense
Code Exchange Community
Get help, share code, and collaborate with other developers in the Code Exchange community.View Community