ASCII Table MCP Server
README.md
# ASCII Table MCP Server
**Beautiful tables in terminal code blocks AND as polished images — with native Thai, Pali, Roman, CJK, and emoji support.**
```text
+------------+------------------+--------+----------------------+
| Pali | Roman | Type | Meaning |
+------------+------------------+--------+----------------------+
| ปฏิจจสมุปฺปาท | paṭiccasamuppāda | Dhamma | Dependent origination |
| กามจฺฉนฺท | kāmacchanda | Nīvaraṇa | Sensual desire |
| อวิชฺชา | avijjā | Kilesa | Ignorance |
+------------+------------------+--------+----------------------+
```
*Thai + Roman + numbers → pixel-perfect alignment, every row.*
---
> 🌐 **Try it in your browser!** → [**ASCII Table → Image Generator**](https://dhammawatthumpra-coder.github.io/ascii-table-mcp/)<br>
> Paste CSV, pick a style, export as PNG / SVG / Grid / Box / Pipe. Thai + emoji ready. No install needed.

---
**Why this exists:** Most ASCII table libraries use `len()` in Python, which counts combining marks (like Thai tone marks, Devanagari vowel signs) as separate characters. This **breaks alignment** for any language with zero-width characters. This server uses `wcwidth` — the same library terminals use — so columns line up correctly, on every platform.
---
## Features
- **9 table formats**: `grid` (`+--+`), `box` (`┌─┬─┐`), `pipe` (`| | |`), `safe`, `html`, `png`, `svg` + more
- **10 grid sub-styles**: `mysql`, `separated`, `compact`, `gfm`, `reddit`, `rounded`, `rst`, `box`, `unicode`, `dots` — inspired by [ozh/ascii-tables](https://github.com/ozh/ascii-tables)
- **Auto-format**: auto-detects numeric columns (right-align) and centers headers
- **Thai / Pali / CJK / emoji**: zero-width combining marks (ิ, ี, ฺ, ◌̱, etc.) align perfectly
- **Safe width mode**: extra padding for platforms where zero-width marks ≠ zero (Discord, browser code blocks)
- **Direct PNG export**: headless Playwright renders styled HTML → cropped PNG with Noto Sans Thai — perfect for Discord/Telegram
- **Direct SVG export**: vector SVG with embedded HTML (foreignObject) — zoomable, embeddable
- **4 polished HTML styles**: `dark`, `light`, `minimal`, `compact`
- **CSV / TSV / JSON input**: convert from any data source
- **Debug tools**: `analyze_table`, `debug_table`, `validate_table_text` — find and fix alignment issues
---
## Direct PNG Export — One Step to a Gorgeous Table Image
For Discord, Telegram, Slack, or any place that can't render a proper monospace Thai table, use `export_png` to generate a **cropped PNG** directly — no browser, no manual screenshot, no crop step.
| Style | Preview | Best for |
|-------|---------|----------|
| `dark` |  | Discord / Telegram dark mode |
| `light` |  | Documentation, PDF |
| `minimal` |  | Blog posts, websites |
| `compact` |  | Dense data, narrow screens |
```json
{
"headers": ["Pali", "Roman", "Type", "Meaning"],
"rows": [
["กมฺม", "kamma", "Noun", "action"],
["ญาณ", "ñāṇa", "Wisdom", "direct knowing"]
],
"style": "dark"
}
```
Output: a `.png` file cropped to just the table — send it with `MEDIA:<path>` in Discord/Telegram.
## Direct SVG Export — Vector That Scales
Use `export_svg` for images that need to be zoomed, embedded, or edited.
```json
{
"headers": ["Term", "Translation"],
"rows": [["กมฺม", "Kamma"], ["ธมฺม", "Dhamma"]],
"style": "dark"
}
```
SVG is HTML-backed (foreignObject) with Noto Sans Thai — renders sharp at any size.
---
## Supported Formats
---
## Quick Start
### uvx (no install)
```bash
uvx --from git+https://github.com/dhammawatthumpra-coder/ascii-table-mcp ascii-table-mcp
```
### pip install
```bash
git clone https://github.com/dhammawatthumpra-coder/ascii-table-mcp.git
cd ascii-table-mcp
pip install -e .
python -m ascii_table_mcp
```
### Register with any MCP client
**Claude Desktop:**
```json
{
"mcpServers": {
"ascii-table": {
"command": "python",
"args": ["-m", "ascii_table_mcp"]
}
}
}
```
**Hermes:**
```bash
hermes mcp add ascii-table --command "python -m ascii_table_mcp"
```
---
## MCP Tools
### `make_table` — The main event
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `headers` | `list[str]` | — | Column headers |
| `rows` / `data` | `list[list[str]]` | — | Data rows |
| `fmt` | `str` | `"grid"` | `"grid"`, `"box"`, `"pipe"`, `"safe"`, `"html"` |
| `style` | `str` | `"mysql"` | One of 10 grid styles or 4 HTML styles |
| `auto_format` | `bool` | `true` | Right-align numbers, center headers |
| `safe_width` | `bool` | `false` | Extra padding for Discord/browser |
**Example with `style="separated"`:**
```json
{
"headers": ["Product", "Price", "Qty"],
"rows": [
["Noodle Soup", "45", "3"],
["Fried Rice", "55", "2"]
],
"fmt": "grid",
"style": "separated"
}
```
```text
+============+======+=======+
| Product | Price | Qty |
+============+======+=======+
+------------+------+-------+
| Noodle Soup | 45 | 3 |
+------------+------+-------+
| Fried Rice | 55 | 2 |
+------------+------+-------+
```
Notice: `auto_format=true` right-aligns `Price` and `Qty` columns, centers headers.
### `make_table_from_csv`
```json
{
"csv_text": "Name,Role,Score\nAlice,Admin,95\nBob,User,87",
"fmt": "grid"
}
```
### `make_table_from_json`
```json
{
"json_data": "[[\"Term\",\"Meaning\"],[\"Kamma\",\"Action\"],[\"Dhamma\",\"Truth\"]]",
"fmt": "box"
}
```
### `export_png` — Direct PNG export
Render table as cropped PNG via headless Playwright.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `headers` | `list[str]` | — | Column headers |
| `rows` / `data` | `list[list[str]]` | — | Data rows |
| `style` | `str` | `\"dark\"` | `\"dark\"`, `\"light\"`, `\"minimal\"`, `\"compact\"` |
| `font_size` | `int` | `24` | Font size in px |
```json
{
"headers": ["คำบาลี", "Roman"],
"rows": [["กมฺม", "kamma"], ["ธมฺม", "dhamma"]],
"style": "dark"
}
```
→ Returns file path to cropped PNG. Send with `MEDIA:<path>`.
### `export_svg` — Direct SVG export
Render table as vector SVG (foreignObject with styled HTML).
Same parameters as `export_png`. Returns file path to `.svg`.
### Debug tools
- **`debug_table`** — shows exact char positions with drift analysis
- **`analyze_table`** — validates column alignment in `+--+` tables
- **`validate_table_text`** — structural check: column count, border alternation, format detection
---
## Thai Character Width: The Problem This Solves
Discord, browser code blocks, and some terminals **don't respect Unicode zero-width combining marks**.
Characters like ` ิ` ` ี` ` ื` ` ู` ` ฺ` each take up width=1 on screen, but `wcwidth` says they're width=0.
```text
len("ก๋วยเตี๋ยว") → 10 ← Python counts combining marks
wcwidth("ก๋วยเตี๋ยว") → 7 ← Reality: only 7 visible column positions
```
### Solution: `safe_width=True`
When enabled, the renderer uses `len()` (counts every character as width 1) instead of `wcwidth`, then adds padding for each combining mark:
```json
{
"headers": ["Name", "Price"],
"rows": [["ก๋วยเตี๋ยว", "45"]],
"fmt": "grid",
"safe_width": true
}
```
```text
+----------------+------+
| Name | Price |
+----------------+------+
| ก๋วยเตี๋ยว | 45 |
+----------------+------+
```
Without `safe_width` (discord will misalign):
```text
+------------+------+
| Name | Price |
+------------+------+
| ก๋วยเตี๋ยว | 45 | ← column drifts right by 3 chars
+------------+------+
```
### Future: Per-Character Width Database
`wcwidth` and `len()` are both approximations. A future plan is to build a **lookup table of actual display widths for each Thai character** on major platforms (Windows Discord, macOS Discord, Linux terminal) for 100% guaranteed alignment.
---
## Grid Styles Reference
| Style | Top border | Header sep | Data sep | Bottom | Notes |
|-------|-----------|-----------|----------|--------|-------|
| `mysql` | `+--+` | `+--+` | `+--+` | `+--+` | default |
| `separated` | `+==+` | `+==+` | `+--+` | `+--+` | bold header separator |
| `compact` | none | ` --- ` | none | none | frameless |
| `gfm` | none | `\|--\|` | none | none | GitHub Flavored Markdown |
| `reddit` | none | `--\|--` | none | none | Reddit tables |
| `rounded` | `.--.` | `:+:` | none | `'--'` | rounded corners |
| `rst` | `+==+` | `+--+` | `+==+` | `+==+` | reStructuredText |
| `box` | `┌┬┐` | `├┼┤` | `├┼┤` | `└┴┘` | Unicode box-drawing |
| `unicode` | `╔╦╗` | `╠╬╣` | `╠╬╣` | `╚╩╝` | double-line |
| `dots` | `┌┬┐` | `├┼┤` | none | `└┴┘` | no row separators |
### Format comparison
| Format | Example | Best for |
|--------|---------|----------|
| `grid` | `+----+----+` | Terminal, code review, GitHub |
| `box` / `safe` | `┌────┬────┐` | Presentation, formal docs |
| `pipe` | `\| \| \|` | Markdown-native |
| `grid` + `safe_width` | `+------+----+` | Discord, browser (zero-width ≠ 0) |
| `png` | `<image>` | Direct export — Discord/Telegram/chat apps |
| `svg` | `<vector>` | Vector — zoomable, embeddable |
---
## CLI Usage
```bash
# CSV args → grid
python -m ascii_table_mcp.generate_table --grid 'Name,Age' 'Alice,30' 'Bob,25'
# TSV pipe → grid
printf 'Name\tAge\nAlice\t30\n' | python -m ascii_table_mcp.generate_table --tsv --grid
# JSON
python -m ascii_table_mcp.generate_table --json '{"headers":["A","B"],"rows":[["1","2"]]}' --grid
```
```text
+-------+-----+
| Name | Age |
+-------+-----+
| Alice | 30 |
| Bob | 25 |
+-------+-----+
```
---
## Architecture
```text
ascii-table-mcp/
├── ascii_table_mcp/
│ ├── __init__.py # MCP server entry (FastMCP) — 9 tools
│ ├── generate_table.py # Core rendering engine + HTML + PNG + SVG
│ └── thaiwidth.py # Thai-aware display width
├── server.py # Thin CLI wrapper
├── table_to_image.py # HTML → screenshot helper (legacy)
├── table_screenshot.py # HTML generator (legacy)
├── requirements.txt # mcp + wcwidth
├── pyproject.toml # uv/pip packaging
├── ascii-table-mcp.bat # Windows launcher
├── LICENSE # MIT
├── README.md # This file (English)
├── README_TH.md # Thai documentation
├── GUIDE_TH.md # Thai user guide
├── GUIDE_EN.md # English user guide
└── examples/
├── ex_dark.png
├── ex_light.png
├── ex_minimal.png
└── ex_compact.png
```
---
## Why `wcwidth` Instead of `len()`?
```text
len("กมฺม") → 4 ❌ (counts the dot-below as a character)
wcswidth("กมฺม") → 3 ✅ (dot-below is zero-width)
```
Using `len()` for table layout:
```text
+----------+-----------+ ← border width 8
| Pali | Roman | ← "กมฺม" = 4 chars → padding 4 → total 9 ❌
+----------+-----------+
```
Using `wcwidth`:
```text
+---------+-----------+
| Pali | Roman | ← "กมฺม" = width 3 → padding 5 → total 8 ✅
+---------+-----------+
```
---
## Related Projects
- [ozh/ascii-tables](https://github.com/ozh/ascii-tables) — original table style system
- [dmarsters/ascii-art-mcp](https://github.com/dmarsters/ascii-art-mcp) — decorative ASCII art
- [schachmat/wego](https://github.com/schachmat/wego) — Go terminal weather (go-runewidth)
- [wcwidth](https://pypi.org/project/wcwidth/) — Python wcwidth implementation
---
## License
MIT — free for personal and commercial use.
TDQS
A4/5.0
Scored across 7 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: analysis, debugging, creation from various inputs, preview, and validation. Overlap is minimal, and agents can easily select the right tool.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern using snake_case, making the set predictable and easy to navigate.
Tool Count5/5
Seven tools is within the ideal range (3-15) and each tool contributes meaningfully to the server's table-focused scope without being excessive.
Completeness5/5
The tool set covers the full lifecycle: creation from raw data, CSV, JSON, preview, analysis, debugging, and validation. No obvious gaps for the intended domain.
Maintenance
ActivityInactive
ResponsivenessNo issues