Stickman Character MCP Server
by meAnkit18
README.md
# Stickman Character MCP Server
Turns your Red-Hat-engineer stickman reference art into a **posable, rigged
character** that any MCP-compatible coding agent (Claude Code, Claude
Desktop, Cursor, etc.) can control directly — set joint angles, pick a
facial expression, and render single frames or full animations (GIF/MP4).
Because every frame is generated from the same parametric SVG rig, the
character stays perfectly on-model across an entire video instead of
drifting the way frame-by-frame image generation does.
## Staying on-model
The rig is measured directly off the reference sheet rather than
approximated, so a render lines up with the source art almost pixel for pixel:
- **Proportions** were taken from the front-view reference (1024px source,
head outer radius 121px) and scaled by 0.546 onto the 400x520 canvas —
spine 215px, upper/fore arm 108/120, upper/lower leg 126/127, limb stroke
~19px, head centre 133px above the shoulder fork.
- **The fedora is a vector trace of the reference hat**, not a hand-built
approximation. It ships as normalised SVG paths (`HAT_PATHS` in `rig.py`)
expressed in head-radius units and drawn via
`translate(head) scale(head_radius)`, so it rides the head through any tilt
and keeps the exact brim sweep, crown pinch and band of the original.
- **Palette** is sampled from the art: `#D51918` bright red, `#A01513` mid,
`#7E1212` shadow, `#C2C3C5` face shading, `#D2D3D5` ground shadow.
- **The face matches the reference conventions** — the neutral face is two
solid black ovals and *no mouth*. White-eyeball-and-pupil eyes are used only
for happy/excited/surprised, and angry uses slanted wedges with gritted
teeth, exactly as in the reference expression sheets.
`hat_color` is still fully parametric: pass any hex and the mid and shadow
tones are derived from it using the reference's own 1.0 / 0.742 / 0.592 ramp.
## What's inside
```
stickman_mcp/
rig.py - the skeleton: joints, forward kinematics, SVG renderer,
traced fedora paths, facial expressions, pose presets
animate.py - keyframe interpolation + export to PNG sequence / GIF / MP4
server.py - the MCP server itself (tools below)
pyproject.toml
```
## Install
```bash
cd stickman_mcp
python3 -m venv venv
source venv/bin/activate
pip install -e .
```
This pulls in `mcp`, `cairosvg` (SVG→PNG rendering) and `pillow` (GIF
export). GIF/PNG-sequence export needs no extra system deps; **MP4 export
requires `ffmpeg`** to be installed and on your `PATH`.
## Register it as an MCP server
**Claude Desktop / Claude Code** — add to your MCP config
(`~/Library/Application Support/Claude/claude_desktop_config.json` on
macOS, or the equivalent Claude Code config):
```json
{
"mcpServers": {
"stickman": {
"command": "/absolute/path/to/stickman_mcp/venv/bin/python",
"args": ["-m", "stickman_mcp.server"],
"cwd": "/absolute/path/to/stickman_mcp"
}
}
}
```
Or run it standalone over stdio to test:
```bash
python -m stickman_mcp.server
```
## Tools exposed
| Tool | What it does |
|---|---|
| `list_pose_presets` | Lists built-in poses **and** their full definitions, so a pose can be copied and tweaked |
| `list_expressions` | Lists built-in facial expressions |
| `list_joints` | Lists every controllable joint and its default angle, for hand-authored poses |
| `render_pose` | Renders **one frame** — a preset name and/or explicit joint overrides, an expression, optional `hands`/`prop`. Returns a PNG (or raw SVG). |
| `render_animation` | Renders a full **keyframe animation** (poses + expressions + hold/transition timings) to GIF, MP4, or a PNG sequence. |
| `render_walk_cycle` | Convenience: N-step looping walk cycle. |
| `render_run_cycle` | Convenience: N-step looping run cycle. |
### Joints (all angles in degrees, 0 = straight down, + = clockwise)
`l_shoulder`, `l_elbow`, `r_shoulder`, `r_elbow`, `l_hip`, `l_knee`,
`r_hip`, `r_knee`, `neck` (head tilt), `spine` (torso lean),
`hip_shift` (px, horizontal), `body_lift` (px, vertical, negative = up).
`l_` is the character's left, i.e. the viewer's right. Mirrored poses need
opposite-signed knee angles (`l_knee=86`, `r_knee=-86`) so both shins bend
the same way.
### Style keys (per pose, not interpolated — they snap at the keyframe)
- `hands` — `"blob"` (default), `"open"`, or `"fist"`. Also accepts a dict for
per-side control: `{"r": "open"}` leaves the other hand a blob.
- `front_arm` — `"l"`, `"r"` or `"both"`, drawing that arm *over* the head.
Needed whenever a hand lands on the face (`facepalm`, `thinking_pose`),
since the hat brim is ~1.5x the head width and would otherwise cover it.
- `prop` — currently `"chair"`, drawn behind the character for `sit`.
### Poses
`idle`, `wave_right`, `wave_left`, `point_right`, `point_left`,
`explain_hands`, `explain_left_hand`, `thinking_pose`, `arms_up_happy`,
`arms_crossed`, `hands_on_hips`, `fists_up`, `walk_1`, `walk_2`, `run_1`,
`run_2`, `jump`, `crouch`, `sit`, `shrug`, `facepalm`, `bow`, `look_up`,
`look_down`, `celebrate`, `typing`.
### Expressions
`neutral`, `happy`, `excited`, `sad`, `angry`, `surprised`, `thinking`,
`wink`, `talking`, `calm`.
### Example: a coding agent building a short clip
```python
render_animation(
keyframes=[
{"pose": "idle", "expression": "neutral", "hold": 0.5},
{"pose": "wave_right", "expression": "happy", "hold": 1.0, "transition": 0.3},
{"pose": "explain_hands", "expression": "excited", "hold": 1.5, "transition": 0.3},
{"pose": "idle", "expression": "happy", "hold": 0.5, "transition": 0.3},
],
fps=12,
out_format="mp4",
)
```
An agent can also skip presets entirely and hand-author a pose:
```python
render_pose(
joints={"r_shoulder": -90, "r_elbow": 20, "neck": -10},
expression="excited",
)
```
Transitions use smoothstep easing, so poses ease in and out rather than
sliding linearly between keyframes.
## Notes / next steps
- The rig covers front-facing 2D poses only (matching most of the reference
set). A left/right-facing profile variant — for the side-profile and
walking-toward-camera references — is the natural next addition: the
skeleton already supports it, but the head, face and traced hat would each
need a profile variant.
- `sit` now draws a simple wooden chair via `prop="chair"`. Other props would
slot into `_chair_svg`'s pattern in `rig.py`.
- `render_animation` keyframes can be produced programmatically for a
"character walks across screen" shot: sweep `hip_shift` while alternating
`walk_1` / `walk_2`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues