Misata

MCP serverDev tools

Multi-table synthetic data with FK integrity and exact declared aggregates. No real data.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Connect ahel once, and every AI you use reads what you have installed.

From the project's README

As published by rasinmuhammed/misata in README.md.

You declare the outcome. Misata generates the data that provably matches it.

Realistic, relational rows that hit exact revenue curves, fraud rates, referential integrity, and statistical structure. From a sentence, YAML, or your database. No real data, no ML model.

Prefer no code? Try Misata Studio, the no-code synthetic data generator: design a schema on a canvas or describe your dataset in plain English, then generate it in your browser. Same engine, same integrity proof.


Most synthetic-data tools learn from a real dataset and imitate it. Misata works the other way: you declare the outcome you want: "monthly revenue rises from $50k to $200k," "fraud is 3% in Q1 rising to 8% by Q4," "every customer's total_spent equals the sum of their orders", and Misata generates individual rows whose aggregates hit those targets exactly, with full referential integrity, from no source data at all.

This is outcome-conformant generation. The mechanism is formalised in an arXiv preprint (2606.08736): a closed-form method that satisfies declared aggregates to $0.00 error, where off-the-shelf imitation synthesisers trained on the same data miss by 74–86%. Every run can also emit an Oracle report, a proof bundle covering referential integrity, constraints, temporal consistency, and reproducibility.

It generates from a plain-English description, a YAML schema, or an existing database schema. No machine-learning model is required. No real data is needed.

Built for:

  • Known-answer testing: declare the KPI, generate the data, then assert your dbt, Spark, or SQL transform returns exactly that number. A pipeline test with a ground truth, before any real data exists
  • Database seeding: fill dev and staging environments with production-like data
  • Integration tests: relational fixtures with FK integrity across every table
  • Demos and prototypes: realistic numbers, names, and distributions, no PII
  • BI and dashboard development: data shaped like your real domain before launch
  • Statistical method validation: longitudinal, grouped, and multi-site datasets that pass mixed-effects models, ICC tests, and autocorrelation checks

Declare or mimic: two ways in

Misata works in two modes, and the difference is the whole point:

  • Declare (the default, no data required). You state the schema and the outcomes you want, exact revenue curves, fraud rates, rollups, constraints, and Misata generates rows from scratch that conform to them. Use this when you do not have real data, or when you need a known answer to test a pipeline, dashboard, or demo against.
  • Mimic (when you already have data). Point misata.mimic() at a real CSV and get a synthetic twin that matches its distributions and correlations but contains none of the original rows, with fidelity_report and privacy_report to measure the result. Use this for privacy-safe copies of data you already hold.

Most synthetic-data tools only do the second, learning from a real dataset and imitating it. Misata leads with the first: you declare the answer, then generate the data around it.


Research

Misata's exact-aggregate engine is backed by an arXiv preprint:

Declarative Outcome-Conformant Synthesis: Exact, Closed-Form Specification Satisfaction and a Conformance Benchmark
Muhammed Rasin, arXiv:2606.08736 (2026)
https://arxiv.org/abs/2606.08736v1

The paper formalises the core claim: when you declare "SaaS MRR from $50k in January to $200k in December", Misata generates individual transactions whose monthly totals match the declared curve to exactly $0.00 error, not approximately, but provably, via a closed-form Gamma conditional-sum mechanism (Lukacs' characterisation). Off-the-shelf imitation synthesisers trained on the very same data miss the declared monthly aggregate by 74–86%; Misata reaches exactly 0.

The paper also introduces SpecBench: the first benchmark measuring conformance to analytical outcomes for cold-start relational synthesis. Misata is the reference implementation.

@article{rasin2026declarative,
  title   = {Declarative Outcome-Conformant Synthesis: Exact, Closed-Form
             Specification Satisfaction and a Conformance Benchmark},
  author  = {Rasin, Muhammed},
  year    = {2026},
  url     = {https://arxiv.org/abs/2606.08736v1}
}

Install

pip install misata

Optional extras:

pip install "misata[llm]"        # multi-provider LLM schema generation
pip install "misata[documents]"  # PDF output via weasyprint
pip install "misata[advanced]"   # SDV/CTGAN statistical synthesis
pip install "misata[mcp]"        # MCP server, expose Misata to Claude, Cursor, and other AI agents
pip install "misata[evalpack]"   # evalpacks: verified eval databases for data agents (DuckDB)

Use it from a coding agent

Misata ships an Agent Skill, so Claude Code and anything else that reads SKILL.md knows which entry point fits which request and what is worth declaring:

/plugin marketplace add rasinmuhammed/misata
/plugin install misata@misata

The skill drives the CLI, so pip install misata is still required. There is also an MCP server (pip install "misata[mcp]") and a Claude Desktop extension in mcpb/.


Use Misata from Claude / Cursor / Windsurf (MCP)

Misata ships a built-in Model Context Protocol server with a clear division of labour: the AI agent designs the schema, Misata guarantees the math. Agents are good at knowing that a veterinary clinic needs a species column; Misata is good at making 50 000 rows where every foreign key resolves, every roll-up reconciles to the cent, and the same seed reproduces byte-identical output. The primary tool, generate_from_schema, accepts the agent's schema dict and returns the data plus an integrity proof: per-relationship orphan counts the agent can show you.

1. Install:

pip install "misata[mcp]"

2. Add to Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "misata": {
      "command": "misata-mcp"
    }
  }
}

