Skip to main content

wise-pizza-mcp

Slice through your data like a hot knife through pizza.

An MCP server that wraps the wise-pizza library, letting LLMs find the most interesting segments in multidimensional data. Load CSVs once, then run multiple analyses — all through the Model Context Protocol.

What it does

wise-pizza finds segments in your data that are unusual or that explain changes between time periods. This MCP server exposes four analysis methods:

  • explain_levels — Find segments whose average is most different from the global average
  • explain_changes_in_totals — Explain what drove changes in totals between two datasets
  • explain_changes_in_average — Explain what drove changes in averages between two datasets
  • explain_timeseries — Find segments with different temporal patterns in panel data

Plus dataset management tools to load, list, and remove CSV data.

Prerequisites

This server uses uv to run. Install it if you don't have it:

curl -LsSf https://astral.sh/uv/install.sh | sh

Quick start

Install with uvx

Add to your Claude Desktop config (~/.claude/claude_desktop_config.json):

{
  "mcpServers": {
    "wise-pizza": {
      "command": "uvx",
      "args": ["wise-pizza-mcp"]
    }
  }
}

If you're working in a clone of this repo with Claude Code, the server is auto-configured via .mcp.json — no manual setup needed.

Tools

load_data

Load a CSV file into the in-memory dataset store.

Parameter Type Required Description
file_path string yes Path to a CSV file on disk
dataset_name string yes Unique name to refer to this dataset
dims list[string] yes Column names to use as dimensions (categorical columns to segment by)
total_name string yes Column name containing the totals (the value to analyze)
size_name string no Column name containing segment sizes/weights

Example:

Load the file /data/sales.csv as "sales_q1" with dimensions ["region", "product", "channel"],
total_name "revenue", and size_name "transactions"

list_loaded_datasets

Returns a list of all dataset names currently in the store.

remove_loaded_dataset

Remove a dataset from the store by name.

explain_levels

Find segments whose average is most different from the global one. Great for answering: "Which segments are unusual in this dataset?"

Parameter Type Default Description
dataset_name string required Name of a loaded dataset
max_segments int auto Maximum segments to find
min_depth int 1 Minimum dimensions per segment
max_depth int 2 Maximum dimensions per segment
constrain_signs bool true Force segment weights to match direction
cluster_values bool false Consider clusters of similar dimension values
image_size list[int] none [width, height] to generate a PNG chart

Example:

Run explain_levels on "sales_q1" with max_segments=5 and image_size=[1200, 600]

explain_changes_in_totals

Find segments that explain differences between totals of two datasets.

Parameter Type Default Description
dataset_name_1 string required Baseline dataset
dataset_name_2 string required Comparison dataset
max_segments int auto Maximum segments to find
min_depth int 1 Minimum dimensions per segment
max_depth int 2 Maximum dimensions per segment
how string "totals" "totals", "split_fits", "extra_dim", or "force_dim"
constrain_signs bool true Force segment weights to match direction
cluster_values bool false Consider clusters of similar dimension values
image_size list[int] none [width, height] for PNG chart

The how parameter controls decomposition:

  • "totals" — Combined analysis of segment total changes
  • "split_fits" — Separately decompose size changes vs average changes (returns SplitAnalysisResult)
  • "extra_dim" — Treat size/average contribution as an additional dimension
  • "force_dim" — Like extra_dim, but each segment must specify a Change_from constraint

explain_changes_in_average

Same signature as explain_changes_in_totals, but focuses on average value changes rather than total volume changes.

explain_timeseries

Split time series panel data into segments with different temporal patterns.

Parameter Type Default Description
dataset_name string required Name of a loaded dataset
time_name string required Column containing time values
num_segments int auto Number of segments to find
max_depth int 2 Maximum dimensions per segment
fit_sizes bool auto Also fit segment sizes over time
num_breaks int 3 Breakpoints in stylized time series
n_jobs int 10 Parallel jobs for tree building
ignore_averages bool true Ignore overall level when comparing
image_size list[int] none [width, height] for PNG chart

