MCP Configuration
This guide covers advanced configuration of Sibyl's MCP (Model Context Protocol) server.
Server Architecture
Sibyl's MCP server is built on FastMCP and integrates with the main Starlette application:
flowchart LR
APP{{"Sibyl Combined App<br/>Starlette · port 3334"}}
API["FastAPI REST endpoints"]
MCP["MCP streamable-http endpoint"]
WS["WebSocket · real-time updates"]
BG["Background queue + session management"]
APP -->|"/api/*"| API
APP -->|"/mcp"| MCP
APP -->|"/ws"| WS
APP -->|"Lifespan"| BGTransport Modes
HTTP Mode (Default)
Sibyl runs as an HTTP server, accepting MCP requests at /mcp:
uv run sibyld serve
# Server listening on http://localhost:3334
# MCP endpoint: http://localhost:3334/mcpConfiguration:
{
"mcpServers": {
"sibyl": {
"type": "http",
"url": "http://localhost:3334/mcp"
}
}
}Stdio Mode
Run Sibyl as a subprocess communicating via stdin/stdout:
uv run sibyld serve -t stdioConfiguration:
{
"mcpServers": {
"sibyl": {
"command": "uv",
"args": ["--directory", "/path/to/sibyl/apps/api", "run", "sibyld", "serve", "-t", "stdio"],
"env": {
"SIBYL_OPENAI_API_KEY": "sk-...",
"SIBYL_JWT_SECRET": "your-secret",
"SIBYL_SURREAL_URL": "ws://127.0.0.1:8000/rpc"
}
}
}
}Authentication Modes
Auto Mode (Default)
Authentication is enabled when SIBYL_JWT_SECRET is set:
SIBYL_MCP_AUTH_MODE=auto # Default
SIBYL_JWT_SECRET=your-secret # If set, auth is enabledForced On
Always require authentication:
SIBYL_MCP_AUTH_MODE=onForced Off (Development Only)
Disable authentication:
SIBYL_MCP_AUTH_MODE=offProduction Warning Never disable authentication in production. Use auto or on mode.
OAuth Flow
When auth is enabled, Sibyl implements OAuth 2.0:
OAuth Endpoints
| Endpoint | Purpose |
|---|---|
/_oauth/login | Login form |
/_oauth/org | Organization selection |
/mcp | Token-authenticated MCP endpoint |
Token Acquisition
- User visits
/_oauth/login - Enters credentials
- Selects organization
- Receives access token
- Token used for MCP requests
API Key Authentication
For programmatic access, use API keys:
# Create API key
sibyl auth api-key create --name "MCP Client" --scopes mcp
# Use in configuration
{
"mcpServers": {
"sibyl": {
"type": "http",
"url": "http://localhost:3334/mcp",
"headers": {
"Authorization": "Bearer sk_xxx..."
}
}
}
}Environment Variables
Core Settings
| Variable | Default | Description |
|---|---|---|
SIBYL_SERVER_HOST | localhost | Host to bind to |
SIBYL_SERVER_PORT | 3334 | Port to listen on |
SIBYL_SERVER_URL | - | Public URL (for OAuth callbacks) |
SIBYL_SERVER_NAME | sibyl | Server name in MCP responses |
Authentication Settings
| Variable | Default | Description |
|---|---|---|
SIBYL_JWT_SECRET | - | JWT signing secret (required for auth) |
SIBYL_ACCESS_TOKEN_EXPIRE_MINUTES | 60 | Access token TTL in minutes |
SIBYL_MCP_AUTH_MODE | auto | Auth mode: auto, on, off |
GitHub OAuth (Optional)
| Variable | Description |
|---|---|
SIBYL_GITHUB_CLIENT_ID | GitHub OAuth app client ID |
SIBYL_GITHUB_CLIENT_SECRET | GitHub OAuth app secret |
Database Settings
| Variable | Default | Description |
|---|---|---|
SIBYL_SURREAL_URL | - | SurrealDB connection URL (ws:// / http://) |
SIBYL_REDIS_HOST | 127.0.0.1 | Optional Redis/Valkey host |
SIBYL_POSTGRES_URL | - | Migration/rehearsal-only PostgreSQL |
moon run dev provisions a local SurrealDB automatically. The memory:// URL is a test-only mode and is rejected by the production config validator.
Embedding Settings
| Variable | Default | Description |
|---|---|---|
SIBYL_OPENAI_API_KEY | - | OpenAI API key (required) |
SIBYL_EMBEDDING_MODEL | text-embedding-3-small | Embedding model |
Server Implementation
Tool Registration
The MCP server is constructed in server.py and registers 11 tools: search, context, synthesis_plan, synthesis_draft, synthesis_verify, explore, add, remember, reflect, manage, and logs. See Agents & MCP for what each one does.
mcp = FastMCP(
settings.server_name,
host=host,
port=port,
stateless_http=False, # Maintain session state
auth=auth_settings,
)
@mcp.tool()
async def search(...) -> dict:
"""Semantic search across knowledge graph."""
org_id = await _require_org_id()
return await _search(..., organization_id=org_id)Organization Context
Every tool extracts organization from the authenticated context:
async def _require_org_id() -> str:
"""Require organization ID from MCP context."""
org_id = await _get_org_id_from_context()
if not org_id:
raise ValueError("Organization context required")
return org_idResource Registration
MCP resources provide read-only data:
@mcp.resource("sibyl://health")
async def health_resource() -> str:
"""Server health status."""
health = await get_health()
return json.dumps(health)
@mcp.resource("sibyl://stats")
async def stats_resource() -> str:
"""Graph statistics."""
stats = await get_stats()
return json.dumps(stats)API Scopes
API keys can have different scopes:
| Scope | Permission |
|---|---|
mcp | Access MCP endpoint |
api:read | REST GET/HEAD/OPTIONS |
api:write | REST writes (implies read) |
Create scoped keys:
# MCP only
sibyl auth api-key create --name "MCP" --scopes mcp
# MCP + read API
sibyl auth api-key create --name "CI/CD" --scopes mcp,api:read
# Full access
sibyl auth api-key create --name "Admin" --scopes mcp,api:writeLoad Balancing
For production deployments with multiple instances:
Sticky Sessions
MCP sessions maintain state. Use sticky sessions if load balancing:
upstream sibyl {
ip_hash; # Sticky sessions
server sibyl1:3334;
server sibyl2:3334;
}Shared State
For stateless scaling:
- Use Redis/Valkey for shared coordination
- Ensure all instances share the same SurrealDB service
- Use sticky sessions for stateful MCP connections
Monitoring
Health Check
curl http://localhost:3334/api/healthMCP Status
Access via MCP resource:
sibyl://health
sibyl://statsMetrics Endpoint
curl http://localhost:3334/api/metricsTroubleshooting
Connection Refused
- Check server is running
- Verify port isn't blocked
- Check firewall settings
Authentication Failed
- Verify
SIBYL_JWT_SECRETis set - Check API key is valid
- Ensure token hasn't expired
Organization Not Found
- Check user belongs to organization
- Verify org_id in token claims
- Check organization exists
Slow Responses
- Check SurrealDB connection and query latency
- Verify embedding API connectivity
- Review query complexity
Example Configurations
Local Development
{
"mcpServers": {
"sibyl": {
"type": "http",
"url": "http://localhost:3334/mcp"
}
}
}Production with Auth
{
"mcpServers": {
"sibyl": {
"type": "http",
"url": "https://sibyl.example.com/mcp",
"headers": {
"Authorization": "Bearer sk_..."
}
}
}
}Subprocess Mode
{
"mcpServers": {
"sibyl": {
"command": "uv",
"args": ["--directory", "/opt/sibyl/apps/api", "run", "sibyld", "serve", "-t", "stdio"],
"env": {
"SIBYL_OPENAI_API_KEY": "sk-...",
"SIBYL_JWT_SECRET": "...",
"SIBYL_SURREAL_URL": "ws://surrealdb.internal:8000/rpc"
}
}
}
}Next Steps
- Agents & MCP - Connect any agent over MCP
- Skills Development - Create custom skills
- Multi-Tenancy - Organization scoping
