FortiGate MCP Server
Provides tools for managing FortiGate devices, including firewall policies, address and service objects, static routes, virtual IPs, and device connections.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@FortiGate MCP Serverlist all firewall policies"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
FortiGate MCP Server
FortiGate MCP Server - A comprehensive Model Context Protocol (MCP) server for managing FortiGate devices. This project provides programmatic access to FortiGate devices and enables integration with MCP-compatible clients such as Claude Desktop, Claude Code, and Cursor.
๐ Features
FortiGate MCP Server exposes 33 unique tools, 61 total tool-surface registrations across stdio (30) and HTTP (31) transports; 3 create-tools have transport-specific parameter shapes. Covered areas:
Device Management: Add, remove, and test connections to FortiGate devices
Firewall Management: List, create, update, and delete firewall rules
Network Management: Manage address and service objects
Routing Management: Manage static routes and interfaces
Virtual IP Management: Manage virtual IPs (VIP/DNAT)
HTTP Transport: MCP protocol over HTTP using FastMCP
Docker Support: Easy installation and deployment
MCP Client Integration: Works with Claude Desktop, Claude Code, Cursor, and other MCP-compatible clients
Related MCP server: FortiOS 7.6.x MCP Server
๐ Requirements
Python 3.11+
uvpackage manager (recommended) orpipAccess to FortiGate device
API token or username/password
๐ ๏ธ Installation
1. Clone the Project
git clone https://github.com/humyai99/mcp-fortigate.git
cd fortigate-mcp-server2. Install Dependencies
# Using uv (recommended) - installs the locked, reproducible dependency set
uv sync --locked
# Or using pip
pip install -e .3. Configuration
Create your local config from the committed example (config/config.json is gitignored and does
not exist on a fresh clone):
cp config/config.example.json config/config.jsonThen edit config/config.json:
{
"server": {
"allow_writes": false
},
"fortigate": {
"devices": {
"default": {
"host": "192.168.1.1",
"port": 443,
"username": "admin",
"password": "your_password",
"api_token": "your-api-token",
"vdom": "root",
"verify_ssl": true,
"timeout": 30
}
}
},
"logging": {
"level": "INFO",
"file": "./logs/fortigate_mcp.log"
}
}Interactive credential setup
To enter the FortiGate host and credential without putting the secret in the shell or chat history, run:
uv run python scripts/setup_credentials.pyThe API token/password is hidden while typing and is written only to the
gitignored config/config.json. TLS verification remains enabled and writes
remain disabled by default.
๐ Usage
Start HTTP Server
# Start with script
./start_http_server.sh
# Or manually
uv run python -m src.fortigate_mcp.server_http \
--host 127.0.0.1 \
--port 8814 \
--path /fortigate-mcp \
--config config/config.jsonUse --host 0.0.0.0 only if you need access from other machines on the network AND have
auth.require_auth=true configured in config/config.json; otherwise keep 127.0.0.1 โ
unauthenticated HTTP should never bind wider than loopback.
Run with Docker
# Build and start
docker-compose up -d
# View logs
docker-compose logs -f fortigate-mcp-serverThe compose file publishes port 8814 on loopback only (127.0.0.1:8814:8814), so the server is
reachable solely from the Docker host itself. To expose it beyond 127.0.0.1, first set
auth.require_auth=true in config/config.json, then widen the ports: mapping deliberately โ
see SECURITY.md.
๐ง MCP Client Integration
FortiGate MCP Server works with any MCP-compatible client. Verified, ready-to-use config examples are provided for Claude Desktop, Claude Code, and Cursor:
examples/claude_desktop_config.stdio.jsonโ Claude Desktop, stdio transport (recommended)examples/claude_desktop_config.http.jsonโ Claude Desktop, HTTP transport via themcp-remotebridgeexamples/claude_code_mcp.jsonโ Claude Code project-scope.mcp.json(stdio and HTTP entries)examples/cursor_mcp_config.jsonโ Cursor MCP configuration
Every stdio example in this project launches the server via uv run --directory <path> python -m src.fortigate_mcp.server instead of a bare python -m ... command. This matters because
GUI-launched MCP clients (like Claude Desktop) commonly start commands from a directory other than
the repo root โ the explicit --directory flag makes the invocation working-directory
independent, so it keeps working regardless of where the client process happens to start from.
Claude Code (.mcp.json)
Add a project-scope .mcp.json at your repository root (see
examples/claude_code_mcp.json for the full file, including the HTTP entry):
{
"mcpServers": {
"fortigate-mcp-stdio": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "${CLAUDE_PROJECT_DIR}", "python", "-m", "src.fortigate_mcp.server"],
"env": {
"FORTIGATE_MCP_CONFIG": "${CLAUDE_PROJECT_DIR}/config/config.json"
}
}
}
}Or register the HTTP transport via the Claude Code CLI:
claude mcp add --transport http fortigate-mcp http://127.0.0.1:8814/fortigate-mcp --header "Authorization: Bearer <token>"--header is only needed when auth.require_auth=true is set in config/config.json.
Claude Desktop
Claude Desktop's native config schema validates stdio servers only. Use
examples/claude_desktop_config.stdio.json for the stdio transport (recommended). For the HTTP
transport, examples/claude_desktop_config.http.json bridges to this server through the
community mcp-remote npm package (npx -y mcp-remote ...) โ this package is not vetted or
installed by this project; inspect it yourself before running it, since npx fetches and
executes third-party code on your behalf.
Cursor
See examples/cursor_mcp_config.json for a working stdio configuration.
๐ API Commands
Device Management
list_devices- List registered devicesget_device_status- Get device statustest_device_connection- Test connectionadd_device- Add new deviceremove_device- Remove devicediscover_vdoms- Discover VDOMs
Firewall Management
list_firewall_policies- List firewall rulescreate_firewall_policy- Create new ruleupdate_firewall_policy- Update ruledelete_firewall_policy- Delete rule
Network Management
list_address_objects- List address objectscreate_address_object- Create address objectlist_service_objects- List service objectscreate_service_object- Create service object
Virtual IP Management
list_virtual_ips- List virtual IPscreate_virtual_ip- Create virtual IPupdate_virtual_ip- Update virtual IPget_virtual_ip_detail- Get virtual IP detaildelete_virtual_ip- Delete virtual IP
Routing Management
list_static_routes- List static routescreate_static_route- Create static routeupdate_static_route- Update static routedelete_static_route- Delete static routeget_static_route_detail- Get static route detailget_routing_table- Get routing tablelist_interfaces- List interfacesget_interface_status- Get interface status
System Commands
health- Health checktest_connection- Connection testget_schema_info- Schema information
๐งช Testing
Run Tests
# One-time setup: test dependencies (pytest, pytest-cov, respx, pyyaml) live in the
# dev/test extras โ the plain `uv sync --locked` from the install step does not install them
uv sync --locked --all-extras
# Quick run with coverage disabled (a bare -q only reduces verbosity;
# without --no-cov the coverage gate from pyproject.toml addopts still runs)
uv run pytest -q --no-cov
# Full suite with coverage (--cov-fail-under=67 enforced per pyproject.toml)
uv run pytest
# Run specific test files
uv run pytest tests/test_device_manager.py
uv run pytest tests/test_fortigate_api.py
uv run pytest tests/test_tools.py
# Verbose output
uv run pytest -v
# Detailed error information
uv run pytest --tb=longTest Categories
Unit Tests: Test individual components and functions
Coverage: Code coverage reporting with HTML output
Manual Testing
The /health route lives at the app root, is exempt from Bearer-token auth, and works with
plain curl:
# Liveness probe
curl http://127.0.0.1:8814/healthThe MCP protocol itself cannot be exercised with a bare curl POST: the streamable-HTTP
transport requires an initialize handshake, session management, and tools/call framing.
Use a real MCP client for protocol-level testing โ for example fastmcp.Client:
uv run python - <<'PY'
import asyncio
from fastmcp import Client
async def main():
# Trailing slash matches the mount convention; the non-slash form 307-redirects
async with Client("http://127.0.0.1:8814/fortigate-mcp/") as client:
tools = await client.list_tools()
print(f"{len(tools)} tools registered")
result = await client.call_tool("health", {})
print(result.content[0].text)
asyncio.run(main())
PYAlternatively, bridge with npx -y mcp-remote http://127.0.0.1:8814/fortigate-mcp or use the
MCP Inspector.
๐ Project Structure
fortigate-mcp-server/
โโโ src/
โ โโโ fortigate_mcp/
โ โโโ __init__.py
โ โโโ server.py # STDIO MCP server
โ โโโ server_http.py # HTTP MCP server
โ โโโ config/ # Configuration management
โ โโโ core/ # Core components
โ โโโ tools/ # MCP tools
โ โโโ formatting/ # Response formatting
โโโ config/
โ โโโ config.json # Main configuration (gitignored, created from example)
โ โโโ config.example.json # Example configuration
โโโ examples/
โ โโโ claude_desktop_config.stdio.json # Claude Desktop, stdio transport
โ โโโ claude_desktop_config.http.json # Claude Desktop, HTTP via mcp-remote
โ โโโ claude_code_mcp.json # Claude Code .mcp.json
โ โโโ cursor_mcp_config.json # Cursor MCP config
โโโ logs/ # Log files
โโโ tests/ # Test files
โโโ docker-compose.yml # Docker compose
โโโ Dockerfile # Docker image
โโโ start_server.sh # STDIO startup script
โโโ start_http_server.sh # HTTP startup script
โโโ README.md # This file๐ Troubleshooting
Common Issues
Connection Error
Ensure FortiGate device is accessible
Verify API token or username/password
If you see SSL certificate errors, install the FortiGate device's certificate as trusted (or replace it with a CA-signed certificate) โ do not disable certificate verification.
verify_ssldefaults totrueand must staytrue; see SECURITY.md for the rationale.
Port Conflict
Ensure port 8814 is available
Change port using
--portparameter
Configuration Error
Ensure
config.jsonis properly formattedCheck JSON syntax
MCP Client Connection Issue
Ensure the server is running
Verify the config file path and URL are correct
Restart the MCP client (Claude Desktop / Claude Code / Cursor)
Logs
Check logs using:
# HTTP server logs
tail -f logs/fortigate_mcp.log
# Docker logs
docker-compose logs -f fortigate-mcp-server๐ Security
Implemented Controls
Write protection (default: read-only)
Write and destructive tools are rejected unless explicitly enabled via
server.allow_writes: truein config, or theFORTIGATE_MCP_ALLOW_WRITES=1environment variable
TLS certificate verification (default: on)
Config-loaded devices verify TLS certificates by default (
verify_ssl: true)
Bearer-token authentication (optional, default: off)
Available via
auth.require_auth/auth.api_tokens; unauthenticated by default โ run only on trusted networks when disabled
Secret redaction
API tokens and passwords are stored as
SecretStrand are never written to logs
Known Limitations
Rate limiting is parsed from config but not enforced โ do not rely on it as a working control.
Unauthenticated HTTP (the default) should bind to
127.0.0.1(loopback) only; binding to0.0.0.0for wider network access requires enabling Bearer auth (auth.require_auth=true) plus network-level controls (firewall rules, VPN, reverse-proxy allowlists).
See SECURITY.md for the full threat model and known limitations.
๐ค Contributing
Fork the repository
Create a feature branch (
git checkout -b feature/amazing-feature)Commit your changes (
git commit -m 'Add amazing feature')Push to the branch (
git push origin feature/amazing-feature)Open a Pull Request
๐ License
This project is licensed under the MIT License. See the LICENSE file for details.
๐ Acknowledgments
FastMCP - For MCP HTTP transport
FortiGate API - For FortiGate integration
๐ Support
For issues:
Use the Issues page
Check the documentation
Review the logs
Note: This project has been tested with FortiGate devices. Please perform comprehensive testing before using in production.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/humyai99/mcp-fortigate'
If you have feedback or need assistance with the MCP directory API, please join our Discord server