metric-contracts
Supports Apache Ossie interop by importing Ossie model YAML into metric contracts and exporting contracts as Ossie custom_extensions payloads using Ossie additivity vocabulary.
Imports dbt semantic manifest files to generate metric contract stubs, mapping dbt non-additive windows, count-distinct metrics, and ratio components while leaving owner, refresh, as-of, and allowed dimensions as TODOs for human declaration.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
metric-contracts
One governed metric contract, many consumers.
A metric contract declares what a semantic layer usually leaves implicit: formula, components, source, refresh cadence, as-of semantics, allowed dimensions, additivity (additive, semi-additive with the non-additive dimension named, or non-additive), caveats, owner. Four consumers enforce it:
Validator CLI for CI. A semi-additive metric must name its non-additive dimension or the build fails. Allowed-dimension sets must be subsets of the declared registry. Ratios must carry components. Every metric needs an owner, a refresh cadence and as-of semantics.
MCP server.
list_metrics,get_contract,plan_query(metric, grain, dimensions)returns allowed or denied with the reason, so an agent reads the contract before it writes SQL.Chart meaning gate. Given a proposed chart, return veto or allow with the rule that fired: ratios are not stacked, pie'd or summed; semi-additive at a coarse grain takes LAST; pie category cap; funnels only for ordered stages; no duplicate axis values. The model proposes, the rules veto.
Decomposition check. A driver/drag breakdown must sum back to the total change, partition one dimension, and state its method for ratios. Otherwise the "insight" is rejected.
Vocabulary follows Apache Ossie discussion #465
(additivity, non_additive_dimensions[{field, window}]), and the contract ships both as a
standalone YAML file and as an Ossie custom_extensions payload. Design notes and the survey of
what dbt, Cube, Malloy, LookML, Rill, Snowflake, Databricks and others already support are in
docs/design.md.
Install in two minutes
pip install "metric-contracts[all]" # CLI + MCP server + DuckDB demo
git clone https://github.com/val-ye/metric-contracts && cd metric-contracts
metric-contracts validate examples/saas # 0 errors
metric-contracts plan active_subscribers --grain month --dims plan -c examples/saas
metric-contracts plan active_subscribers --grain month --dims plan --agg sum -c examples/saas # denied, P04
python demo/before_after.py # the before/after agent demo, offlinePython 3.11 or newer. The core package depends on pydantic, PyYAML and jsonschema only. The
mcp, demo and live extras add the MCP SDK, DuckDB and the Anthropic SDK.
Related MCP server: governed-analytics-agent
A contract
contract_version: "0.1"
domain: subscriptions
owner: { team: analytics-engineering, contact: analytics-eng@example.com }
dimensions:
- { name: date, type: time, grains: [day, week, month] }
- { name: plan, type: categorical, values: [free, starter, pro, enterprise] }
metrics:
- name: active_subscribers
kind: measure
formula: count(subscription_id)
source: { table: fct_subscriber_snapshot_daily, grain: [date, subscription_id], time_dimension: date }
additivity: semi_additive
non_additive_dimensions:
- { field: date, window: last }
dimensions: { allowed: [date, plan, region], time_grains: [day, week, month] }
refresh: { cadence: daily, lands_at: "06:00Z", settling_window: P1D }
as_of: { semantics: snapshot, snapshot_at: end_of_day }
caveats:
- A month-end figure is the last daily snapshot, never a sum of days.The full example with an additive measure, a semi-additive snapshot, a non-additive ratio and a distinct count is in examples/saas. The JSON Schema is committed under schema/ and regenerated from the pydantic models in CI.
Before and after
python demo/before_after.py seeds a synthetic DuckDB database and asks two scripted agents the
same question: total active subscribers by month for Q1, split by plan. Measured output of the
committed seed:
Agent A has only the table name. It writes
COUNT(subscription_id) ... GROUP BY monthover a daily snapshot table and reports a total 23.3x the month-end truth.Agent B calls
plan_queryfirst, receives{"date": "last", "plan": "sum"}and a reference query, and matches the month-end truth row for row.Agent B then asks to SUM across months and is denied with
P04_sum_over_non_additive_dim: active_subscribers is semi-additive over date.
python demo/before_after.py --live runs the same two prompts through a real model with
plan_query exposed as a tool (needs ANTHROPIC_API_KEY and the live extra) and prints the SQL
each one wrote.
MCP server
metric-contracts serve -c examples/saas # stdio
metric-contracts serve -c examples/saas --http # streamable-http on 127.0.0.1:8765Claude Desktop / Claude Code config:
{ "mcpServers": { "metric-contracts": { "command": "metric-contracts", "args": ["serve", "-c", "/path/to/contracts"] } } }Tools: list_metrics, get_contract, plan_query, check_chart, check_decomposition,
list_dimensions, validate_contracts. Resources: contract://schema,
contract://schema/payload, contract://{domain}. The server's instructions tell the agent to
plan before it queries and to treat a denial as an answer, not an obstacle.
CI
- uses: val-ye/metric-contracts@v0
with:
paths: contracts/Or pipx run metric-contracts validate contracts/ --format github. Exit code 1 on any error;
findings are emitted as GitHub annotations.
Rules
Every rule names the contract field it reads, so a veto is explainable and testable.
Family | Ids | What they check |
Validate | V00-V15 | schema, semi-additive names its dimension, allowed dims in registry, grains, ratio components, distinct count non-additive, source or components, owner, refresh, as-of, dangling components, ordinal funnel dims, unique names, no TODO markers |
Plan | P01-P06 | unknown metric, dimension not allowed, grain not allowed, sum over the non-additive dimension, ratio aggregated directly, distinct count rolled up |
Chart | C01-C14 | ratio stacked / pie'd / summed, semi-additive at coarse grain takes the declared window, semi-additive stacked over time, distinct count summed, pie cap, pie needs additive, funnel needs ordinal stages, duplicate axis values, dimension and grain allowed, settling window unmarked, series density |
Decompose | D01-D06 | sums to total within tolerance, one partition, partition allowed, ratio needs a method, semi-additive deltas are last-to-last, residual disclosed and bounded |
Full tables with the field each rule reads: docs/design.md.
Interop
metric-contracts import dbt target/semantic_manifest.json --out contracts/ # stubs with TODO markers
metric-contracts import ossie model.yaml --out contracts/
metric-contracts export ossie -c contracts/ --out model.ossie.yaml # payload in custom_extensionsThe dbt importer maps non_additive_dimension.window_choice max to last and min to
first, count_distinct to a non-additive distinct count, ratio metrics to components.
Refresh, as-of, owner and allowed dimensions are never inferred; they come back as TODO and
validate fails until a human declares them.
What this is not
metric-contracts is not a query engine, not a semantic layer, and not a replacement for dbt,
Cube, MetricFlow, Malloy or LookML. It does not compute metrics. It holds the declarations those
tools leave implicit, checks them in CI, and hands them to agents, chart builders and insight
generators so the rules are enforced where the mistakes happen. The reference SQL plan emits
is a DuckDB illustration of the aggregation plan, not a dialect-aware compiler.
Development
uv venv && uv pip install -e ".[dev]"
pytest
ruff check src tests demo examples && mypy
metric-contracts schema --write schema # regenerate after a model changeApache-2.0. Everything in examples/ is synthetic.
This server cannot be deployed
Maintenance
Related MCP Connectors
The system of record for AI agent authority: playbooks, routed policy questions, reusable rules.
Deterministic allow/require_approval/deny verdicts for agent actions, before they happen.
Pre-execution governance for AI agents. Deterministic PASS/FAIL/REVIEW verdicts, replayable proof.
Tenant-scoped control layer for agent-to-agent systems: governed routing, approvals, evidence.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables agents to interact with a governed semantic layer for querying and authoring metrics, providing tools for discovery, planning, validation, and execution of analytics queries.30314 PyPI2Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables agents to answer analytics questions with provable correctness by resolving metrics through the semantic layer, tracing lineage, traversing knowledge graphs, checking freshness, and searching glossary definitions, while enforcing access controls and logging every call.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to inspect schemas, walk Delta table history, run read-only DuckDB queries, and check data-quality and job-run status safely.MIT
- AlicenseAqualityCmaintenanceEnables AI agents and their users to ask business questions in plain language and receive governed metric values, breakdowns, share-of-total figures, and the exact parameterized SQL behind them, drawn from a single semantic model rather than ad-hoc SQL. It also lets clients inspect available metrics and their definitions, and preview optimized queries without executing them.4MIT