Metis is an open-source, Ossie-first, engine-neutral semantic layer runtime engine. It sits between AI agents or applications and analytical databases, turning structured requests for metrics and dimensions into deterministic SQL and, when execution is configured, analytical results.
Agents reason. Metis resolves semantics. Engines execute.
For example, an agent asks for total revenue by region. Metis resolves the metric definition and compatible dimension from an Ossie model, plans the required joins and aggregations, and generates SQL for the selected database. The same semantic definitions serve the CLI, MCP, REST, and embedded Go APIs.
- Ossie-first: Apache Ossie models define metrics, dimensions, relationships, and ontology concepts that Metis discovers, validates, and resolves.
- Engine-neutral: semantic resolution and planning are shared across database targets. Renderers generate dialect-specific SQL; execution backends manage database access. Built-in targets are Doris, ClickHouse, and DuckDB.
- Deterministic: structured semantic requests go through validation, resolution,
planning, and compilation. Metric definitions and relationship rules come from
the model. Cumulative averages retain and merge their
sumandcountstate instead of averaging already-finalized period averages. Filtered custom-calendar rolling windows preserve the preceding logical periods required for calculation and apply the requested time range to the final output. - Ready for analytical workflows: discover semantic assets, compile SQL, query metrics, compare periods, and analyze metric-change attribution.
Agent or application
|
| metrics, dimensions, filters, time ranges
v
Metis <----- Apache Ossie models
|
+--- discover / validate / resolve / plan
|
+--- compile SQL ---> database client of your choice
|
+--- execute through a configured backend ---> analytical results
Compilation works without a database connection. Execution adds connection management, cancellation, timeouts, and output limits. MCP and REST expose the same runtime services that Go applications can embed directly.
| Tool or interface | Use it to |
|---|---|
metis |
Validate and compile semantic models offline, manage local projects, or run the standalone semantic runtime. |
| MCP | Give an agent tools for semantic discovery, SQL compilation, and bounded analytics over stdio or HTTP. |
| REST | Integrate semantic discovery, compilation, explanation, and analytics into applications. |
a2sbench |
Run repeatable agent analytics experiments and inspect correctness, readiness, and execution evidence. |
| Go packages | Compose a runtime with your own configuration, policies, and database integrations. |
Requires Go 1.25 or later. The default binary does not require CGO.
git clone https://github.com/meaningforge/metis.git
cd metis
go build -o bin/metis ./cmd/metis
bin/metis validate --project demo examples/demo/models/sales.ossie.yamlThe example runtime configuration is examples/demo/metis.yaml. It registers a local project manifest that points to Ossie model files. Update those files and your DataSource configuration locally, then restart the runtime to apply changes.
A local MCP client can launch Metis as a stdio subprocess. Replace the absolute paths below with the location of your checkout:
{
"mcpServers": {
"metis": {
"command": "/absolute/path/to/metis/bin/metis",
"args": ["mcp", "--config", "/absolute/path/to/metis/examples/demo/metis.yaml"]
}
}
}Start with a request such as: “In the demo project, compile total revenue by
region.” The agent can discover the model, select total_revenue and region,
and call compile_sql.
| MCP tools | Purpose |
|---|---|
list_projects, list_models, get_model |
Find and inspect available projects and models. |
list_metrics, get_metric |
Discover metrics and inspect their definitions. |
get_dimensions, get_dimension, get_relationships |
Find compatible dimensions, time grains, and relationships. |
search_ontology_concepts, resolve_ontology_concept |
Map business concepts to semantic assets. |
compile_sql |
Validate a semantic request and return SQL with an output schema. |
query_metrics, get_dimension_values |
Retrieve bounded metric results and live dimension values. |
compare_metrics |
Compare metrics across two periods, including values and changes. |
attribute_metric |
Decompose metric changes using supported additive or ratio metrics. |
Tools that retrieve data require a configured execution backend. Metric-change attribution provides a numerical decomposition; interpreting its business causes remains the caller's task.
Configure a shared bearer token and start the server:
export METIS_API_KEY='replace-with-your-own-secret'
bin/metis serve --config examples/demo/metis.yaml --addr 127.0.0.1:8080Connect to http://127.0.0.1:8080/mcp with
Authorization: Bearer <your-token>. The REST endpoints under /v1/** use the
same authentication and semantic services. /healthz and /readyz are public
health endpoints. Local stdio runs under the subprocess owner's permissions.
For example, compile total revenue grouped by region:
curl -fsS http://127.0.0.1:8080/v1/compile-sql \
-H "Authorization: Bearer $METIS_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"dialect": "DUCKDB",
"query": {
"project": "demo",
"model": "sales",
"metrics": [{"name": "total_revenue"}],
"dimensions": [{"name": "region"}]
}
}'The response includes sql_render_result and output_schema. This endpoint
compiles the query without executing it. /v1/explain accepts the same request
shape and returns SQLExplainResult: semantic planning evidence, the same
sql_render_result and output_schema as Compile, and any compilation warnings.
Public filter operands are strings or string arrays. The Resolver interprets
them using the referenced semantic datatype, so an Integer value of
"9007199254740993", a Decimal value of "0.10000000000000000001", and a
Boolean value of "true" become typed database parameters without asking API
clients to reproduce database types in JSON. Incompatible or out-of-range
operands return INVALID_FILTER_VALUE without disclosing the value.
The Resolver also rejects ordered comparisons for Boolean and Opaque fields and
validates Date, Time, DateTime, and DateTimeTz literals before planning. A query
targets one selected data source; Metis does not perform cross-database query
execution.
Explain generates SQL without executing it.
To filter source metrics by matching rows on one declared detail relationship without duplicating source measures, use a relationship-existence predicate:
{
"metrics": [{"name": "order_revenue"}],
"filters": {
"kind": "exists",
"relationship": "orders_to_items",
"where": {
"kind": "filter",
"filter": {"field": "items.category", "operator": "eq", "value": "target"}
}
}
}This compiles to a correlated EXISTS; related rows decide membership but do
not change the source grain. See the
Agent query contract for
the bounded V1 shape and rejection rules.
Semantic authoring commands use metis semantic; the former metis project
group is removed without an alias. --project and project.yaml still identify
the Project namespace and its manifest.
The metis model, metis semantic, and metis query commands work offline. Use
them to validate and inspect models, compare projects, or generate SQL without
starting a server, connecting to a database, or resolving secrets.
go build -o bin/metis ./cmd/metis
bin/metis query compile \
--model examples/demo/models/sales.ossie.yaml \
--dialect DUCKDB --metric total_revenue --dimension regionUse --dialect DORIS, CLICKHOUSE, or DUCKDB to select a target. Add repeatable
--metric, --dimension, and --filter flags, or pass a structured request with
--request-json.
metis query compile outputs JSON containing dialect, sql, and optional parameters.
SQL retains its placeholders; pass parameter values in order to your database
driver. Values are never interpolated into SQL text.
| Command | Purpose |
|---|---|
metis query compile |
Compile a semantic request into SQL and parameters as JSON. |
metis model validate, metis model inspect |
Validate an Ossie document or inspect its metadata. |
metis semantic validate [--offline], metis semantic inspect |
Load and check a complete semantic project without database connections. |
metis semantic validate --online |
Inspect query dependencies and check Doris/ClickHouse or optional DuckDB EXPLAIN acceptance. |
metis semantic diff |
Compare two local semantic project inputs. |
metis semantic init |
Generate a reviewable Ossie project from catalog evidence and an explicit map, offline. |
metis semantic test --mode compile |
Check project-owned compile expectations in CI without connecting to a database. |
metis semantic test --mode runtime |
Assert metric results against an externally prepared database fixture; emit JSON and optional JUnit. |
metis model format |
Format a model file. |
bin/metis semantic validate --project demo --config examples/demo/project.yaml
bin/metis model inspect --model examples/demo/models/sales.ossie.yamlValidation is offline by default. Explicit online validation requires a deployment configuration, query inventory and a fresh report path:
bin/metis semantic validate --online --project sales --config ./metis.yaml \
--queries ./queries.json --output ./validation.jsonSee online validation for modes, authorization, parameter support and report coverage.
ClickHouse supports server-bound query and policy parameters during online validation. Doris parameterized validation remains explicitly unsupported; unparameterized validation is available for both backends.
Run the demo compile suite against a complete project. The command writes a private JSON report and exits nonzero on a failed or incomplete case:
bin/metis semantic test --mode compile --project demo \
--config examples/demo/project.yaml --suite examples/demo/checks/compile.yaml \
--dialect DORIS --output ./compile-report.jsonSee the regression-suite contract
for assertion syntax, limits, and exit codes, and the
runtime fixture example for Doris/ClickHouse setup.
Both modes support --junit-output; runtime currently supports query_metrics.
These commands test your project's business definitions, not Metis engine conformance or custom authorization. Fixture setup and CI orchestration stay external; the CLI is not a general-purpose testing framework.
To start a model from metadata, use the offline authoring example:
bin/metis semantic init --catalog examples/authoring/catalog-doris.json \
--mapping examples/authoring/model-map.yaml --output ./candidate-sales
bin/metis semantic validate --project sales --config ./candidate-sales/project.yamlThe generator selects only mapped fields and optional technical row counts. Review its report and author business metrics and relationships before adoption. The authoring contract defines supported schemas and mappings. To capture metadata from Doris or ClickHouse first:
bin/metis catalog inspect --config ./metis.yaml --project sales \
--data-source warehouse --relations examples/authoring/relations.json \
--output ./catalog.jsonOnly explicitly selected, qualified relations are inspected; there is no database
crawl or row sampling. The command authorizes before reading deployment/source
configuration or resolving credentials. Local CLI authoring uses the operator's
OS/database identity; remote embedders must enforce Project author and their
physical metadata access policy. The snapshot feeds semantic init; successful
inspection does not prove SELECT permission. Follow the
Doris/ClickHouse table-to-query walkthrough
for disposable setup data, explicit business-model review, and verified query
results through the CLI and authenticated REST interface.
To enable execution, reference a named DataSource from your project registration and define it in a local DataSource registry. The DataSource type selects the database backend and SQL renderer.
The default build includes Doris and ClickHouse execution backends. To enable
DuckDB execution, catalog inspection and online validation, build with CGO and the duckdb tag:
CGO_ENABLED=1 go build -tags duckdb -o bin/metis ./cmd/metisThe local DuckDB walkthrough covers an existing read-only database file through catalog capture, reviewed project generation, parameterized online validation and result tests, without Docker. Offline generation from DuckDB snapshots and DuckDB SQL compilation work with the default build. Compile-only deployments require no database credentials. The execution runtime manages connections, secrets, cancellation, timeouts, and output limits.
A2SBench (Agent-to-SQL Benchmark) evaluates how agents use Metis Core to complete analytical tasks. It runs frozen questions with explicit budgets, scores independently reviewed result expectations, records attempts and query evidence, and produces machine-readable reports. Metis MCP is the system under test; OKF assets/direct SQL provide controlled baselines. Neutrality means fair scoring and comparable experimental conditions, not a multi-semantic-engine platform. The oracle must not favor Metis SQL spelling or output aliases.
New report identifiers use the a2sbench prefix. Branding migrations must
preserve experiment identities, tested interface names, scores and raw results;
they do not constitute new benchmark runs. Input and agent-driver protocols
retain their existing versions independently of the executable name.
These tools have separate responsibilities: metis semantic test checks an
author's explicit compile/result expectations without running an agent, while
a2sbench owns frozen benchmark suites, agent runners, scoring and comparisons.
Metis does not expose benchmark commands; A2SBench does not replace semantic
authoring, catalog capture or project-owned regression commands. Both use the
existing semantic/query services rather than implementing another query engine.
A run selects one interface: metis-mcp for Metis tools, or okf for
catalog-derived semantic files. Running the same suite with the same agent and
model through each interface enables a paired comparison.
Build with embedded DuckDB support and inspect the available commands:
make a2sbench-build
bin/a2sbench --help
bin/a2sbench run --helpAgent runs require an installed, authenticated agent CLI and model access. The
runner supports Codex, Claude Code, Pi, and a generic driver. The DuckDB build
also requires CGO and a C toolchain. Start with the smoke suite and set the
model identifiers to those used by your agent:
bin/a2sbench run \
--suite smoke \
--arm metis-mcp \
--agent codex \
--model '<model-id>' \
--provider '<provider>' \
--model-version '<model-version>' \
--output ./a2sbench-results/smoke-metisCompleted runs contain manifest.json, collection.json, attempts.jsonl, and
report.json. Repeat an interrupted command with --resume to keep completed
work. Use --detach for a background run and a2sbench stop <output-directory>
to stop it.
For a paired experiment, repeat the run with --arm okf and a separate output
directory, then compare both collections:
bin/a2sbench analyze \
--input ./a2sbench-results/smoke-okf \
--input ./a2sbench-results/smoke-metis \
--output ./a2sbench-results/comparison.jsonAdditional commands cover workload generation (gen), catalog-derived file
creation (okfgen), reports (report), and attribution and comparison
experiments. Use bin/a2sbench <command> --help for their inputs and options.
Agent benchmark runs are separate from the standard correctness tests.
- Copy examples/demo into your project directory.
- Add your Ossie model files under
models/. - Update
project.yamlto select those files and register your project inmetis.yaml. - Validate the project with
metis semantic validate, then startmetis serveormetis mcpwith your runtime configuration.
Model paths are resolved relative to the project manifest. A runtime can register multiple projects; see examples/multi-project/metis.yaml.
Import packages from github.com/meaningforge/metis and pin a version or
commit in your application's go.mod.
| Task | Entry point |
|---|---|
| Load a runtime from local configuration | bootstrap.LoadRuntime |
| Build a runtime from configuration and model bytes | bootstrap.NewRuntime, RuntimeInput, ProjectInput |
| Serve REST and MCP with an identity verifier | hosting.NewHTTPHandler |
| Configure project access and data constraints | bootstrap.WithProjectAuthorizer, WithAssetVisibilityPolicy, WithDataAccessPolicy |
| Supply database backends and secret resolution | bootstrap.WithBackendRegistry, WithSecretResolver |
| Load model documents from memory | source.LoadProjectDocuments |
| Replace an active semantic snapshot | runtime.Manager.Replace |
| Close database pools | bootstrap.Runtime.Close |
Runtime integration packages live under app/bootstrap, app/hosting,
app/auth, and app/service. Database extension interfaces live under
renderer and execution. Integrations use ordinary Go interfaces and
compile-time composition. Passing an explicit nil asset visibility policy
rejects runtime initialization. A request pinned again by the same runtime
keeps its snapshot; pinning it through another runtime creates a new scope
for that runtime.
The embedding example constructs a runtime from memory, applies access policies, and replaces a semantic snapshot. Before v1, pin exact versions and run compatibility tests when upgrading.
The documentation and license checks require Python 3 in addition to Go.
go mod tidy
make check
make test-e2e
make test-duckdb-backendmake check runs documentation and source contract checks, go vet, unit tests,
sample models, semantic conformance tests, and the quickstart verification.
The Apache Ossie fixture check downloads a pinned upstream commit. For an offline
run, set OSSIE_GIT_DIR to a local Apache Ossie Git object directory containing
that commit. Database integration tests require explicitly configured services
and run separately.
CI reuses Go module/build caches. Ordinary code PRs run the complete correctness,
embedded DuckDB, E2E and container checks; full release archives additionally run
for packaging, dependency, license, CI or platform-specific changes, on main
code pushes, and for manual dispatch. Documentation-only changes use the docs
gate (changes to packaged license files still trigger full checks). Snapshot
packaging shares the same commit's correctness gate rather than repeating its
tests through GoReleaser hooks. Tag releases run make release-check before
publishing. See the CI scope classifier.
Apache License 2.0. Third-party attribution and license texts are
available in NOTICE, THIRD_PARTY_NOTICES, and
licenses/. Run make licenses after updating dependencies;
make licenses-check verifies the bundle included in release archives and images.
See the documentation guide for architecture, public contracts, model authoring, execution, and A2SBench. Core RFCs record proposals and design rationale, with explicit lifecycle status.