Install
$ agentstack add mcp-ralforion-orionbelt-semantic-layer-mcp ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
OrionBelt Semantic Layer MCP
Thin MCP server that delegates to the OrionBelt Semantic Layer REST API
[](https://github.com/ralforion/orionbelt-semantic-layer-mcp/releases) [](https://github.com/ralforion/orionbelt-semantic-layer) [](https://www.python.org/downloads/) [](https://github.com/ralforion/orionbelt-semantic-layer-mcp/blob/main/LICENSE) [](https://gofastmcp.com) [](https://docs.pydantic.dev) [](https://docs.astral.sh/ruff/)
[](https://cloud.google.com/bigquery) [](https://www.postgresql.org) [](https://www.snowflake.com) [](https://clickhouse.com) [](https://www.dremio.com) [](https://www.databricks.com) [](https://duckdb.org) [](https://www.mysql.com)
[](https://hub.docker.com/r/ralforion/orionbelt-semantic-layer-mcp/tags) [](https://hub.docker.com/r/ralforion/orionbelt-semantic-layer-mcp) [](https://hub.docker.com/r/ralforion/orionbelt-semantic-layer-mcp/tags)
A thin MCP server that delegates all business logic to the OrionBelt Semantic Layer REST API via HTTP. No embedded engine — pure API pass-through.
Architecture
The OrionBelt Semantic Layer platform has two deployment modes. This MCP server supports both:
- Standalone — Deploy the OrionBelt Semantic Layer API anywhere (Cloud Run, Docker, localhost) and point this MCP server at it via
API_BASE_URL. - Hosted — Connect to the public Cloud Run deployment with zero local setup (see [Hosted MCP Server](#hosted-mcp-server) below).
┌────────────┐ ┌──────────────────────────────────────────────────────┐
│ LLM Client │ │ OrionBelt Platform │
│ │ │ │
│ Claude, │──MCP──│──> server.py ──HTTP /v1──> Semantic Layer REST API │
│ Cursor, │ │ (FastMCP (FastAPI: parse OBML, │
│ any MCP │ │ + httpx) validate, compile │
│ client │ │ to SQL) │
└────────────┘ └──────────────────────────────────────────────────────┘
- No business logic — all tool calls delegate to the REST API (v1 endpoints)
- Dual-mode — auto-detects single-model or multi-model API mode at startup
- Auto-session management — creates an API session on first tool call, caches the ID (multi-model mode)
- 15 tools (single-model mode) or 19 tools (multi-model mode) for querying (QueryObject), execution, batch, discovery, composability (ACR), examples, diagrams, RDF/SPARQL, OSI export, and OBML reference + JSON schemas. (20 distinct tools exist in total; the API mode selects which subset is active — they overlap in 14 — and no client ever sees all 20 at once.) The visible surface is narrowed further in the design-time phase and when query execution is disabled (see [Design-time vs run-time tool switching](#design-time-vs-run-time-tool-switching))
- 4 prompts + 2 resources for OBML / OBSQL reference and usage guidance
Live Demo
A public demo of the OrionBelt Semantic Layer API is available at:
> API endpoint: https://orionbelt.ralforion.com — Swagger UI | ReDoc | Gradio UI
Set API_BASE_URL=https://orionbelt.ralforion.com in your .env file to use it (see .env.example).
Installation
uv sync
For development (includes pytest, respx, ruff):
uv sync --all-groups
Usage
stdio (default)
uv run server.py
HTTP transport
MCP_TRANSPORT=http uv run python server.py
MCP client configuration
Add to your MCP client config (e.g. claude_desktop_config.json):
{
"mcpServers": {
"orionbelt": {
"command": "uv",
"args": ["run", "python", "server.py"],
"cwd": "/path/to/orionbelt-semantic-layer-mcp"
}
}
}
Configuration
Environment variables or .env file (pydantic-settings). See .env.example for defaults.
| Variable | Default | Description | | ----------------- | ------------ | ------------------------------------- | | API_BASE_URL | — (required) | OrionBelt Semantic Layer REST API URL | | API_KEY | — (unset) | API credential; required only when the API runs with AUTH_MODE=api_key | | API_KEY_HEADER | X-API-Key | Header the credential is sent in; must match the API's API_KEY_HEADER | | MCP_TRANSPORT | stdio | stdio, http, or sse | | MCP_SERVER_HOST | localhost | Bind host for HTTP/SSE | | MCP_SERVER_PORT | 9000 | Bind port for HTTP/SSE | | LOG_LEVEL | INFO | Logging level | | API_TIMEOUT | 30 | HTTP timeout in seconds |
Tools
Model lifecycle
| MCP Tool | Description | | ------------------------------- | ---------------------------------------------------------------- | | get_obml_reference() | Returns the full OBML format specification | | load_model(model? \| osi_yaml?, dedup=True) | Parse, validate, and store a model (returns health + model_load). Pass model (OBML JSON) or osi_yaml (OSI YAML, converted to OBML server-side) | | describe_model(model_id) | Inspect data objects, dimensions, measures, metrics | | remove_model(model_id) | Remove a model from the current session | | list_models() | List all models loaded in the current session | | export_model_to_osi(model_id, ...) | Export a loaded model as OSI YAML |
Model discovery
| MCP Tool | Description | | ---------------------------------------- | ------------------------------------------------------------------------------------------------ | | find_artefacts(model_id, query?, kind?, name?) | Look up artefacts. With query → fuzzy, ranked search (resolve a vague term: exact / synonym / fuzzy). Without query → exact, deterministic lookup (all artefacts, one kind, or one named artefact, full records) | | explain_artefact(model_id, name) | Explain lineage of a dimension, measure, or metric | | list_examples(model_id, intent?) | List authored example queries (filterable by intent tag) | | get_example(model_id, name) | Get one example with query + compiled SQL preview | | get_join_graph(model_id) | Return the join graph as an adjacency list | | find_composables(query_json?, anchors?, anchor_type?, model_id?) | ACR — given an in-progress query or named anchor(s), return the dimensions/measures/metrics that still compose into a valid, fanout-free result (plus CFL candidates). Guaranteed to compile |
Query, execution & diagrams
| MCP Tool | Description | | ----------------------------- | ------------------------------------------------------- | | execute_query(...) | Compile and execute a QueryObject, returning SQL + rows | | run_batch(queries, ...) | One-shot: load a model + run N queries in parallel | | get_model_diagram(model_id) | Generate a Mermaid ER diagram for a loaded model |
Semantic graph (RDF / SPARQL)
| MCP Tool | Description | | --------------------------------------------- | ------------------------------------------- | | get_model_graph(model_id) | Return the model as OBSL-Core RDF (Turtle) | | query_model_graph_by_sparql(query, ...) | Run a read-only SPARQL query (SELECT / ASK) |
References
| MCP Tool | Description | | ----------------------- | ------------------------------------------------------- | | get_obml_reference() | OBML (model authoring) grammar reference | | get_json_schema(name) | JSON Schema for obml (model) or query (QueryObject) |
Utilities
| MCP Tool | Description | | ----------------- | -------------------------------------------- | | list_dialects() | List available SQL dialects and capabilities |
Design-time vs run-time tool switching
The server presents a phase-scoped tool surface: instead of listing all all tools at once, it shows only the tools that make sense for where you are in the model lifecycle. About half the tools are meaningless until a model is loaded (execute_query, describe_model, find_artefacts, …) and the rest are about authoring or reference (get_obml_reference, get_json_schema, list_dialects). Splitting them keeps the surface small and prevents a whole class of error — calling a query tool with no model loaded.
Three buckets, swapped by phase
Tools fall into three buckets. The visible surface is a swap at the load/unload transition, not additive — the run phase does not show the design/reference tools:
| Bucket | Listed when | Tools | | --------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Always | always (both phases) | load_model, remove_model (transition verbs — stay available in the run phase so a second model can be loaded mid-session, up to max_models_per_session); run_batch (self-contained one-shot — loads/references a model inline, so it needs no prior session state); get_json_schema (QueryObject/OBML schemas — needed in both phases) | | Design-only | only when no model loaded | get_obml_reference, list_dialects | | Run-only | only when a model is loaded | describe_model, get_model_diagram, find_artefacts, explain_artefact, execute_query, list_examples, get_example, get_model_graph, get_join_graph, find_composables, query_model_graph_by_sparql, list_models, export_model_to_osi |
load_model (returns "re-list" signal)
┌─────────────────┐ ────────────────────────────────▶ ┌───────────────┐
│ design phase │ │ run phase │
│ always + design │ ◀─────────────────────────────── │ always + run │
└─────────────────┘ remove_model (last model) / TTL └───────────────┘
expiry — back to design phase
So design phase → always + design-only, run phase → always + run-only. Design/reference tools are hidden once a model is loaded, keeping the run surface focused on querying.
Re-listing
The MCP tools/list response is filtered to the active phase. Because the stateless MCP spec makes push notifications (notifications/tools/list_changed) unreliable, transitions are pull-based: load_model (design → run) and remove_model (run → design, once no models remain) return a short signal telling the client to re-list its tools and pick up the swapped surface.
Guard against premature calls
If a client calls a run-only verb while still in the design phase (e.g. a stale host that hasn't re-listed yet), the server returns a structured error rather than an opaque failure:
> No model loaded — 'execute_query' is a run-time tool and is not available > yet. Call load_model first, then re-list tools.
Capability gating (orthogonal to phase)
Separately from lifecycle phase, a tool can be hidden because the server is configured not to support it. The execution tool execute_query is gated on the API's query_execute capability: when the server runs compile-only it is dropped from tools/list and calling it returns a structured error. This composes with phase — a verb is listed only if its phase is active and its capability is enabled. The mechanism is a general capability registry, so future "the server can't do X here" flags hide their tools the same way.
Single-model mode
When the API runs in single-model mode a model is pre-loaded at startup, so the server is permanently in the run-time phase — every applicable tool is listed from the first request and there is no load_model step.
> Note on caching hints. The 2026-07-28 MCP spec adds ttlMs / cacheScope > hints on tools/list (SEP-2549). These are intentionally not set yet — the > fields are a release candidate, and FastMCP's list-tools hook exposes only the > tool list, not the result envelope. The explicit re-list signal above is the > primary (and spec-recommended) transition mechanism in the meantime.
Supported SQL Dialects
postgres, snowflake, clickhouse, databricks, dremio, bigquery, duckdb
Workflow
- Get reference — call
get_obml_reference()to learn OBML syntax - Load model — call
load_model(model_yaml)to get amodel_id - Explore — call
describe_model(model_id)or use discovery tools (find_artefacts,explain_artefact) - Execute — call
execute_query(model_id, query_json='{"select": {"dimensions": [...], "measures": [...]}}')to compile and run SQL, returning rows (requiresQUERY_EXECUTE=trueon the API; seeget_json_schema("query")for the QueryObject shape)
Integration Guides
Use the OrionBelt Semantic Layer MCP server with popular AI agent frameworks and automation platforms:
| Framework | Transport | Guide | | --------------------- | ---------------- | -------------------------------------------------------------------------------- | | OpenAI Agents SDK | stdio, HTTP, SSE | [docs/integrations/openai-agents-sdk.md](docs/integrations/openai-agents-sdk.md) | | LangChain | stdio, HTTP | [docs/integrations/langchain.md](docs/integrations/langchain.md) | | Google ADK | stdio, HTTP, SSE | [docs/integrations/google-adk.md](docs/integrations/google-adk.md) | | n8n | HTTP, SSE | [docs/integrations/n8n.md](docs/integrations/n8n.md) | | CrewAI | stdio, HTTP | [docs/integrations/crewai.md](docs/integrations/crewai.md) |
Each guide includes quick-start examples, multi-agent patterns, and connection options for both the hosted demo and self-hosted deployments.
Development
# Run tests
uv run pytest
# Lint and format
uv run ruff check server.py
uv run ruff format server.py tests/
# Set up pre-commit hooks (recommended)
./scripts/setup-hooks.sh
Release Process
The release script (scripts/release.sh) includes comprehensive pre-flight checks to prevent issues like the v2.8.2 formatting problem:
-
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ralforion
- Source: ralforion/orionbelt-semantic-layer-mcp
- License: Apache-2.0
- Homepage: https://ralforion.com/orionbelt-semantic-layer/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.