MCP API
github-webhook-server can optionally expose a Model Context Protocol (MCP) endpoint at /mcp. It runs on the same FastAPI server as the normal webhook API, but it is a separate interface: GitHub still sends events to /webhook_server, while MCP clients connect to /mcp.
When enabled, the server builds the MCP layer from the existing FastAPI app instead of maintaining a separate MCP-only API. That keeps the MCP surface aligned with the routes you already run.
Note:
/mcpis hidden from the autogenerated FastAPI schema. The route is registered withinclude_in_schema=False, so you will not see it in Swagger or other OpenAPI-based docs.
Enable and connect
MCP is disabled unless the process starts with ENABLE_MCP_SERVER=true. The same pattern is used for the log server feature, which matters because most of the useful agent-facing operations are log and workflow analysis routes.
LOG_SERVER_ENABLED: bool = os.environ.get("ENABLE_LOG_SERVER") == "true"
MCP_SERVER_ENABLED: bool = os.environ.get("ENABLE_MCP_SERVER") == "true"
Tip: Use the exact lowercase string
true. Values such asTrue,1, oryesdo not enable these features in this codebase.
The example Compose file ships with MCP off by default:
environment:
- ENABLE_LOG_SERVER=true
- ENABLE_MCP_SERVER=false
To enable MCP:
- Set
ENABLE_MCP_SERVER=true. - Set
ENABLE_LOG_SERVER=trueif you want agents to query logs, PR flow data, or workflow step timelines. - Restart the server.
- Point your MCP client at
http://<host>:5000/mcp, or the HTTPS equivalent for your deployment.
With the default container setup, port 5000 is the expected default.
Note: Enabling MCP does not change your GitHub webhook URL. GitHub should continue posting to
/webhook_server.
How /mcp is implemented
The MCP transport is mounted as a single streamable HTTP endpoint at /mcp. It is created from the main FASTAPI_APP, and routes tagged mcp_exclude are filtered out.
# MCP Integration - Only register if ENABLE_MCP_SERVER=true
if MCP_SERVER_ENABLED:
# Create MCP instance with the main app
# NOTE: No authentication configured - MCP server runs without auth
# ⚠️ SECURITY WARNING: Deploy only on trusted networks (VPN, internal)
# Never expose to public internet - use reverse proxy with auth for external access
mcp = FastApiMCP(FASTAPI_APP, exclude_tags=["mcp_exclude"])
# Create stateless HTTP transport to avoid session management issues
# Override with stateless session manager
http_transport = FastApiHttpSessionManager(
mcp_server=mcp.server,
event_store=None, # No event store needed for stateless mode
json_response=True,
)
# Manually patch to use stateless mode
http_transport._session_manager = None # Force recreation with stateless=True
# Register the HTTP endpoint manually
@FASTAPI_APP.api_route("/mcp", methods=["GET", "POST", "DELETE"], include_in_schema=False, operation_id="mcp_http")
async def handle_mcp_streamable_http(request: Request) -> Response:
# Session manager is initialized in lifespan
if http_transport is None or http_transport._session_manager is None:
LOGGER.error("MCP session manager not initialized")
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="MCP server not initialized")
return await http_transport.handle_fastapi_request(request)
During startup, the app initializes the underlying MCP session manager in stateless mode:
http_transport._session_manager = StreamableHTTPSessionManager(
app=mcp.server,
event_store=http_transport.event_store,
json_response=True,
stateless=True, # Enable stateless mode - no session management required
)
In practice, this means:
/mcpis either mounted or not mounted at all.- It uses one HTTP endpoint instead of a separate URL per capability.
- It is stateless on the server side.
- Proxies and gateways in front of it must allow
GET,POST, andDELETEto/mcp.
What agents can access
Because the MCP layer is built from the FastAPI app, the operation IDs in webhook_server/app.py are the best guide to what an agent can use.
These routes are part of the app surface:
@FASTAPI_APP.get(f"{APP_URL_ROOT_PATH}/healthcheck", operation_id="healthcheck")
def healthcheck() -> dict[str, Any]:
return {"status": requests.codes.ok, "message": "Alive"}
@FASTAPI_APP.get(
"/logs/api/entries",
operation_id="get_log_entries",
dependencies=[Depends(require_log_server_enabled)],
)
@FASTAPI_APP.get(
"/logs/api/export",
operation_id="export_logs",
dependencies=[Depends(require_log_server_enabled)],
)
@FASTAPI_APP.get(
"/logs/api/pr-flow/{hook_id}",
operation_id="get_pr_flow_data",
dependencies=[Depends(require_log_server_enabled)],
)
@FASTAPI_APP.get(
"/logs/api/workflow-steps/{hook_id}",
operation_id="get_workflow_steps",
dependencies=[Depends(require_log_server_enabled)],
)
@FASTAPI_APP.get(
"/logs/api/step-logs/{hook_id}/{step_name}",
operation_id="get_step_logs",
dependencies=[Depends(require_log_server_enabled), Depends(require_trusted_network)],
)
The main capabilities this gives to AI agents are:
- Health checks through
healthcheck - Filtered webhook log search through
get_log_entries - Log exports through
export_logs - PR workflow analysis through
get_pr_flow_data - Step-by-step execution timelines through
get_workflow_steps - Time-correlated step log inspection through
get_step_logs
If ENABLE_LOG_SERVER is still off, the log-related operations are not usable and return the same "log server is disabled" behavior as the regular HTTP API.
The webhook receiver itself is intentionally excluded from the MCP surface:
@FASTAPI_APP.post(
APP_URL_ROOT_PATH,
operation_id="process_webhook",
dependencies=[Depends(gate_by_allowlist_ips_dependency)],
tags=["mcp_exclude"],
)
async def process_webhook(request: Request) -> JSONResponse:
That is an important design choice. Agents can inspect server state and workflow data, but they do not get an MCP path to submit synthetic GitHub webhooks through the main ingestion endpoint.
Tip: If you do not want AI agents to inspect logs or workflow history, leave
ENABLE_LOG_SERVER=falseeven if you enable MCP.
Logging and configuration
MCP traffic uses its own log file instead of sharing the main webhook server log. The example config includes the relevant settings:
log-file: webhook-server.log
mcp-log-file: mcp_server.log
logs-server-log-file: logs_server.log
mask-sensitive-data: true
A few practical details matter here:
mcp-log-filedefaults tomcp_server.log.- Relative log file names are resolved under
${WEBHOOK_SERVER_DATA_DIR}/logs/. - If you do not set
WEBHOOK_SERVER_DATA_DIR, the default data directory is/home/podman/data, so the default MCP log path becomes/home/podman/data/logs/mcp_server.log. - MCP logging is configured during application startup, so plan to restart the server after changing
ENABLE_MCP_SERVERormcp-log-file. - The MCP logger reuses the same masking setting as the rest of the app, so
mask-sensitive-data: truealso applies to MCP logs.
Security and deployment expectations
Warning: The MCP endpoint has no built-in authentication. Treat
/mcpas an internal or administrative interface.
The code is explicit about this:
# NOTE: No authentication configured - MCP server runs without auth
# ⚠️ SECURITY WARNING: Deploy only on trusted networks (VPN, internal)
# Never expose to public internet - use reverse proxy with auth for external access
mcp = FastApiMCP(FASTAPI_APP, exclude_tags=["mcp_exclude"])
Use these expectations when deploying it:
- Keep
/mcpon a trusted private network, VPN, or localhost-only deployment whenever possible. - If it must be reachable through a shared edge, put it behind a reverse proxy or load balancer that enforces authentication and TLS.
- Do not assume the GitHub IP allowlist protects
/mcp. That allowlist is attached to the webhook receiver, not to the MCP transport. - Be extra careful if
ENABLE_LOG_SERVER=true. In that mode, agents can query operational data such as hook IDs, repository names, GitHub usernames, workflow timings, error details, and token-spend metadata. - Treat
mask-sensitive-data: trueas a helpful safeguard, not as a security boundary. - If you run behind a reverse proxy, verify client IP handling carefully before relying on network-based restrictions.
- Keep the whole MCP endpoint off the public internet unless you have strong compensating controls in front of it.
Warning:
get_step_logsis the most restricted log route in the HTTP app because it requires bothrequire_log_server_enabledandrequire_trusted_network. That extra check is useful, but it is still not a replacement for proper network isolation and authentication in front of/mcp.