> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mauvely.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The MCP server

> Run Compose as an MCP tool server so an AI agent can build a course start to finish — and take a real screenshot to check what it made.

Compose can run as an [MCP](https://modelcontextprotocol.io) server instead of opening a window,
which lets an AI agent author a course the same way a person does.

```bash theme={null}
MauvelyCompose --mcp
```

Register that command with any MCP client as a stdio server. No arguments, no configuration.

<Note>
  This is the real application, not a separate script. The agent's course and yours are the same
  format, produced by the same code — which is why an agent can hand you a `.course` file you then
  open and keep editing.
</Note>

## Setting it up

<Steps>
  <Step title="Find the binary">
    Wherever Compose is installed. On Linux that is inside the AppImage; on Windows, next to the
    installed `MauvelyCompose.exe`.
  </Step>

  <Step title="Add it to your client's config">
    ```json theme={null}
    {
      "mcpServers": {
        "mauvely-compose": {
          "command": "/path/to/MauvelyCompose",
          "args": ["--mcp"]
        }
      }
    }
    ```
  </Step>

  <Step title="Check the tools loaded">
    Your client should report **25 tools**. If it reports none, run the command by hand — anything
    it prints to stderr is the reason.
  </Step>
</Steps>

## What an agent can do

| Group        | Tools                                                                                                                               |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| Project      | `new_course` · `open_course` · `save_course` · `get_course` · `set_course`                                                          |
| Design       | `set_theme` · `list_fonts`                                                                                                          |
| Lessons      | `list_lessons` · `add_lesson` · `rename_lesson` · `remove_lesson` · `move_lesson`                                                   |
| Blocks       | `list_block_kinds` · `list_blocks` · `add_block` · `get_block` · `update_block` · `remove_block` · `move_block` · `duplicate_block` |
| Landing page | `set_overview` · `set_resources`                                                                                                    |
| Media        | `add_asset`                                                                                                                         |
| Output       | `export_course` · `preview_course`                                                                                                  |

Every block type is reachable, including quizzes, flashcards, branching scenarios and custom code.

## Writing a good prompt

Two things trip agents up, both worth saying explicitly in your instructions.

<AccordionGroup>
  <Accordion title="A new course is not empty">
    `new_course` gives you one lesson that **already contains a Welcome block** — the same thing you
    get opening the app, so a person has something to edit rather than a blank page.

    If you want exact control over what the course contains, tell the agent to pass `empty: true`,
    or ask it to remove the Welcome block. Otherwise "make a course with one block" produces two.
  </Accordion>

  <Accordion title="Newlines must be real newlines">
    Text fields take actual line breaks. An agent that sends the two characters `\` and `n` gets
    them rendered literally in the finished course, which looks like `\n\n` sitting in the middle
    of a paragraph.

    Smaller models get this wrong fairly often. If you see it, the fix is in the prompt rather than
    the course.
  </Accordion>

  <Accordion title="Point it at list_block_kinds first">
    Blocks carry every type's fields at once, so setting the wrong ones is not an error — it just
    produces an empty block. `list_block_kinds` returns each type's field list, and an agent that
    reads it first makes far fewer silent mistakes.
  </Accordion>
</AccordionGroup>

## Checking the result

`preview_course` renders the **real exported course** and returns a PNG. This matters: it is the
same runtime a learner gets, not a sketch of it, so an agent looking at the picture is looking at
what you would actually ship.

```
preview_course { "width": 1200, "height": 900, "path": "/tmp/check.png" }
```

Ask the agent to call it after building something and describe what it sees. It catches empty
blocks, broken media and theme choices that read badly far faster than reading the JSON does.

### On a server

Rendering needs a graphics backend. On a headless machine, run the whole thing under a virtual
framebuffer:

```bash theme={null}
xvfb-run -a --server-args="-screen 0 1400x1000x24" MauvelyCompose --mcp
```

Without one, every other tool still works — only `preview_course` returns an error, and it says
why.

<Note>
  The screenshot is rendered in a separate process. A browser engine that cannot find a graphics
  backend does not fail politely, it exits — and doing that inside the tool server would take your
  agent's unsaved work with it.
</Note>

## A worked prompt

> Using Mauvely Compose, make a course called "Onboarding" with exactly two lessons. Call
> `new_course` with `empty: true` so there is no starter content. Read `list_block_kinds` before
> adding anything. Lesson one gets a rich text introduction and a multiple-choice question with
> three answers; lesson two gets a flashcard set. Use real newlines in any text. Then call
> `preview_course` and tell me whether it looks right before saving to `~/onboarding.course`.

## Limits

<Card title="Worth knowing" icon="circle-info">
  * The server holds **one course at a time**. `new_course` and `open_course` discard what is in
    memory.
  * There is **no undo across tool calls** — an agent that removes the wrong block has to add it
    back.
  * Media has to exist on disk for `add_asset`; the agent cannot generate an image.
  * Nothing here talks to Mauvely Cloud. Courses are saved and exported locally.
</Card>
