compono-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: compono-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server exposing compono's render_deck/validate as MCP tools.
|
|
5
|
+
Author: Shaik-Hamzah123
|
|
6
|
+
Author-email: Shaik-Hamzah123 <hamzah.shaik2003@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Requires-Dist: compono>=0.1.0
|
|
9
|
+
Requires-Dist: fastmcp>=4.0.3
|
|
10
|
+
Requires-Python: >=3.13
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# compono-mcp
|
|
14
|
+
|
|
15
|
+
MCP server wrapper exposing [`compono`](https://pypi.org/project/compono/)'s
|
|
16
|
+
`render_deck`/`validate` as MCP tools, for any MCP-compatible client (not just
|
|
17
|
+
Claude Code).
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install compono-mcp
|
|
23
|
+
# or
|
|
24
|
+
uv add compono-mcp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
Add to your MCP client config (Claude Desktop / Claude Code style):
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{ "mcpServers": { "compono": { "command": "compono-mcp" } } }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Exposes:
|
|
36
|
+
- **`validate(spec)`** — schema + layout + overflow checks, no file write.
|
|
37
|
+
- **`render_deck(spec, output_path)`** — writes a real `.pptx` file.
|
|
38
|
+
- **resource `compono://reference`** — the full agent-facing reference doc
|
|
39
|
+
(quickstart, primitive catalog, worked examples, error shape), for MCP
|
|
40
|
+
clients that don't have Claude Code's skill system.
|
|
41
|
+
|
|
42
|
+
See the [main compono README](https://github.com/Shaik-Hamzah123/compono) for
|
|
43
|
+
the primitive schema and worked examples — `spec` here is the identical JSON
|
|
44
|
+
shape `compono.render_deck`/`validate` accept directly.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# compono-mcp
|
|
2
|
+
|
|
3
|
+
MCP server wrapper exposing [`compono`](https://pypi.org/project/compono/)'s
|
|
4
|
+
`render_deck`/`validate` as MCP tools, for any MCP-compatible client (not just
|
|
5
|
+
Claude Code).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install compono-mcp
|
|
11
|
+
# or
|
|
12
|
+
uv add compono-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Add to your MCP client config (Claude Desktop / Claude Code style):
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{ "mcpServers": { "compono": { "command": "compono-mcp" } } }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Exposes:
|
|
24
|
+
- **`validate(spec)`** — schema + layout + overflow checks, no file write.
|
|
25
|
+
- **`render_deck(spec, output_path)`** — writes a real `.pptx` file.
|
|
26
|
+
- **resource `compono://reference`** — the full agent-facing reference doc
|
|
27
|
+
(quickstart, primitive catalog, worked examples, error shape), for MCP
|
|
28
|
+
clients that don't have Claude Code's skill system.
|
|
29
|
+
|
|
30
|
+
See the [main compono README](https://github.com/Shaik-Hamzah123/compono) for
|
|
31
|
+
the primitive schema and worked examples — `spec` here is the identical JSON
|
|
32
|
+
shape `compono.render_deck`/`validate` accept directly.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "compono-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP server exposing compono's render_deck/validate as MCP tools."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Shaik-Hamzah123", email = "hamzah.shaik2003@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"compono>=0.1.0",
|
|
13
|
+
"fastmcp>=4.0.3",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
compono-mcp = "compono_mcp.server:main"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["uv_build>=0.11.17,<0.12.0"]
|
|
21
|
+
build-backend = "uv_build"
|
|
22
|
+
|
|
23
|
+
[tool.uv.sources]
|
|
24
|
+
compono = { workspace = true }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""compono-mcp — MCP server exposing compono's render_deck/validate as MCP tools."""
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Served verbatim as the `compono://reference` MCP resource (see server.py).
|
|
3
|
+
Kept in sync with the repo root skills/compono/SKILL.md and README.md —
|
|
4
|
+
same convention already used between those two files. Update all three
|
|
5
|
+
together when the API surface changes.
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# compono
|
|
9
|
+
|
|
10
|
+
**Agent-oriented, code-based PPTX generation — "Manim, but for PowerPoint."**
|
|
11
|
+
|
|
12
|
+
compono lets you describe a slide deck as data — headers, bullet text,
|
|
13
|
+
stats, tables, charts, images, process sequences, shapes — and get back a
|
|
14
|
+
real, editable `.pptx` file. You never write raw `x`/`y`/`w`/`h`
|
|
15
|
+
coordinates: a constraint-based layout resolver computes every position
|
|
16
|
+
from a small set of typed primitives.
|
|
17
|
+
|
|
18
|
+
Every rendered element is a genuine, editable native shape (`p:sp`, `p:pic`,
|
|
19
|
+
`p:graphicFrame`) — never a flattened image or embedded video. Opening the
|
|
20
|
+
result in PowerPoint and dragging a box around works; it's a real object,
|
|
21
|
+
not a picture of one.
|
|
22
|
+
|
|
23
|
+
You are receiving this document through the `compono-mcp` MCP server's
|
|
24
|
+
`compono://reference` resource — use its `validate`/`render_deck` tools as
|
|
25
|
+
described below.
|
|
26
|
+
|
|
27
|
+
## Quickstart
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from compono import render_deck
|
|
31
|
+
|
|
32
|
+
spec = {
|
|
33
|
+
"slides": [
|
|
34
|
+
{
|
|
35
|
+
"header": {"title": "Q3 Results", "subtitle": "Engineering team"},
|
|
36
|
+
"body": [
|
|
37
|
+
{
|
|
38
|
+
"primitive": "text",
|
|
39
|
+
"mode": "bullets",
|
|
40
|
+
"content": [
|
|
41
|
+
"Shipped the new layout resolver",
|
|
42
|
+
"Cut render time by 40%",
|
|
43
|
+
"Zero overflow bugs in production",
|
|
44
|
+
],
|
|
45
|
+
"emphasis_indices": [1],
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
report = render_deck(spec, "deck.pptx")
|
|
53
|
+
print(report.pptx_path, report.warnings)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Through this MCP server, call the `validate` and `render_deck` tools with
|
|
57
|
+
the identical `spec` shape instead of importing Python directly.
|
|
58
|
+
|
|
59
|
+
## Core concepts
|
|
60
|
+
|
|
61
|
+
- **One entry point, two verbs.** `render_deck(spec, output_path)` and
|
|
62
|
+
`validate(spec)` are the only two functions you need. `validate` is cheap
|
|
63
|
+
— no pptx write, millisecond-scale — so iterate on a spec before paying
|
|
64
|
+
render cost.
|
|
65
|
+
- **A spec is plain data.** A raw `dict`/JSON (what tool-calling naturally
|
|
66
|
+
produces) is all you need — pass it straight to `render_deck`/`validate`.
|
|
67
|
+
- **You never write coordinates.** Every primitive claims space in a slide;
|
|
68
|
+
the resolver (a CSS-flexbox-style directional box model) computes real
|
|
69
|
+
EMU positions. `grid` is the one primitive that does true 2D
|
|
70
|
+
row/column math.
|
|
71
|
+
- **Errors are fixes, not diagnoses.** Every validation/render failure is
|
|
72
|
+
`{slide, primitive, field, error, detail, fix}` — see
|
|
73
|
+
[Error shape](#error-shape) below. Act on `fix`, don't just retry blindly.
|
|
74
|
+
- **render_deck returns a report, not just a file** —
|
|
75
|
+
`{pptx_path, manifest, warnings, actual_layout}` — reason about what
|
|
76
|
+
happened without reopening the file.
|
|
77
|
+
|
|
78
|
+
## Tools (via this MCP server)
|
|
79
|
+
|
|
80
|
+
| Tool | Input | Output | Notes |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| `validate` | `spec: object` | `{valid, errors, warnings}` | No file write. Never errors out on malformed input — `valid: false` with structured errors instead. |
|
|
83
|
+
| `render_deck` | `spec: object, output_path: string` | `{pptx_path, manifest, warnings}` on success, or `{valid: false, errors}` on failure | Writes a real `.pptx` at `output_path` on the machine running this server. |
|
|
84
|
+
|
|
85
|
+
A `spec` (a `Deck`) is `{template?: str, slides: [Slide, ...]}`. A `Slide` is
|
|
86
|
+
`{header?: Header, body: [primitive, ...], notes?: str}`. `body` (and
|
|
87
|
+
`grid.items`) accept any primitive, keyed by its `"primitive"` field.
|
|
88
|
+
|
|
89
|
+
**Prefer `validate` before `render_deck` when iterating** — it's cheap and
|
|
90
|
+
gives you the same structured errors without writing a file.
|
|
91
|
+
|
|
92
|
+
### Error shape
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"slide": 3,
|
|
97
|
+
"primitive": "grid.items[1]",
|
|
98
|
+
"field": "content",
|
|
99
|
+
"error": "overflow",
|
|
100
|
+
"detail": "Text is ~14pt too tall for the box at font size 18pt (6 lines).",
|
|
101
|
+
"fix": "Shorten the text, reduce bullet/line count, or split into two slides."
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Primitive catalog
|
|
106
|
+
|
|
107
|
+
Every primitive accepts an optional `id` (needed if another primitive
|
|
108
|
+
references it, e.g. a connector) and an optional `notes` (speaker notes).
|
|
109
|
+
There is no separate "title slide" / "content slide" / "thank-you slide"
|
|
110
|
+
taxonomy — a slide is just `{header?, body: [...]}`, and genre/density/tone
|
|
111
|
+
decisions (what kind of slide this is, how much goes on it) are yours to
|
|
112
|
+
make by composing primitives, not a schema type to pick.
|
|
113
|
+
|
|
114
|
+
| Primitive | Key fields | Purpose |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `header` | `title`, `subtitle?`, `eyebrow?`, `align` | Slide title region. |
|
|
117
|
+
| `text` | `mode` (paragraph/bullets), `content`, `columns?`, `emphasis_indices?` | Prose or bullet list. |
|
|
118
|
+
| `image` | `src?`, `placeholder`, `caption?`, `fit` (cover/contain) | A real picture, or a first-class placeholder — see below. |
|
|
119
|
+
| `stat` | `value`, `label`, `trend?` | A headline number with a label. |
|
|
120
|
+
| `grid` | `items`, `columns`, `direction`, `align`, `justify` | The one primitive with true 2D layout. Items can be any primitive, including nested grids. |
|
|
121
|
+
| `table` | `headers`, `rows`, `emphasis_row?`, `emphasis_col?` | Renders as a real OOXML table (`p:graphicFrame`), not an image. |
|
|
122
|
+
| `sequence` | `steps` (`{label, description?}`), `orientation` | A row/column of connected step boxes — process/timeline diagrams. |
|
|
123
|
+
| `chart` | `chart_type` (bar/line/pie), `categories`, `series` | A real, editable native chart with live data — not a picture of a chart. |
|
|
124
|
+
| `shape` | `kind` (rect/rounded_rect/oval/line/arrow/connector), `fill`, `border`, `connects?`, `text?` | Freeform shape, optionally with text inside, or a connector between two other primitives by `id`. |
|
|
125
|
+
|
|
126
|
+
### Image placeholders
|
|
127
|
+
|
|
128
|
+
Set `"placeholder": true` (with an optional `caption`) instead of `src` when
|
|
129
|
+
you don't have a real image yet. It renders as an intentional design
|
|
130
|
+
element — dashed border, centered caption — and `render_deck`'s response
|
|
131
|
+
gets one manifest entry per placeholder:
|
|
132
|
+
`{slide, primitive, rect: {x, y, w, h}, caption}`. A later pass (image
|
|
133
|
+
search/generation/human upload) can fill each reserved rect directly from
|
|
134
|
+
the manifest EMU rect — no re-layout needed, and you don't need
|
|
135
|
+
image-generation capability just to build the deck.
|
|
136
|
+
|
|
137
|
+
## Worked examples
|
|
138
|
+
|
|
139
|
+
### 1. Title slide
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"slides": [
|
|
144
|
+
{ "header": { "title": "2026 Roadmap", "subtitle": "Platform team", "eyebrow": "Q1 Kickoff" } }
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 2. Two-column comparison with a connector
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"slides": [{
|
|
154
|
+
"header": { "title": "Before vs. After" },
|
|
155
|
+
"body": [
|
|
156
|
+
{
|
|
157
|
+
"primitive": "grid",
|
|
158
|
+
"columns": 2,
|
|
159
|
+
"items": [
|
|
160
|
+
{ "id": "before", "primitive": "shape", "kind": "rounded_rect", "fill": "#EF4444",
|
|
161
|
+
"text": { "content": "Manual layout" } },
|
|
162
|
+
{ "id": "after", "primitive": "shape", "kind": "rounded_rect", "fill": "#10B981",
|
|
163
|
+
"text": { "content": "Resolver-computed layout" } }
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
{ "primitive": "shape", "kind": "connector", "connects": { "from_id": "before", "to_id": "after" } }
|
|
167
|
+
]
|
|
168
|
+
}]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 3. Stat + table + chart dashboard
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"slides": [{
|
|
177
|
+
"header": { "title": "Q3 Metrics" },
|
|
178
|
+
"body": [
|
|
179
|
+
{ "primitive": "stat", "value": "42%", "label": "YoY growth", "trend": "+12% vs Q2" },
|
|
180
|
+
{ "primitive": "table", "headers": ["Quarter", "Revenue"], "rows": [["Q1", "10"], ["Q2", "14"]] },
|
|
181
|
+
{ "primitive": "chart", "chart_type": "bar", "categories": ["Q1", "Q2"],
|
|
182
|
+
"series": [{ "name": "Revenue", "values": [10, 14] }] }
|
|
183
|
+
]
|
|
184
|
+
}]
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 4. Process sequence
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"slides": [{
|
|
193
|
+
"header": { "title": "Our Process" },
|
|
194
|
+
"body": [{
|
|
195
|
+
"primitive": "sequence",
|
|
196
|
+
"orientation": "horizontal",
|
|
197
|
+
"steps": [
|
|
198
|
+
{ "label": "Discover", "description": "Understand the problem" },
|
|
199
|
+
{ "label": "Design", "description": "Sketch options" },
|
|
200
|
+
{ "label": "Ship", "description": "Release to users" }
|
|
201
|
+
]
|
|
202
|
+
}]
|
|
203
|
+
}]
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Fonts and overflow validation
|
|
208
|
+
|
|
209
|
+
Overflow checking reads real glyph advance widths via `fonttools` — no
|
|
210
|
+
rendering required. As of this release, no font is bundled yet; validation
|
|
211
|
+
falls back to a system font if one is found, and is skipped — not faked —
|
|
212
|
+
with a warning if none is available.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""MCP server exposing compono's render_deck/validate as MCP tools.
|
|
2
|
+
|
|
3
|
+
Every tool here is a thin proxy — no reimplemented logic. `spec` parameters
|
|
4
|
+
are typed as plain `dict`, not the `Deck` pydantic model, so malformed input
|
|
5
|
+
reaches compono.validate/render_deck itself and comes back as compono's own
|
|
6
|
+
structured {slide, primitive, field, error, detail, fix} shape, never MCP's
|
|
7
|
+
generic schema-rejection error (this is deliberate — see the project plan).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import importlib.resources
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from fastmcp import FastMCP
|
|
16
|
+
|
|
17
|
+
from compono import DeckValidationError, render_deck, validate
|
|
18
|
+
|
|
19
|
+
mcp = FastMCP("compono")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@mcp.tool()
|
|
23
|
+
def validate_deck(spec: dict[str, Any]) -> dict[str, Any]:
|
|
24
|
+
"""Validate a compono deck spec: schema + layout + text-overflow checks, no file write.
|
|
25
|
+
|
|
26
|
+
Cheap (millisecond-scale) — prefer this before render_deck when iterating.
|
|
27
|
+
Never raises: malformed input comes back as {valid: false, errors: [...]},
|
|
28
|
+
each error shaped {slide, primitive, field, error, detail, fix}.
|
|
29
|
+
"""
|
|
30
|
+
report = validate(spec)
|
|
31
|
+
return {"valid": report.valid, "errors": report.errors, "warnings": report.warnings}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@mcp.tool()
|
|
35
|
+
def render_deck_tool(spec: dict[str, Any], output_path: str) -> dict[str, Any]:
|
|
36
|
+
"""Render a compono deck spec to a real, editable .pptx file at output_path.
|
|
37
|
+
|
|
38
|
+
On success: {pptx_path, manifest, warnings}. On any validation/layout/
|
|
39
|
+
overflow error: {valid: false, errors: [...]} in the same shape as
|
|
40
|
+
validate_deck — nothing is written to output_path in that case.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
report = render_deck(spec, output_path)
|
|
44
|
+
except DeckValidationError as exc:
|
|
45
|
+
return {"valid": False, "errors": exc.errors}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
"pptx_path": str(report.pptx_path),
|
|
49
|
+
"manifest": report.manifest,
|
|
50
|
+
"warnings": report.warnings,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@mcp.resource("compono://reference")
|
|
55
|
+
def reference() -> str:
|
|
56
|
+
"""The full agent-facing compono reference: quickstart, primitive catalog,
|
|
57
|
+
worked examples, and error shape — for MCP clients without Claude Code's
|
|
58
|
+
skill system.
|
|
59
|
+
"""
|
|
60
|
+
return (
|
|
61
|
+
importlib.resources.files("compono_mcp")
|
|
62
|
+
.joinpath("reference.md")
|
|
63
|
+
.read_text(encoding="utf-8")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main() -> None:
|
|
68
|
+
mcp.run(transport="stdio")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
main()
|