Streamlit-like API for AI agents to generate structured Markdown reports — enabling expert-level data analytics through programmatic notebook-style output
Project description
notebookmd
The notebook for AI agents. Write Python. Get Markdown reports.
pip install notebookmd
python -m notebookmd # generates a full demo report instantly
from notebookmd import nb
n = nb("report.md", title="Q4 Revenue Analysis")
n.metric("Revenue", "$4.2M", delta="+18%")
n.line_chart(df, x="month", y="revenue", title="Monthly Trend")
n.table(df.head(), name="Top Performers")
n.success("Analysis complete!")
n.save()
See sample output
# Q4 Revenue Analysis
| Label | Value | Delta |
|:------|:------|:------|
| **Revenue** | **$4.2M** | ▲ +18% |

### Top Performers
| rank | name | revenue |
|-----:|:---------|--------:|
| 1 | Alice | $1.2M |
| 2 | Bob | $980K |
| 3 | Charlie | $870K |
> ✅ **Success:** Analysis complete!
---
📎 **Artifacts:** monthly_trend.png · top_performers.csv
The Problem
AI agents are great at data analysis. But when it comes to presenting results, they're stuck
with print() statements and unstructured text dumps. Meanwhile:
- Jupyter requires a running kernel, interactive cells, and a browser — none of which exist in an agent's world
- Streamlit needs a live server — great for dashboards, useless for batch analysis
- Plain Markdown by hand means every agent reinvents table formatting, chart embedding, and report structure from scratch
AI agents think sequentially. They call functions one after another. They need a tool that works the same way — call a function, get structured output. No kernel. No server. No GUI.
The Solution
notebookmd is a Jupyter-style notebook that runs as plain Python function calls and
outputs clean Markdown. Think of it as print(), but for structured reports.
n = nb("dist/analysis.md", title="Stock Analysis")
n.section("Price Data")
n.dataframe(df.head(10), name="Recent Prices")
n.summary(df, title="Statistical Summary")
n.section("Trend")
n.line_chart(df, x="date", y="close", title="30-Day Price Trend")
n.section("Export")
n.export_csv(df, "prices.csv", name="Full price data")
n.save()
The output is a self-contained Markdown file with tables, charts (as PNGs), metrics, and an artifact index — readable by humans, parseable by other agents, committable to git.
Why notebookmd?
Built for how agents actually work
Agents generate code sequentially — one function call at a time. notebookmd matches that
mental model exactly. No cells, no execution context, no state management. Just call
n.metric(), n.table(), n.line_chart() in order.
Markdown is the universal format
LLMs read Markdown natively. Humans read Markdown natively. GitHub renders it. CI/CD logs display it. It's the one format that works everywhere agents operate.
Zero dependencies by default
The core package needs nothing — not even pandas. Add optional packages only when you need them. Your agent never crashes because a charting library isn't installed; it degrades gracefully with a helpful message instead.
40+ widgets out of the box
Metrics with delta arrows, DataFrames as tables, line/bar/area charts, collapsible sections, tabs, JSON display, progress bars, badges, LaTeX math, code blocks — everything you need for a real analysis report.
Artifact management built in
Figures and CSVs are auto-saved to an assets/ directory with deduplication and relative
path linking. The report includes an auto-generated artifact index. No manual file management.
Who is this for?
- AI agent builders — using Claude, GPT, LangChain, CrewAI for data analysis tasks
- Data scientists — automating reports without Jupyter's production headaches
- DevOps / MLOps engineers — report generation in CI/CD pipelines and cron jobs
- Python developers — structured Markdown output from any script, no server needed
Installation
# Core package (zero dependencies)
pip install notebookmd
# With pandas — tables, DataFrames, CSV export, data summaries
pip install "notebookmd[pandas]"
# With matplotlib — chart image generation
pip install "notebookmd[plotting]"
# Everything
pip install "notebookmd[all]"
Requirements: Python 3.11+
Quick Start
Agent Tool Pattern
The most common use case — wrap notebookmd as an agent tool:
from notebookmd import nb
def analyze(query: str, data: dict) -> str:
"""Agent tool: generate a structured analysis report."""
n = nb("dist/agent_report.md", title=query)
n.section("Data Overview")
n.kv(data["metadata"], title="Dataset Info")
n.table(data["df"].head(), name="Sample Data")
n.section("Key Findings")
n.metric_row([
{"label": "Mean", "value": f"{data['df']['value'].mean():.2f}"},
{"label": "Std", "value": f"{data['df']['value'].std():.2f}"},
{"label": "N", "value": f"{len(data['df']):,}"},
])
n.section("Conclusion")
n.badge("BULLISH", style="success")
n.change("Revenue", current=1_200_000, previous=1_000_000, fmt=",.0f")
n.save()
return n.to_markdown()
Data Analysis
from notebookmd import nb
import pandas as pd
n = nb("dist/analysis.md", title="Stock Analysis")
df = pd.DataFrame({
"date": pd.date_range("2026-01-01", periods=30),
"close": [95 + i * 0.5 for i in range(30)],
"volume": [1_000_000 + i * 50_000 for i in range(30)],
})
n.section("Price Data")
n.dataframe(df.head(10), name="Recent Prices")
n.summary(df, title="Statistical Summary")
n.section("Trend")
n.line_chart(df, x="date", y="close", title="30-Day Price Trend")
n.bar_chart(df.tail(7), x="date", y="volume", title="Weekly Volume")
n.section("Export")
n.export_csv(df, "prices.csv", name="Full price data")
n.save()
Sections and Layout
from notebookmd import nb
n = nb("dist/report.md", title="Structured Report")
# Sections as context managers (add dividers on exit)
with n.section("Setup", "Initialize data sources"):
n.kv({"Database": "Connected", "API": "Ready"}, title="Status")
# Collapsible sections
with n.expander("Methodology", expanded=True):
n.write("Multi-factor model combining value, momentum, and quality.")
# Tab groups
tabs = n.tabs(["Overview", "Technical", "Fundamental"])
with tabs.tab("Overview"):
n.metric("Price", "$95.40", delta="+1.2%")
with tabs.tab("Technical"):
n.kv({"RSI": "62.3", "MACD": "Bullish"}, title="Indicators")
n.save()
CLI
notebookmd includes a command-line interface for running report scripts:
# Run a report script
notebookmd run analysis.py
# Live output — stream Markdown to stderr as it's generated
notebookmd run analysis.py --live
# Watch mode — re-run automatically when the script changes
notebookmd run analysis.py --watch
# Inject variables into the script
notebookmd run report.py --var ticker=AAPL --var period=1y
# Combine them
notebookmd run analysis.py --live --watch --var ticker=AAPL
| Command | Description |
|---|---|
notebookmd run <script> |
Execute a report script |
notebookmd run --live |
Stream Markdown output in real time |
notebookmd run --watch |
Re-run on file changes |
notebookmd run --var KEY=VALUE |
Inject variables into the script namespace |
notebookmd run --output, -o |
Override the output file path |
notebookmd cache show |
Display cache statistics |
notebookmd cache clear |
Clear cached data |
notebookmd version |
Show installed version |
Variables passed with --var are also available as environment variables (NOTEBOOKMD_VAR_TICKER, etc.).
API Reference
Factory
| Function | Description |
|---|---|
nb(out_md, title, assets_dir, cfg) |
Create a new Notebook report builder |
NotebookConfig(max_table_rows, float_format) |
Configure rendering behavior |
Text Elements
| Method | Description |
|---|---|
n.title(text) |
Top-level # heading |
n.header(text, divider) |
Section ## heading |
n.subheader(text, divider) |
Subsection ### heading |
n.caption(text) |
Small italic caption |
n.text(body) |
Preformatted monospace text |
n.latex(body) |
LaTeX math expression |
n.md(text) |
Raw Markdown passthrough |
n.code(source, lang) |
Fenced code block |
n.divider() |
Horizontal rule |
n.write(*args) |
Auto-format any value type |
Data Display
| Method | Description |
|---|---|
n.metric(label, value, delta, delta_color) |
Single metric card with optional delta arrow |
n.metric_row(metrics) |
Multiple metrics side-by-side in one table |
n.table(df, name, max_rows) |
DataFrame as Markdown table with truncation |
n.dataframe(df, name, max_rows) |
Alias for table() |
n.json(data, expanded) |
Formatted JSON code block |
n.kv(data, title) |
Key-value dictionary as Markdown table |
n.summary(df, title) |
Auto-generated DataFrame summary (shape, nulls, stats) |
Charts and Visualization
| Method | Description |
|---|---|
n.line_chart(data, x, y, title) |
Line chart (saves PNG via matplotlib) |
n.bar_chart(data, x, y, horizontal) |
Bar chart with optional horizontal mode |
n.area_chart(data, x, y, title) |
Area chart with fill |
n.figure(fig, filename, caption) |
Save any matplotlib figure as PNG |
n.plotly_chart(fig, filename) |
Save Plotly figure (PNG or HTML fallback) |
n.altair_chart(chart, filename) |
Save Altair/Vega-Lite chart |
n.image(source, caption, width) |
Display image from path, URL, PIL, or numpy |
Status and Feedback
| Method | Description |
|---|---|
n.success(body) |
Green success blockquote |
n.error(body) |
Red error blockquote |
n.warning(body) |
Yellow warning blockquote |
n.info(body) |
Blue info blockquote |
n.exception(exc) |
Exception display with type and message |
n.progress(value, text) |
Text-based progress bar (0.0-1.0) |
n.toast(body) |
Toast notification message |
n.balloons() / n.snow() |
Celebration markers |
Layout and Structure
| Method | Description |
|---|---|
n.section(title, description) |
Semantic section (plain call or context manager) |
n.expander(label, expanded) |
Collapsible <details> section |
n.tabs(labels) |
Tab group — use with tabs.tab(label) |
n.columns(spec) |
Column layout — use with cols.col(index) |
n.container(border) |
Visual container with optional border |
Analytics Helpers
| Method | Description |
|---|---|
n.stat(label, value, description, fmt) |
Single-line statistic with bold value |
n.stats(stats, separator) |
Multiple inline stats on one line |
n.badge(text, style) |
Inline badge/pill (success, warning, error, info) |
n.change(label, current, previous, fmt, pct) |
Value with absolute and percentage change |
n.ranking(label, value, rank, total, percentile) |
Value with rank/percentile context |
Output and Export
| Method | Description |
|---|---|
n.save() |
Write report to disk, returns Path |
n.to_markdown() |
Get report as string without saving |
n.export_csv(df, filename, name) |
Save DataFrame as CSV artifact |
How It Compares
| notebookmd | Jupyter | Streamlit | Plain print() | |
|---|---|---|---|---|
| Works without a kernel | Yes | No | No | Yes |
| Works without a server | Yes | Yes | No | Yes |
| Agent-friendly (sequential calls) | Yes | No | No | Yes |
| Rich widgets (tables, charts, metrics) | 40+ | Unlimited | Unlimited | None |
| Structured, parseable output | Yes | JSON blobs | HTML | No |
| Built-in artifact management | Yes | Manual | N/A | Manual |
| CI/CD and git friendly | Yes | Painful | N/A | Yes |
| Zero dependencies | Yes | No | No | Yes |
Extend with Plugins
notebookmd ships with 8 built-in plugins (text, data, charts, status, layout, media, analytics, utility). Add your own:
from notebookmd.plugins import PluginSpec, register_plugin
class FinancePlugin(PluginSpec):
name = "finance"
def pe_ratio(self, price: float, earnings: float) -> None:
ratio = price / earnings if earnings else float("inf")
self._w(f"**P/E Ratio:** {ratio:.1f}x\n\n")
register_plugin(FinancePlugin) # available to all notebooks
# Or per-instance:
n = nb("report.md")
n.use(FinancePlugin)
n.pe_ratio(150.0, 10.0)
Community plugins can also be distributed via pyproject.toml entry points:
[project.entry-points."notebookmd.plugins"]
my_plugin = "my_package.plugin:MyPlugin"
Configuration
from notebookmd import nb, NotebookConfig
cfg = NotebookConfig(
max_table_rows=50, # Max rows before truncation (default: 30)
float_format="{:.2f}", # Number format for floats (default: "{:.4f}")
)
n = nb("report.md", title="Report", cfg=cfg)
Examples
See examples/ — each example is a self-contained folder with a run.py script,
sample data, and a generated Markdown report with assets.
| Example | Description | Key Features |
|---|---|---|
| financial-analysis | Stock price analysis for AAPL | Tables, summary stats, weekly aggregation, price/volume charts, CSV export |
| widget-showcase | Tour of all 40+ widgets | Text, metrics, status, layout, charts, analytics helpers |
| sales-dashboard | KPI dashboard from regional sales data | Metric cards, rankings, change indicators, tabs, margin analysis |
| project-status | Sprint status report from task tracker | Badges, progress bars, team workload, estimation accuracy |
| data-exploration | Employee dataset exploration | Summary stats, department tabs, salary box plots, scatter plots |
# Run any example
python examples/financial-analysis/run.py
python examples/widget-showcase/run.py
Contributing
git clone https://github.com/minhlucvan/notebookmd.git
cd notebookmd
pip install -e ".[dev]"
pytest tests/ -v
Get Started
pip install notebookmd
python -m notebookmd
Star the repo if you find it useful. PRs and feedback welcome!
License
MIT License. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file notebookmd-1.0.0.tar.gz.
File metadata
- Download URL: notebookmd-1.0.0.tar.gz
- Upload date:
- Size: 641.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c9896bd375ceb43796b2cc8e01b177d532a655b13a85a6c8477cfdbc126914f
|
|
| MD5 |
512c5a15b29a2050f90581d85c3f4cc6
|
|
| BLAKE2b-256 |
a2bcf4b29dc1e416c9513459d25925cddf09055e7012fc40eb39326d77aa53c2
|
Provenance
The following attestation bundles were made for notebookmd-1.0.0.tar.gz:
Publisher:
release.yml on minhlucvan/notebookmd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notebookmd-1.0.0.tar.gz -
Subject digest:
4c9896bd375ceb43796b2cc8e01b177d532a655b13a85a6c8477cfdbc126914f - Sigstore transparency entry: 975873314
- Sigstore integration time:
-
Permalink:
minhlucvan/notebookmd@761dac4bcadfaf83bcfce4df350534b8b65e7ec5 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/minhlucvan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@761dac4bcadfaf83bcfce4df350534b8b65e7ec5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file notebookmd-1.0.0-py3-none-any.whl.
File metadata
- Download URL: notebookmd-1.0.0-py3-none-any.whl
- Upload date:
- Size: 49.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c782e9d1d988ed37698cded0da3f1b677006d6fe37be3f76f28cd9f30f04004
|
|
| MD5 |
1c260fe4a4805bc1193d78e40d9a7617
|
|
| BLAKE2b-256 |
e41322e999c80e16d45947ee9e05553d53bdbd30b35d60a531335b07a839a9dc
|
Provenance
The following attestation bundles were made for notebookmd-1.0.0-py3-none-any.whl:
Publisher:
release.yml on minhlucvan/notebookmd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notebookmd-1.0.0-py3-none-any.whl -
Subject digest:
1c782e9d1d988ed37698cded0da3f1b677006d6fe37be3f76f28cd9f30f04004 - Sigstore transparency entry: 975873316
- Sigstore integration time:
-
Permalink:
minhlucvan/notebookmd@761dac4bcadfaf83bcfce4df350534b8b65e7ec5 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/minhlucvan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@761dac4bcadfaf83bcfce4df350534b8b65e7ec5 -
Trigger Event:
push
-
Statement type: