scuffed-painter
# Scuffed Painter
An MCP (Model Context Protocol) server that renders drawing
commands into raster images using Pillow. It exposes two tools:
- `draw_image` — render the commands and return the result as an inline PNG
image.
- `draw_image_to_file` — render the commands and save the result to a file.
Five shapes are supported, each a JSON object discriminated by its `"shape"`
field:
| shape | parameters |
|------------|-------------------------------------------------------|
| `line` | `start`, `end`, `color`, `width` |
| `rect` | `box` (left, top, right, bottom), `fill`, `outline`, `width` |
| `circle` | `center`, `radius`, `fill`, `outline`, `width` |
| `ellipse` | `box` (x0, y0, x1, y1), `fill`, `outline`, `width` |
| `triangle` | `points` (exactly three vertices), `fill`, `outline`, `width` |
Commands run in list order on a fixed-size white canvas; later commands
overpaint earlier ones. Colors are CSS color names or hex codes.
## Installation
```sh
pip install .
```
This provides the `scuffed-painter` console script, which launches the MCP
server over stdio (the default). `python -m scuffed_painter` is an equivalent
alternative. The same command can also serve the server over HTTP
(MCP Streamable HTTP transport):
```sh
scuffed-painter # stdio transport (default)
scuffed-painter --transport http # HTTP on 127.0.0.1:8000
scuffed-painter --transport http --host H --port P
```
`--host` and `--port` only apply to the HTTP transport; the defaults are
`127.0.0.1` and `8000`. You may enable CORS with `--enable-cors` if necessary.
## MCP host configuration
### stdio
Add the server to your MCP host's stdio server configuration:
```json
{
"mcpServers": {
"scuffed-painter": {
"command": "scuffed-painter"
}
}
}
```
Note: on a host with a restricted PATH or when running inside a virtualenv,
use the absolute path to the installed `scuffed-painter` script instead (e.g.
the one reported by `which scuffed-painter`).
### Streamable HTTP
Start the server yourself, then point your MCP host at the endpoint
(the path is `/mcp`):
```sh
scuffed-painter --transport http --host 127.0.0.1 --port 8000
```
```json
{
"mcpServers": {
"scuffed-painter": {
"type": "streamable-http",
"url": "http://127.0.0.1:8000/mcp"
}
}
}
```
(Adjust the key names to match your MCP host's configuration format.)
Note: when `draw_image_to_file` is used, `output_path` refers to the machine
the server runs on. Over stdio that is the same host as the MCP client; when
the server is reached over HTTP, it is the server's host instead.
### Example tool call
```json
{
"width": 200,
"height": 200,
"commands": [
{"shape": "circle", "center": [100, 80], "radius": 50, "fill": "gold", "outline": "orange", "width": 3},
{"shape": "triangle", "points": [[50, 170], [150, 170], [100, 70]], "fill": "darkgreen"},
{"shape": "line", "start": [20, 185], "end": [180, 185], "color": "black", "width": 2}
]
}
```
## Development
Repository layout:
```
scuffed_painter/ the package
server.py server, tools, command models, rendering
__init__.py re-exports mcp, defines __version__
__main__.py python -m scuffed_painter support
py.typed typing marker
tests/ pytest suite (in-process fastmcp.Client)
```
Setup and running the tests:
```sh
pip install -e ".[dev]"
python -m pytest
```
TDQS
Scored across 2 tools
The two tools split cleanly by output destination: draw_image_to_file persists to disk, while draw_image returns inline PNG content. Their names and the presence of output_path only in the file variant make misselection unlikely.
Both tools follow the same draw_image verb-object prefix, with the file-save variant adding the clear _to_file suffix. The naming is snake_case and consistent across the whole set.
Two tools is slightly below the typical 3-15 range, but the count is reasonable for a narrow painter: one output mode is file saving and the other is inline PNG. It feels minimal rather than excessive.
The core draw-and-output workflow is fully covered: all five shapes, validation, coordinate handling, and both file and inline delivery are present. Minor gaps remain, such as only having a fixed white background and no text or image-import tools, though these are workaround-able or outside the narrow scope.