MCP Servers¶
AKKO ships two Model Context Protocol (MCP) servers that let LLM agents (Claude Desktop, Cursor, VS Code, Continue, LibreChat) talk to the platform natively — while every byte and every model stays inside your cluster.
Service-reference page (image, env vars, health): Services / MCP Servers. This page focuses on capabilities, security model and integration.
Why MCP on AKKO¶
Model Context Protocol is Anthropic's open standard for connecting LLMs to tools and resources through a JSON-RPC-like interface. AKKO is one of the only open-source sovereign platforms that exposes its SQL engine and its data catalog through MCP, with Keycloak-enforced authentication and OPA-enforced authorization — no SaaS MCP gateway needed.
- Sovereign — both servers run inside the
akkonamespace; traffic never leaves the cluster unless your agent lives outside (Ingress-exposed with TLS). - Sovereign models too — tools like
akko_ai_sentimentcall the local Trino AI plugin, which routes to Ollama. No OpenAI. - Governed — every tool call is authenticated (Keycloak JWT or service token) and authorized (Trino/OpenMetadata RBAC + OPA).
- Observable — every call emits a span in Tempo and a structured log line in Loki.
Architecture¶
flowchart LR
subgraph Agents[AI agents]
CLAUDE[Claude Desktop]
CURSOR[Cursor / VS Code]
LIB[LibreChat / Continue]
end
subgraph Edge[Edge]
TR[Traefik<br/>TLS + OIDC]
end
subgraph Servers[AKKO MCP servers]
MCT[MCP Trino<br/>8 tools]
MCO[MCP OpenMetadata<br/>catalog + lineage]
end
subgraph Plane[Data plane]
TRI[Trino 480<br/>+ ai_* plugin]
OM[OpenMetadata]
AIS[AI Service<br/>FastAPI]
OLL[Ollama]
end
CLAUDE & CURSOR & LIB --> TR
TR --> MCT
TR --> MCO
MCT --> TRI
MCT --> AIS
AIS --> OLL
MCO --> OM
Both servers are deployed as standard Kubernetes Deployments with 2 replicas, exposed through Traefik at:
https://mcp-trino.<domain>/ssehttps://mcp-openmetadata.<domain>/sse
TLS is terminated by the shared akko-tls secret. Only GET and POST are allowed at the Ingress level.
MCP Trino — 8 Tools¶
Enables agents to discover data, run SELECTs safely, and invoke sovereign AI functions.
| Tool | Signature | Purpose |
|---|---|---|
list_catalogs() |
— | List Trino catalogs (iceberg, postgresql, tpch, …) |
list_schemas(catalog) |
(catalog: str) |
List schemas in a catalog |
list_tables(catalog, schema) |
(catalog, schema) |
List tables in a schema |
describe_table(catalog, schema, table) |
(catalog, schema, table) |
Return column types, nullability, comments |
execute_query(sql) |
(sql: str) |
Read-only SELECT, max 100 rows, 30 s timeout |
akko_ai_sentiment(text) |
(text: str) |
Local sentiment via Trino AI plugin → Ollama |
akko_ai_summarize(text) |
(text: str) |
Local summarization via Trino AI plugin |
akko_ai_ask(question, context?) |
(question: str, context?: str) |
Free-form question with optional context |
Security model¶
- Read-only by default — the server parses the SQL with
sqlglotand rejects anything that is not a singleSELECT / WITH / UNION / EXCEPT / INTERSECT. - Keyword denylist —
INSERT,UPDATE,DELETE,DROP,CREATE,ALTER,GRANT,REVOKEare rejected a second time. - Identity forwarding — the caller's Keycloak JWT (or a delegated service token) is forwarded as
X-Trino-User; OPA policies apply end-to-end. - Hard limits —
TRINO_MAX_ROWS=100,TRINO_TIMEOUT_S=30. Adjustable in values, but any agent config > 1000 rows requiresakko-admin. - AI sub-tools —
akko_ai_sentiment/akko_ai_summarize/akko_ai_askcallSELECT akko_ai_sentiment(?)via Trino; the Trino AI plugin enforces its own rate limits per user.
MCP OpenMetadata — 5 Tools¶
Enables agents to find datasets, explore lineage, and reference the business glossary.
| Tool | Signature | Purpose |
|---|---|---|
search_datasets(query) |
(query: str) |
Full-text search across the OpenMetadata catalog |
get_table_metadata(fqn) |
(fqn: str) |
Return schema, tags, owners, description for an FQN |
get_lineage(fqn, hops=3) |
(fqn: str, hops: int) |
Upstream / downstream lineage (max hops) |
list_glossary_terms() |
— | Enumerate business glossary entries |
list_dashboards() |
— | Superset dashboards registered in OpenMetadata |
Security model¶
- Read-only — the server uses a scoped JWT that only has the
catalog:viewandlineage:viewpermissions in OpenMetadata. - Service JWT stored in the Kubernetes Secret
akko-mcp-omand injected at startup. Rotate viakubectl delete secret akko-mcp-om && helm upgrade. - No impersonation — MCP OpenMetadata does not forward the caller identity; OpenMetadata lineage is considered public metadata within the platform. PII column tags remain visible; actual PII values are served by Trino only.
Configuration¶
Helm values (helm/akko/values.yaml)¶
akko-mcp:
trino:
enabled: true
image: akko-mcp-trino:2026.03
ingress:
host: mcp-federation.akko.local
env:
TRINO_HOST: akko-trino
TRINO_PORT: "8080"
TRINO_USER: mcp-trino
TRINO_READ_ONLY: "true"
TRINO_MAX_ROWS: "100"
TRINO_TIMEOUT_S: "30"
openmetadata:
enabled: true
image: akko-mcp-openmetadata:2026.03
ingress:
host: mcp-catalog.akko.local
env:
OPENMETADATA_HOST: openmetadata
OPENMETADATA_PORT: "8585"
envFromSecret: akko-mcp-om
Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json¶
{
"mcpServers": {
"akko-trino": {
"url": "https://mcp-federation.akko.local/sse"
},
"akko-openmetadata": {
"url": "https://mcp-catalog.akko.local/sse"
}
}
}
Cursor / Continue / VS Code¶
All three use the same url field; see their respective docs for the exact config file.
Local development without Ingress¶
kubectl port-forward -n akko svc/akko-akko-mcp-trino 3000:3000
kubectl port-forward -n akko svc/akko-akko-mcp-openmetadata 3001:3000
# Then point the agent at http://localhost:3000/sse and :3001/sse
RBAC¶
| Role | MCP Trino execute_query |
MCP Trino ai_* |
MCP OpenMetadata all tools |
|---|---|---|---|
akko-admin |
yes (rows capped) | yes | yes |
akko-engineer |
yes | yes | yes |
akko-analyst |
yes (OPA masks PII) | yes | yes |
akko-user |
yes (OPA masks PII) | yes (subset) | yes |
akko-viewer |
yes (row filters apply) | no | yes |
Token forwarding only works if the agent is behind Traefik with OIDC. When using a raw kubectl port-forward, the MCP server falls back to its service identity (mcp-trino in group.txt), which is mapped to a restricted analyst profile.
Observability¶
- Tempo — every tool call is a span: service name
akko-mcp-trinoorakko-mcp-openmetadata, attributes includetool.name,trino.sql.hash,user.id. - Loki — structured JSON lines with
app=akko-mcp-trino, fieldstool,user,latency_ms,status. - Prometheus — counters and histograms:
akko_mcp_tool_calls_total{server, tool, status}
akko_mcp_tool_duration_seconds{server, tool}
akko_mcp_trino_rows_returned{user}
A Grafana dashboard AKKO / MCP Servers ships with p95 latency, top tools, error rate and per-user activity.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Claude Desktop shows "tool not available" | Agent cached an old schema | Restart Claude Desktop fully |
| 403 on every call | OIDC cookie expired | Re-login to Traefik via the redirect; or rotate the service token for headless agents |
execute_query returns 0 rows on a table that exists |
OPA row filter applies for the role | Run the same query as akko-admin in Trino CLI; check OPA logs |
| Lineage empty for a known FQN | OpenMetadata not yet ingested | Trigger openmetadata_ingest_dag in Airflow |
| SSE connection drops every 30 s | Traefik timeout too low | Set spec.defaultTimeout=3600s on the IngressRoute |
Related¶
- Services / MCP Servers — image, helm values, tests
- AI / ADEN — similar spirit, browser-based
- AI / Trino functions — the
ai_*tools called by MCP Trino - Architecture / AI Stack
- Admin / RBAC