---
name: synthdata-serve
description: >
  Spin up a read-only MCP server from a synthetic dataset, making tables queryable by Claude.
  Use this skill when the user wants to "serve data as an MCP server", "make this dataset queryable",
  "create an MCP server from my data", "expose this data to Claude", "let Claude query this dataset",
  "export a data server", "serve this xlsx", or "start a data API from my generated data".
  Works with xlsx, csv, json, or parquet input — or generates fresh data via synthdata-generate first.
version: 0.1.0
allowed-tools: Read Bash Glob Write
---

# Synthdata Serve

Expose a synthetic dataset as a **read-only MCP server** — Claude can then query tables, filter rows,
sample data, and inspect statistics using auto-generated tools. No code generation required.

## Prerequisites

```bash
pip install openpyxl pandas numpy mcp --break-system-packages
# Optional (parquet support): pip install pyarrow --break-system-packages
```

## Workflow

### Step 1: Identify the data source

Ask the user:

1. **Existing dataset or fresh generation?**
   - If existing: get the file path (xlsx, csv, json, parquet, or directory of csv/json files)
   - If fresh: use `synthdata-generate` to create data first, then serve the output

2. **Server name** (optional): defaults to `synthdata-<filename>`. This name appears in MCP tool listings.

### Step 2: Inspect the data

Run the script in inspect mode to verify what will be served:

```bash
python3 scripts/serve.py --inspect --input <path>
```

Show the user:
- Table names, row counts, column counts
- Column dtypes and null counts
- 3-row sample per table

**Confirm with the user before proceeding.**

### Step 3: Start the MCP server

The server runs over stdio. To register it with Claude Code, add to `.mcp.json` in the project root (or `~/.claude/.mcp.json` for global):

```json
{
  "mcpServers": {
    "<server-name>": {
      "command": "python3",
      "args": ["<absolute-path-to>/scripts/serve.py", "--input", "<absolute-path-to-data>"]
    }
  }
}
```

For Claude Desktop, add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "<server-name>": {
      "command": "python3",
      "args": ["<absolute-path-to>/scripts/serve.py", "--input", "<absolute-path-to-data>"]
    }
  }
}
```

After adding the config, the user needs to restart Claude Code (or Claude Desktop) to pick up the new server.

Tell the user the 5 tools that are now available:

| Tool | What it does |
|------|--------------|
| `list_tables` | Table names, row counts, column summaries |
| `describe_table` | Column details — dtypes, sample values, null/unique counts, FK hints |
| `query_table` | Fetch rows with filters, column selection, sort, limit, offset |
| `sample_rows` | Random sample of N rows |
| `get_stats` | Aggregate stats — mean, std, min, max, percentiles (numeric); top values (categorical) |

### Step 4: (Optional) Export as standalone project

If the user wants a self-contained MCP server they can deploy independently:

```bash
python3 scripts/export.py --input <path> --output ./my-data-server/ --name <server-name>
```

This produces a standalone directory with:
- `server.py` — self-contained MCP server (no synthdata dependency)
- `data/dataset.json` — embedded dataset
- `requirements.txt` — Python deps
- `README.md` — setup instructions with config snippets

### Step 5: Verify

Confirm the server is working by asking Claude to use one of the tools:

> "Use the `list_tables` tool from the `<server-name>` server to show me what's available."

Or test directly:

```bash
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | python3 scripts/serve.py --input <path> 2>/dev/null
```

## CLI Reference

| Flag | Default | Description |
|------|---------|-------------|
| `--input <path>` | (required) | Dataset path: xlsx, csv, json, parquet, or directory |
| `--name <string>` | `synthdata-<filename>` | MCP server name |
| `--inspect` | — | Print table schemas and exit (no server) |

## Query Filters

The `query_table` tool supports these filter operators:

| Filter | Example | Meaning |
|--------|---------|---------|
| Equality | `{"status": "delivered"}` | Exact match |
| Greater than | `{"salary": {">": 100000}}` | Numeric comparison |
| Less than | `{"age": {"<": 30}}` | Numeric comparison |
| Range | `{"score": {"between": [50, 90]}}` | Inclusive range |
| Membership | `{"dept": {"in": ["Eng", "Sales"]}}` | Value in list |
| Substring | `{"name": {"contains": "smith"}}` | Case-insensitive substring |
| Not equal | `{"status": {"!=": "cancelled"}}` | Exclusion |

Filters can be combined: `{"status": "delivered", "total": {">": 100}}` applies both conditions (AND logic).

## Limits

- Default query limit: 50 rows per request
- Maximum query limit: 500 rows per request
- Maximum response size: 100KB (auto-truncates if exceeded)
- All operations are read-only — no mutations

## When to use this skill vs others

| Need | Skill |
|------|-------|
| Generate new data from a schema/template | `synthdata-generate` |
| Aggregate, score, or transform data | `synthdata-compute` |
| Add rows/columns to existing data | `synthdata-extend` |
| Replace PII with fake values | `synthdata-anonymize` |
| **Expose data as a queryable MCP server** | **synthdata-serve** |
