Model Context Protocol (MCP) server for Flight Control
The MCP server provides a read-only API layer for querying and retrieving contextual information about devices and fleets managed by Flight Control. It is designed for safe external integration, reporting, and automation, exposing a REST API that supports filtering and selector-based queries. The MCP server leverages the flightctl-python-client for backend communication and enforces authentication compatible with Flight Control's authorization model.
To build the container image locally using Podman, run:
podman build -t mcp-server:latest .
This will create a local image named mcp-server:latest
that you can use to run the server.
✅ Ready-to-use images are automatically published to quay.io!
# Pull the latest stable image
docker pull quay.io/flightctl/flightctl-mcp:latest
# Run immediately with streamable-http transport
docker run -p 8000:8000 quay.io/flightctl/flightctl-mcp:latest
- 🔄 Automatic builds: Every commit merged to
main
triggers a new build - ✅ Quality assured: Only builds after passing all tests (linting, type checking, unit tests)
- 🔒 Security scanned: All images are scanned for vulnerabilities with Trivy
- 🏗️ Multi-platform: Available for both
linux/amd64
andlinux/arm64
- 🏷️ Smart tagging: Images tagged with
latest
, branch name, and commit SHA
For maintainers: See CONTAINER-PUBLISHING.md for setup instructions and workflow details.
If you've run flightctl login
, you can mount the config directory. Note: The server now defaults to streamable-http
transport for better web-based integration.
{
"mcpServers": {
"mcp-server": {
"command": "podman",
"args": [
"run",
"-i",
"--rm",
"-p", "8000:8000",
"-v", "~/.config/flightctl:/root/.config/flightctl:ro",
"-e", "MCP_TRANSPORT",
"-e", "MCP_HOST",
"-e", "MCP_PORT",
"quay.io/flightctl/flightctl-mcp:latest"
],
"env": {
"MCP_TRANSPORT": "streamable-http",
"MCP_HOST": "0.0.0.0",
"MCP_PORT": "8000"
}
}
}
}
For environments where mounting the config file isn't possible:
{
"mcpServers": {
"mcp-server": {
"command": "podman",
"args": [
"run",
"-i",
"--rm",
"-p", "8000:8000",
"-e", "API_BASE_URL",
"-e", "OIDC_TOKEN_URL",
"-e", "OIDC_CLIENT_ID",
"-e", "REFRESH_TOKEN",
"-e", "INSECURE_SKIP_VERIFY",
"-e", "LOG_LEVEL",
"-e", "MCP_TRANSPORT",
"-e", "MCP_HOST",
"-e", "MCP_PORT",
"quay.io/flightctl/flightctl-mcp:latest"
],
"env": {
"API_BASE_URL": "https://api.flightctl.example.com",
"OIDC_TOKEN_URL": "https://auth.flightctl.example.com/realms/flightctl/protocol/openid-connect/token",
"OIDC_CLIENT_ID": "flightctl",
"REFRESH_TOKEN": "REDACTED",
"INSECURE_SKIP_VERIFY": "false",
"LOG_LEVEL": "INFO",
"MCP_TRANSPORT": "streamable-http",
"MCP_HOST": "0.0.0.0",
"MCP_PORT": "8000"
}
}
}
}
The MCP server supports three transport methods with stdio
as the default for maximum compatibility:
- Best for: Local tools, command-line scripts, integrations with clients like Claude Desktop
- Configuration: Set
MCP_TRANSPORT=stdio
(default) - Why default: Maximum compatibility with existing MCP clients
- Best for: Web-based deployments, microservices, exposing MCP over a network
- Default endpoint:
http://127.0.0.1:8000/mcp
- Configuration: Set
MCP_TRANSPORT=streamable-http
- Note: Requires MCP clients that support the new streamable-http transport
- Best for: Legacy deployments that specifically require SSE
- Configuration: Set
MCP_TRANSPORT=sse
- Default endpoint:
http://127.0.0.1:8000/sse
The MCP server uses OIDC/OAuth2 refresh tokens for authentication. To obtain the required credentials:
-
OIDC_TOKEN_URL: This is typically in the format
https://your-auth-server/realms/your-realm/protocol/openid-connect/token
- If you only have the base realm URL (e.g.,
https://auth.example.com/realms/flightctl
), the server will automatically append/protocol/openid-connect/token
- If you only have the base realm URL (e.g.,
-
REFRESH_TOKEN: Obtain this from your Flight Control authentication system
- This token should have appropriate permissions to read Flight Control resources
-
OIDC_CLIENT_ID: Usually
flightctl
(this is the default if not specified)
The MCP server supports two configuration methods:
If you've run flightctl login
, the server will automatically read configuration from ~/.config/flightctl/client.yaml
. This includes:
- API server URL
- OIDC authentication settings
- SSL certificate configuration
- Refresh tokens
The following environment variables can override or supplement the automatic configuration:
- API_BASE_URL: Base URL for the Flight Control API (e.g.,
https://api.flightctl.example.com
) - Optional (read from config file) - OIDC_TOKEN_URL: Full URL to the OIDC token endpoint (e.g.,
https://auth.flightctl.example.com/realms/flightctl/protocol/openid-connect/token
) - Optional (read from config file) - OIDC_CLIENT_ID: OIDC client identifier (defaults to
flightctl
) - Optional - REFRESH_TOKEN: OAuth2 refresh token for authentication - Optional (read from config file)
- INSECURE_SKIP_VERIFY: Skip SSL certificate verification (
true
/false
) - Optional (read from config file) - CA_CERT_PATH: Path to custom CA certificate file for SSL verification - Optional
- LOG_LEVEL: Logging level (
DEBUG
,INFO
,WARNING
,ERROR
) - Optional (defaults toINFO
)
The following environment variables control the MCP server transport and network settings:
- MCP_TRANSPORT: Transport mechanism (
stdio
,sse
,streamable-http
) - Optional (defaults tostdio
) - MCP_HOST: Host to bind to for HTTP transports - Optional (defaults to
127.0.0.1
) - MCP_PORT: Port to listen on for HTTP transports - Optional (defaults to
8000
) - MCP_PATH: Path for the MCP endpoint - Optional (defaults to
/mcp
for streamable-http) - MCP_LOG_LEVEL: Server log level (
debug
,info
,warning
,error
) - Optional (defaults toinfo
)
The server properly handles SSL certificates in the following priority:
- Custom CA Certificate: If
CA_CERT_PATH
is set, uses the specified certificate file - Skip SSL Verification: If
INSECURE_SKIP_VERIFY=true
, disables certificate verification (useful for development) - System CA Bundle: Uses the system's default certificate authority bundle (production default)
The server uses file-based logging to avoid conflicts with the MCP protocol on stdio:
- Log Location:
~/.local/share/flightctl-mcp/flightctl-mcp.log
- Log Rotation: Automatic rotation at 10MB with 5 backup files
- Log Levels: Configurable via
LOG_LEVEL
environment variable - Structured Logging: Includes timestamps, component names, and detailed error context
The server provides robust error handling:
- Specific Exceptions: Uses typed exceptions (
AuthenticationError
,APIError
,FlightControlError
) - Detailed Logging: All errors are logged with full context
# Run with default stdio transport
python main.py
# Run with streamable-http transport (for web deployments)
MCP_TRANSPORT=streamable-http python main.py
# Run with custom HTTP configuration
MCP_TRANSPORT=streamable-http MCP_HOST=0.0.0.0 MCP_PORT=8080 python main.py
When running with HTTP transport, the MCP endpoint will be available at:
- Streamable HTTP:
http://127.0.0.1:8000/mcp
(default) - SSE:
http://127.0.0.1:8000/sse
from fastmcp import Client
# Connect to streamable-http server
client = Client("http://127.0.0.1:8000/mcp")
{
"mcpServers": {
"flightctl": {
"command": "python",
"args": ["main.py"],
"env": {
"MCP_TRANSPORT": "stdio"
}
}
}
}
The MCP server exposes the following tool endpoints:
query_devices
: Query and filter devices using label and field selectorsrun_command_on_device
: Execute Linux commands on specific devices
query_fleets
: Query and filter fleet configurations
query_events
: Query system events and audit logs
query_enrollment_requests
: Query device enrollment requests
query_repositories
: Query configuration repositoriesquery_resource_syncs
: Query resource synchronization status
To test against your actual Flight Control instance:
# Ensure you have logged in first
flightctl login
# Run the live integration test
python test_live_instance.py
# Run unit tests
python -m pytest test_flightctl_mcp.py -v
# Run with coverage
python -m pytest test_flightctl_mcp.py --cov=resource_queries --cov=main --cov=cli --cov-report=html
# Test the MCP server with a simple client
python -c "
from fastmcp import Client
import asyncio
async def test():
client = Client('http://127.0.0.1:8000/mcp')
async with client:
tools = await client.list_tools()
print(f'Available tools: {[t.name for t in tools]}')
asyncio.run(test())
"
-
Server not starting: Check if the port is already in use
lsof -i :8000
-
Authentication failures: Verify your Flight Control credentials
flightctl login
-
Connection refused: Ensure the server is running and accessible
curl -v http://127.0.0.1:8000/mcp
-
Transport issues: Verify the transport configuration matches your client
# Check server logs tail -f ~/.local/share/flightctl-mcp/flightctl-mcp.log
Enable debug logging for more detailed information:
MCP_LOG_LEVEL=debug LOG_LEVEL=DEBUG python main.py
If you're migrating from the previous stdio-only version:
- Update your client configuration to use HTTP endpoints instead of stdio
- Set environment variables for host/port configuration if needed
- Update firewall rules if running on a remote server
- Test the connection using the provided client examples
The server will still support stdio transport if you set MCP_TRANSPORT=stdio
, maintaining backward compatibility.
- Read-only querying of devices, fleets, events, enrollment requests, repositories, and resource syncs from Flight Control
- Support for filtering by labels and fields using Kubernetes-style selectors
- Context-rich JSON responses including metadata and links to related resources
- Secure OIDC/OAuth2 refresh token–based authentication
- Remote device console access for executing commands on managed devices
- Automatic pagination handling for large result sets
API endpoints, filtering options, and example requests will be described in the docs/ directory or in the OpenAPI specification.
This project is open source. See LICENSE for details.
Issues and pull requests are welcome! Please see CONTRIBUTING.md for guidelines.