Skip to main content
Glama
meAnkit18

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`.