Restart Claude Desktop. Then just ask:

"Generate a fintech dataset with 1 000 customers, payments, and a 2% fraud rate."

"Design a clinical-trials database (sites, patients, visits, adverse events) and generate 100k rows."

"I need SaaS data: MRR from $50k in January, doubled by December, with a Q3 slump."

The agent designs whatever tables the request needs (any domain; it isn't limited to Misata's built-ins), calls Misata, writes CSVs to disk, and reports back with previews and the verified integrity summary. See the MCP guide for Cursor/Windsurf/Zed setup and all six available tools.

mcp-name: io.github.rasinmuhammed/misata


Quick start

misata generate \
  --story "Brazilian fintech with R$ payments, CPF verification, and 3% fraud" \
  --rows 1000 \
  --output-dir ./demo_data

# Writes CSVs plus:
# ./demo_data/oracle_report.json
import misata

# One sentence → multi-table DataFrame dict
tables = misata.generate("A SaaS company with 5k users, monthly subscriptions, and 20% churn")

print(tables["users"].head())
print(tables["subscriptions"].head())
# Or from the CLI
misata generate --story "A SaaS company with 5k users and 20% churn" --rows 5000

Misata Oracle

The Oracle report is Misata's proof layer. It separates hard guarantees from advisory realism checks so generated data can be trusted in CI, demos, notebooks, and research comparisons.

Guaranteed checks:

  • referential integrity across configured relationships
  • requested row-count fulfillment
  • schema validation and configured constraints
  • deterministic reproducibility when a seed is set

Advisory checks:

  • quality score and plausibility warnings
  • privacy heuristics
  • schema-vs-output fidelity score
  • locale/domain fit for countries, cities, phone prefixes, and national IDs
  • data-card metadata
import misata

schema = misata.parse("Brazilian fintech with CPF verification", rows=1000)
tables = misata.generate_from_schema(schema)
oracle = misata.build_oracle_report(tables, schema, seed=schema.seed)

print(oracle["passed"])
print(oracle["advisory"]["locale_domain_fit"]["locale"])

Mimic mode: clone any CSV in one call

Point misata.mimic() at a real dataset and get a synthetic twin that matches every column's distributions but contains none of the original rows. No schema authoring, no config.

import pandas as pd
import misata

real = pd.read_csv("titanic.csv")
twin = misata.mimic(real, rows=2000, seed=42, table_name="passengers")["passengers"]

The profiler handles the columns that break other tools:

  • Alphanumeric code columns (Ticket "A/5 21171", Cabin "C85", SKUs, reference numbers) are detected by their character-class shape and reproduced structurally, same shapes in the right proportions, entirely new values, zero verbatim leak from the source. They no longer fall through to prose text generation.
  • Floats keep their cents. A Fare of 7.25 generates as 7.25-shaped values. The profiler infers decimal places from the data; semantic quantization (charm pricing) never fires on mimicked columns.
  • Distributions are fit from the data. Skewed-positive columns get lognormal; constant columns get a uniform stub; everything else gets normal. Categorical columns with fewer than 50 values carry their real frequencies.
# Verify: no verbatim rows can leak through
shared = [c for c in real.columns if c in twin.columns]
overlap = pd.merge(real[shared].astype(str), twin[shared].astype(str), how="inner")
assert len(overlap) == 0

Eight ways to generate data

1. Plain English, no config required

tables = misata.generate("A fintech startup with 10k customers, fraud rate 3%, and IBAN accounts")

Misata reads the story, infers domain (fintech), scale (10 000 rows), and column semantics (fraud flag, IBAN format), no schema authoring needed.

A sentence is read by a recogniser that handles a fixed set of phrasings. Anything it cannot turn into a declaration is named in a warning rather than dropped, so you always know what took effect. When a prompt needs to be exact, write it as a spec instead.

1b. A structured spec, parsed deterministically

Prose that declares tables, row counts, columns and rules is not a story, and guessing at it is the wrong tool. Misata detects that shape and parses it directly, so "exactly 4000" means exactly 4000. No model, no inference, no rewriting.