Response format

AnalysisResult

Most tools return an AnalysisResult JSON:

{
  "task": "levels",
  "segments": [
    {
      "segment": {"region": "US", "product": "A"},
      "total": 1500.0,
      "seg_size": 200.0,
      "naive_avg": 7.5,
      "impact": 120.0,
      "avg_impact": 0.6
    }
  ],
  "relevant_clusters": {},
  "global_average": 5.2,
  "markdown_summary": "| ... |",
  "image_path": "/tmp/tmpXXXXXX.png"
}
  • segment: Dimension-value pairs defining this segment
  • total: Total value within the segment
  • seg_size: Size/weight of the segment
  • naive_avg: Simple average in the segment (total / seg_size)
  • impact: Segment's impact on the overall total (lasso solver only)
  • avg_impact: Impact per unit weight (lasso solver only)
  • relevant_clusters: Cluster definitions when cluster_values=True
  • image_path: Path to generated PNG file (when image_size is set)

SplitAnalysisResult

When using how="split_fits", the response contains separate analyses:

{
  "task": "changes (split)",
  "size_analysis": { "...AnalysisResult..." },
  "average_analysis": { "...AnalysisResult..." },
  "image_path": "/tmp/tmpXXXXXX.png"
}

Use case walkthroughs

1. Finding unusual segments in current data

1. Load my sales data: load_data("/data/sales_jan.csv", "jan_sales",
   dims=["region", "product", "channel"], total_name="revenue", size_name="orders")

2. Find unusual segments: explain_levels("jan_sales", max_segments=5, image_size=[1200, 600])

This identifies segments where the average revenue per order differs most from the global average — e.g., "APAC + Premium products have 3x higher revenue per order than average".

2. Explaining changes between periods

1. Load both periods:
   load_data("/data/sales_q1.csv", "q1", dims=["region", "product"], total_name="revenue", size_name="orders")
   load_data("/data/sales_q2.csv", "q2", dims=["region", "product"], total_name="revenue", size_name="orders")

2. What drove the total change?
   explain_changes_in_totals("q1", "q2", image_size=[1000, 800])

3. What about average changes specifically?
   explain_changes_in_average("q1", "q2")

4. Break down size vs average contributions:
   explain_changes_in_totals("q1", "q2", how="split_fits")

3. Time series segmentation

1. Load panel data:
   load_data("/data/monthly_metrics.csv", "monthly",
   dims=["region", "product"], total_name="revenue", size_name="customers")

2. Find segments with different trends:
   explain_timeseries("monthly", time_name="month", image_size=[1200, 800])

This finds groups like "EU + Product B is declining while US + Product A is growing".

Development

git clone https://github.com/MotleyAI/wise-pizza-mcp.git
cd wise-pizza-mcp
pip install -e ".[dev]"
pytest tests/ -v

License

MIT

Metadata

Release files for wise-pizza-mcp 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wise-pizza-mcp 0.1.2
File Size Uploaded
wise_pizza_mcp-0.1.2.tar.gz 160.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wise-pizza-mcp 0.1.2
File Interpreter ABI Platform
wise_pizza_mcp-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 174.5 kB

Release files / wise_pizza_mcp-0.1.2.tar.gz

Download URL wise_pizza_mcp-0.1.2.tar.gz
Size 160.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5378a31d69b63e861709ce2fa09f29278dbe2983dbd770efa6f18586116bed4f
BLAKE2b-256 checksum
How to use checksums
53234bbc6e7e799873aece44d965335e62fb6b5285f5a6fe1096c0980c6fcbe7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.11

Release files / wise_pizza_mcp-0.1.2-py3-none-any.whl

Download URL wise_pizza_mcp-0.1.2-py3-none-any.whl
Size 13.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a5cdc41755bebdc7dd33c66d8ee1b01727db99894bc508a48a56772e324afdb7
BLAKE2b-256 checksum
How to use checksums
d467fd2285d5cdd04e22d11849c461802b42ca0875b100ea8fa82257f20f98e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.11

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page