forticnapp-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
๐ก๏ธ forticnapp-mcp โ FortiCNAPP MCP Server
Expose FortiCNAPP (formerly Lacework) API 2.0 as typed, auth-aware tools for any MCP client
Overview โข Install โข Configure โข stdio โข HTTP โข Architecture โข Security โข Tools โข Quick Reference
๐ Overview
forticnapp-mcp is an MCP (Model Context Protocol) server for FortiCNAPP API 2.0. Tools are not hand-written โ they are generated at startup from the Lacework swagger spec (lw.yaml), so the tool surface tracks whichever spec you point it at.
The same tool registry ships behind two interchangeable transports. Pick the one that matches how you run the server.
# | Transport | Entry point | Process Model | Auth | Best For |
1 | stdio โ JSON-RPC over stdin/stdout |
| Spawned per client, dies with it | FortiCNAPP credentials from its own env | ๐ป Claude Desktop, Claude Code (single client) |
2 | Streamable HTTP โ |
| Long-running (bare metal or Docker) | FortiCNAPP credentials + bearer token gating the endpoint | โ๏ธ Shared server, multiple remote clients |
Choosestdio to run locally on your own machine. Choose Streamable HTTP to share the server across users, deploy to the cloud, or require centralized authentication.
Tenancy: each instance serves exactly one FortiCNAPP account in both modes. In HTTP mode, every caller holding the bearer token sees the same account โ there is no per-client credential isolation.
Both modes share every other module (config.py, openapi_loader.py, auth.py, http_client.py, tool_registry.py). http_server.py only adds a Starlette app, a bearer-token gate and a /healthz endpoint in front of the same mcp.server.lowlevel.Server that main.py runs over stdio.
๐ค Why This Exists
FortiCNAPP's API 2.0 spec makes naive OpenAPI-to-tool generation unreliable:
Spec quirk | How this server handles it |
No OpenAPI | Hardcodes the real FortiCNAPP keyId + secret โ bearer token handshake |
No | Derives deterministic tool names from method + path |
Read-only "search" endpoints mixed in with mutations on same verbs | Classifies "is this mutating" from the path shape, not the HTTP verb |
See CLAUDE.md for the full list of spec quirks the design accounts for.
Related MCP server: OpenAI-Compatible MCP Gateway
๐ฆ Installation
Requires Python 3.11+
pip install -e ".[dev]"โ๏ธ Configuration
Credentials come from the FortiCNAPP/Lacework console (Settings โ API Keys), which issues a keyId and a secret โ both are required for the default auth mode.
๐ Option A โ Interactive setup (recommended)
Prompts for credentials, validates them against the real FortiCNAPP token endpoint, and writes .env, a portable .mcp.json and .gitignore.
forticnapp-mcp-setupโ๏ธ Option B โ Manual
cp .env.example .env
# then fill in FORTICNAPP_API_BASE_URL, FORTICNAPP_KEY_ID, FORTICNAPP_API_SECRET๐ Key environment variables
Variable | Purpose |
| Tenant URL, e.g. |
| API key ID |
| API secret |
| Path to the spec (default |
| Which OpenAPI tags become tools |
| Opt in to mutating tools โ off by default, only read-only tools are exposed |
| Bearer token protecting the HTTP endpoint (HTTP mode only) |
| HTTP bind address (default |
See .env.example for every supported variable.
1๏ธโฃ stdio Mode (local)
The client (e.g. Claude Desktop) launches the server as a subprocess and talks JSON-RPC over its stdin/stdout.
forticnapp-mcp
# equivalently:
python -m forticnapp_mcp.mainThe server validates configuration and loads the spec before it starts listening. On failure it exits with a clear one-line error on stderr โ it will not start with a broken configuration. This is the default mode.
๐ฅ๏ธ Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"forticnapp": {
"command": "python",
"args": ["-m", "forticnapp_mcp.main"],
"cwd": "/absolute/path/to/mcp_forticnapp",
"env": {
"FORTICNAPP_API_BASE_URL": "https://YourAccount.lacework.net",
"FORTICNAPP_KEY_ID": "YOUR_KEY_ID",
"FORTICNAPP_API_SECRET": "YOUR_SECRET",
"FORTICNAPP_OPENAPI_SPEC": "/absolute/path/to/mcp_forticnapp/lw.yaml"
}
}
}
}cwd must be the project root so the default FORTICNAPP_OPENAPI_SPEC=./lw.yaml resolves โ or set an absolute path as shown above.
Prefer a real.env file over inlining secrets in this config wherever your setup allows it.
2๏ธโฃ HTTP Mode (shared/remote)
Use the forticnapp-mcp-http entry point for one server shared by multiple MCP clients. Every request to /mcp requires a bearer token (FORTICNAPP_MCP_HTTP_TOKEN) โ a separate secret from your FortiCNAPP credentials, protecting the HTTP endpoint itself.
Bare metal
cp .env.example .env
# fill in FORTICNAPP_API_BASE_URL / KEY_ID / API_SECRET as usual, plus:
# FORTICNAPP_MCP_HTTP_TOKEN=<a long random secret>
forticnapp-mcp-http
# equivalently:
python -m forticnapp_mcp.http_serverDocker
docker compose up --build๐ Endpoints
Endpoint | Method | Auth | Purpose |
|
|
| MCP Streamable HTTP (a bare |
|
| None | Container |
The server listens on 0.0.0.0:8000 by default.
Generic Streamable HTTP client config (shape varies by client)
{
"mcpServers": {
"forticnapp": {
"type": "http",
"url": "http://<host>:8000/mcp/",
"headers": { "Authorization": "Bearer <FORTICNAPP_MCP_HTTP_TOKEN>" }
}
}
}๐๏ธ Architecture
flowchart LR
subgraph Clients["MCP Clients"]
A["Claude Desktop<br/>Claude Code"]
B["Remote MCP clients"]
end
subgraph Server["forticnapp-mcp"]
M["main.py<br/>stdio"]
H["http_server.py<br/>Streamable HTTP + bearer gate"]
R["tool_registry.py<br/>OperationSpec โ MCP Tools"]
L["openapi_loader.py<br/>lw.yaml โ OperationSpec"]
C["http_client.py<br/>retries ยท 401 refresh ยท pagination"]
U["auth.py<br/>keyId + secret โ bearer token"]
end
API[("FortiCNAPP API 2.0")]
A -->|stdio| M
B -->|HTTPS + token| H
M --> R
H --> R
L --> R
R --> C
C --> U
C --> API
style R fill:#DA291C,color:#fff,stroke:#DA291C
style API fill:#24292f,color:#fff๐งฉ Modules
Module | Responsibility |
| Env vars โ validated |
|
|
|
|
|
|
|
|
| Wires the above into |
| Starlette app, bearer-token gate and |
|
|
|
|
| Structured JSON logs to stderr with header/secret redaction |
| Tool-name derivation, mutation/pagination classification, JSON Schema โ Python type mapping |
๐จ Tool response envelope
Every tool call returns the same structured JSON envelope:
{
"success": true,
"status_code": 200,
"operation_id": "forticnapp_alerts_list",
"request": {"method": "GET", "path": "/api/v2/Alerts", "query_keys": ["startTime"], "has_body": false},
"data": { "...": "..." },
"pagination": {"rows": 50, "total_rows": 400, "next_page_url": "https://...", "has_more": true},
"error": null
}Pagination: call the same tool again with page_url set to pagination.next_page_url from the previous response. Every other argument is ignored when page_url is set.
๐ง Customizing the token exchange
If your deployment's token endpoint differs from the documented contract (self-hosted, FedRAMP, a future API revision), there is exactly one place to change: ApiKeyToTokenStrategy._acquire_token in auth.py.
It builds the token request and parses the response using field names from a TokenOperationHint, inferred from the spec at startup (openapi_loader.discover_token_operation) with fallback defaults matching FortiCNAPP's current contract:
Direction | Fields |
Request |
|
Response |
|
Token caching, proactive refresh and 401-triggered re-acquisition are wire-format-agnostic and live in the surrounding ApiKeyToTokenStrategy methods โ you shouldn't need to touch them.
๐ก๏ธ Security
Control | Behavior |
๐ง In-memory secrets | Tokens and secrets are kept in memory only; nothing is persisted to disk |
๐ Log redaction |
|
๐ Read-only by default | Mutating operations (anything that isn't a |
๐ซ Endpoint gate (HTTP) | Every |
๐ฅ Shared account (HTTP) | No per-client credential isolation โ all callers share the same FortiCNAPP account |
Never commit API keys, secrets or the HTTP bearer token to Git. Keep .env out of source control:
.env
.mcp.json๐งฐ MCP Tools
37 read-only tools across 9 categories.
Prefix:
mcp__remote-devices__forticnapp__โ each name below is prefixed with this. The server also appeared earlier undermcp__claude-device__lcl-forticnapp-.
Category | Tools |
๐จ Alerts | 5 |
โ๏ธ Cloud accounts | 4 |
๐ง Entities | 14 |
๐ Events | 1 |
๐ Inventory | 2 |
๐ Policies | 4 |
๐ Reports | 1 |
๐ Vulnerabilities | 3 |
๐ Vulnerability exceptions | 3 |
Total | 37 |
forticnapp_alerts_listforticnapp_alerts_getforticnapp_alerts_searchforticnapp_alerts_entities_getforticnapp_alerts_entity_details_get
forticnapp_cloud_accounts_listforticnapp_cloud_accounts_searchforticnapp_cloud_accounts_by_type_getforticnapp_cloud_accounts_by_intg_guid_get
forticnapp_entities_machines_searchforticnapp_entities_machine_details_searchforticnapp_entities_containers_searchforticnapp_entities_images_searchforticnapp_entities_k8s_pods_searchforticnapp_entities_applications_searchforticnapp_entities_packages_searchforticnapp_entities_processes_searchforticnapp_entities_command_lines_searchforticnapp_entities_files_searchforticnapp_entities_new_file_hashes_searchforticnapp_entities_users_searchforticnapp_entities_network_interfaces_searchforticnapp_entities_internal_ip_addresses_search
forticnapp_events_searchforticnapp_inventory_searchforticnapp_inventory_scan_listforticnapp_reports_list
forticnapp_policies_listforticnapp_policies_getforticnapp_policies_searchforticnapp_policies_tags_list
forticnapp_vulnerabilities_hosts_searchforticnapp_vulnerabilities_containers_searchforticnapp_vulnerabilities_containers_scan_getforticnapp_vulnerability_exceptions_listforticnapp_vulnerability_exceptions_getforticnapp_vulnerability_exceptions_search
๐งช Development
ruff check src/
pytestโก Quick Reference
stdio (local)
pip install -e ".[dev]"
forticnapp-mcp-setup # interactive credentials + .env + .mcp.json
forticnapp-mcp # launched by the MCP client as a subprocessHTTP (shared)
cp .env.example .env # set FORTICNAPP_* and FORTICNAPP_MCP_HTTP_TOKEN
docker compose up --build
curl http://<host>:8000/healthzClient call
POST http://<host>:8000/mcp/
Authorization: Bearer <FORTICNAPP_MCP_HTTP_TOKEN>๐ Keep secrets out of source control ยท Mutations are opt-in ยท Rotate API keys regularly
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelogโฆ
This server cannot be deployed
Maintenance
Related MCP Connectors
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA dual-transport MCP server that exposes your API as tools to LLM clients, supporting both stdio transport for local clients like Claude Desktop and HTTP/SSE transport for remote clients like OpenAI's Responses API.-
- FlicenseNot gradedqualityDmaintenanceLocal MCP server that exposes fixed tools for GPT, Claude, and Gemini while routing to any OpenAI-compatible chat completions backend with independent configuration per target.1-
- FlicenseCqualityDmaintenanceSchema-driven MCP server that exposes all Paperclip API operations as typed MCP tools over stdio or Streamable HTTP, supporting both authenticated and local_mode deployments.100-
- AlicenseNot gradedqualityCmaintenanceEnables AI tools to uniformly discover, inspect, and call tools, prompts, and resources from multiple upstream MCP servers through a small set of fixed MCP tools, over stdio or HTTP.2MIT