Table 1: accounts
Rows: exactly 600
Columns:
  account_id
  company_name
  plan
  seats
  signed_up_on
plan must only be:
Starter
Professional
Enterprise
seats must be 1 to 120
signed_up_on must be 2023-01-01 to 2023-12-31

Table 2: invoices
Rows: exactly 3200
Columns:
  invoice_id
  account_id
  amount
  issued_on
account_id must match values from accounts table
amount must be 120 to 8500
issued_on must be 2024-01-01 to 2024-12-31

Revenue curve on invoices.amount by issued_on:
Jan 180000
Feb 195000
Mar 210000
tables = misata.generate_from_schema(misata.parse(open("spec.txt").read()))

What the spec guarantees:

You writeYou get
Rows: exactly 32003200 rows, not about 3200
x must match values from y tablea foreign key with zero orphans
plan must only be: + a listthose values and no others
seats must be 1 to 120every row inside the bound
signed_up_on must be 2023-01-01 to 2023-12-31dates inside that window
Revenue curve on t.col by t.date:each month lands on its figure to the cent

Rules can sit inside a table's block or in one section at the end; either way they attach by column name. A column ending _on, _at, _date or _for is generated as a date. Anything the parser cannot translate is listed back to you, never guessed at.

2. YAML schema-as-code, commit it to git

misata init           # scaffolds misata.yaml in the current directory
misata generate       # reads misata.yaml automatically
# misata.yaml
name: my-app
seed: 42

tables:
  users:
    rows: 1000
    columns:
      user_id: { type: int, unique: true }
      email:   { type: text, text_type: email }
      plan:    { type: categorical, choices: [free, pro, enterprise] }

  orders:
    rows: 5000
    columns:
      order_id: { type: int, unique: true }
      user_id:  { type: foreign_key }
      amount:   { type: float, min: 5.0, max: 500.0 }

relationships:
  - "users.user_id → orders.user_id"

constraints:
  - name: amount_above_cost
    table: orders
    type: inequality
    column_a: amount
    operator: ">"
    column_b: cost
schema = misata.load_yaml_schema("misata.yaml")
tables = misata.generate_from_schema(schema)

3. Seed an existing database directly

from misata import schema_from_db, generate_from_schema, seed_database

# Introspect the live schema: no manual column definitions
schema = schema_from_db("postgresql://user:pass@localhost/myapp")
tables = generate_from_schema(schema)

# Seed it back: insert order respects FK dependencies automatically
report = seed_database(tables, "postgresql://user:pass@localhost/myapp_dev")
# SeedReport: seeded 6 tables, 47,300 rows in 1.2s
# One-command workflow
misata init --db postgresql://user:pass@localhost/myapp   # writes misata.yaml
misata generate --db-url postgresql://user:pass@localhost/myapp_dev --db-create

SQLAlchemy models are supported too:

from misata import seed_from_sqlalchemy_models
from myapp.models import Base

report = seed_from_sqlalchemy_models(Base, db_url="sqlite:///test.db", row_count=500, create_tables=True)

4. From a dbt project's own schema.yml

cd my-dbt-project && misata dbt-seed

No story, no config. Misata reads the properties YAML your project already has and generates seed CSVs that satisfy it: relationships tests become foreign keys with guaranteed integrity, accepted_values become the exact category pools, unique and not_null become hard constraints, and data_type plus column-name semantics decide the rest. Then:

dbt build   # seed + run + test — the tests you already wrote, passing on day zero

Both the legacy inline test syntax and the dbt 1.9+ arguments: nesting are understood. Tests Misata can't translate (dbt_utils.*, custom generics) are listed in the output rather than silently guessed at.

5. From a Prisma schema

cd my-app && misata prisma-seed

Reads the schema.prisma your app already maintains: @relation becomes foreign keys with zero orphans, enums become the exact value pools, @id and @unique are honoured, @@id/@@unique become composite uniqueness, and optional fields may be null. CSVs land in seed-data/ ready for your seed script.

6. Python dict schema

schema = misata.from_dict_schema({
    "customers": {
        "id":    {"type": "integer", "primary_key": True},
        "email": {"type": "email"},
        "plan":  {"type": "string", "enum": ["free", "pro", "enterprise"]},
    },
    "orders": {
        "id":          {"type": "integer", "primary_key": True},
        "customer_id": {"type": "integer", "foreign_key": {"table": "customers", "column": "id"}},
        "amount":      {"type": "float", "min": 1.0, "max": 999.0},
        "order_date":  {"type": "date"},
    },
}, row_count=5_000)

tables = misata.generate_from_schema(schema)

Declared outcome curves: add __outcome_curves__ as a top-level key alongside the table definitions. Generated rows sum to every declared target exactly, to the cent:

import pandas as pd

schema = misata.from_dict_schema({
    "__outcome_curves__": [{
        "table": "orders",
        "column": "amount",
        "time_column": "order_date",
        "time_unit": "month",
        "value_mode": "absolute",
        "start_date": "2024-01-01",
        "avg_transaction_value": 120.0,
        "curve_points": [
            {"month": 1,  "target_value":  50_000.0},
            {"month": 6,  "target_value": 110_000.0},
            {"month": 12, "target_value": 200_000.0},
        ],
    }],
    "orders": {
        "__rows__": 5000,
        "order_id":   {"type": "integer", "primary_key": True},
        "amount":     {"type": "float", "min": 5, "max": 500},
        "order_date": {"type": "date"},
    },
}, seed=42)

tables = misata.generate_from_schema(schema)
monthly = (
    tables["orders"]
    .assign(m=pd.to_datetime(tables["orders"]["order_date"]).dt.month)
    .groupby("m")["amount"].sum()
)
assert abs(monthly[1]  -  50_000) < 0.01   # exact
assert abs(monthly[12] - 200_000) < 0.01   # exact

Exact group shares: declare how a measure divides across a categorical column ("Electronics is 40% of revenue, Home 25%") with __group_shares__. Paired with an outcome curve on the same table and measure, the shares hold to the cent inside every declared period, and the period totals still hold; without a curve, the shares hold over the table total:

schema = misata.from_dict_schema({
    "__group_shares__": [{
        "table": "orders",
        "measure": "amount",
        "group_column": "category",
        "shares": {"Electronics": 0.4, "Home": 0.25, "Toys": 0.2, "Grocery": 0.15},
    }],
    # ... same orders table and __outcome_curves__ as above,
    # plus a "category" enum column
}, seed=42)

A period with fewer rows than positive-share groups is skipped with a warning rather than silently mangled; see LIMITATIONS.md. story_audit verifies the shares in the output, and evalpacks turn each period-group pair into a verified filtered-aggregation question.

Constraints and correlations: enforce business rules and inter-column relationships directly in the dict schema:

schema = misata.from_dict_schema({
    "patients": {
        "__rows__": 1000,
        "__constraints__": [
            # visit must be on or after enrollment: enforced at generation, not post-processing
            {"type": "inequality", "column_a": "visit_date",
             "operator": ">=", "column_b": "enroll_date", "action": "cap"},
        ],
        "__correlations__": [
            # heavier patients tend to have higher blood pressure (r = 0.41)
            {"col_a": "bmi", "col_b": "systolic_bp", "r": 0.41},
        ],
        "patient_id":  {"type": "integer", "primary_key": True},
        "enroll_date": {"type": "date"},
        "visit_date":  {"type": "date"},
        "bmi":         {"type": "float", "min": 16, "max": 55},
        "systolic_bp": {"type": "float", "min": 90, "max": 200},
    },
})

__rate_curves__ works the same way for per-period rate targets on boolean or categorical columns (fraud rates, churn flags, plan distributions).

7. LLM-assisted generation, richer semantics, optional

from misata import LLMSchemaGenerator

gen = LLMSchemaGenerator(provider="groq", model="llama-3.3-70b-versatile")  # free tier, fast & reliable
# gen = LLMSchemaGenerator(provider="anthropic")   # Claude
# gen = LLMSchemaGenerator(provider="ollama", model="llama3")  # fully local, no API key

schema = gen.generate_from_story(
    "A fraud detection dataset, 2% positive rate, FICO scores, transaction velocity features"
)
tables = misata.generate_from_schema(schema)

Requires pip install "misata[llm]" plus one of GROQ_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY.

Groq model tip: llama-3.3-70b-versatile is the reliable free-tier default. Larger models (e.g. openai/gpt-oss-120b) can return 413 Request too large on Groq's free tier, so use them only on a paid tier. Whatever the model returns, generation never crashes on an imperfect schema: missing relationships, malformed probabilities, and out-of-range time_units are repaired automatically.

8. Incremental generation, grow a dataset without re-seeding

tables = misata.generate("A fintech company with 1000 customers", seed=1)

# Add 1 000 more rows: IDs auto-offset, FK integrity maintained across both batches
tables = misata.generate_more(tables, schema, n=1000, seed=2)
print(len(tables["customers"]))  # 2000

Realism that survives inspection

Synthetic data rarely fails on the big numbers; it fails on the small tells a reviewer spots in five seconds. Misata kills each tell with a specific, deterministic mechanism. No LLM is involved; everything is seeded and reproducible.

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
68
Forks
4
Last commit
Sep 2026
Advanced
Delivery
misata MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
Catalog kind
mcp-server
Gateway key
io-github-rasinmuhammed-misata
Source
github.com/rasinmuhammed/misata