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

# MCP Overview

> Use Faces with Claude and other MCP-compatible AI assistants to build presentations slide by slide.

The Faces MCP (Model Context Protocol) server lets AI assistants like Claude build interactive, explorable decks slide by slide. Your assistant writes each slide's code directly, without using any credits.

## What is MCP?

MCP is an open protocol that lets AI assistants connect to external tools. When you connect Claude to the Faces MCP server, Claude can:

* **Create a blank project** to start a new presentation (in your personal team or any team you belong to)
* **List the teams** you belong to so the assistant can pick the right one
* **List and browse** your existing projects (including team-shared projects)
* **Read and edit individual slides**: inspect slide source files and write new ones. Works for any project where you are the owner, a team member (on team-shared projects), or an invited editor
* **Set the project's design system**: the palette and fonts shared across every slide
* **Attach plugins** to a slide, such as a contact form or a live AI video presenter
* **Screenshot a slide** to visually verify layout, spacing, and typography after editing
* **Publish or unpublish** a project and read its live URL

There is no server-side "generate a full presentation" tool exposed through MCP. The assistant itself authors every slide. To build a new deck, it creates a blank project and then fills in slides one at a time.

## Available tools

### Session & agent mode

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_user\_state</code></td><td>Read your live editor state across open browser tabs: the project you're viewing, the selected slide, and whether agent mode is on. The assistant calls this first to know which project you're working on.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>start\_agent\_mode</code></td><td>Turn on "agent mode" for a project in your browser: hides the right sidebar and shows a floating bar indicating an agent is driving the session. Pass <code>enabled: false</code> to turn it off. You can always deactivate it manually.</td></tr>
  </tbody>
</table>

### Teams

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>list\_teams</code></td><td>List the teams you belong to. Returns <code>id</code>, <code>slug</code>, <code>name</code>, <code>role</code>, and <code>isPersonal</code>. Use the team <code>id</code> when calling <code>create\_project</code>.</td></tr>
  </tbody>
</table>

### Projects

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>create\_project</code></td><td>Create a new blank project. Returns the <code>projectSlug</code> and <code>editorUrl</code>. Accepts an optional <code>teamId</code>; omit it to use your personal team, or pass an <code>id</code> from <code>list\_teams</code> to create the project under a specific team.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>list\_projects</code></td><td>List projects you can access (owned, team-shared, or shared with you) with pagination (<code>cursor</code>, <code>limit</code>, <code>published</code>).</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_project</code></td><td>Get project details and URLs for a given slug.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>rename\_project</code></td><td>Rename a project. The slug and URLs stay the same.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>duplicate\_project</code></td><td>Create a full copy of a project, including all slides. Returns a new <code>projectSlug</code> and <code>editorUrl</code>.</td></tr>
  </tbody>
</table>

### Design system

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_project\_config</code></td><td>Read <code>project.config.json</code>, including layout settings and the shared design system tokens (palette colors and fonts) used across every slide.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>update\_project\_config</code></td><td>Update <code>project.config.json</code>, for example to define the project's palette and fonts before styling slides. Only the keys you pass are changed.</td></tr>
  </tbody>
</table>

### Publishing

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>publish\_project</code></td><td>Publish a project's latest version, making it publicly accessible. Returns the <code>publishedUrl</code>.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>unpublish\_project</code></td><td>Take a published project offline. The project and its slides are kept.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_project\_url</code></td><td>Get a project's current live URL (subdomain or custom domain plus path) and the subdomains and domains available to switch to.</td></tr>
  </tbody>
</table>

<Note>
  Changing the live URL, adding subdomains, or connecting custom domains is done from the dashboard, the [CLI](/en/cli/commands), or the [REST API](/en/api-reference/publishing/update-url). These are not exposed as MCP tools.
</Note>

### Slides

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>list\_slides</code></td><td>List all slides in a project. Returns slide IDs and names.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_slide</code></td><td>Read a slide's source files (<code>face.tsx</code>, <code>face.content.json</code>, <code>face.controls.json</code>).</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>create\_slides</code></td><td>Create one or more empty slides in order in a single atomic call. Returns the new <code>slideId</code>s in the same order. Pass <code>editing: true</code> to show the loading gradient on every new slide immediately.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>reorder\_slides</code></td><td>Reorder every slide in a project in a single atomic call. Pass the full list of existing <code>slideId</code>s in the desired final order. Use this to restructure an existing deck; never delete and recreate slides just to reorder them.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>update\_slide</code></td><td>Update a slide's source files. Validates and compiles before saving; returns errors if the code doesn't compile.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>start\_slide\_editing</code></td><td>Show the loading gradient in the editor before making edits.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>finish\_slide\_editing</code></td><td>Clear the loading gradient after editing (always call, even on failure).</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>view\_slide</code></td><td>Take a screenshot of a slide and return it as an image so the assistant can visually verify layout, spacing, and typography. Pass <code>isMobile: true</code> for the mobile layout.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_slide\_guide</code></td><td>Get the full slide authoring reference (file formats, block types, controls, typography, examples). Call before writing slide code for the first time.</td></tr>
  </tbody>
</table>

### Plugins

<table>
  <colgroup>
    <col style={{ width: "300px" }} />

    <col />
  </colgroup>

  <thead>
    <tr><th>Tool</th><th>Description</th></tr>
  </thead>

  <tbody>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>list\_plugins</code></td><td>List the plugins available to attach to a slide: runtime behaviors a slide's code can't express on its own, such as collecting form submissions or embedding a live AI video presenter.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>get\_plugin\_docs</code></td><td>Fetch a plugin's config schema, the functions it exposes, and code examples showing how a slide calls it. Read this before <code>setup\_plugin</code>.</td></tr>
    <tr><td style={{ whiteSpace: "nowrap" }}><code>setup\_plugin</code></td><td>Configure a plugin instance on a slide. The config is validated against the plugin's schema and written to the slide's <code>face.plugins.json</code>; other instances are preserved.</td></tr>
  </tbody>
</table>

## Building a new presentation

1. `get_slide_guide`: read the authoring reference (first time only)
2. *(Optional)* `list_teams`: if the user might want the project in a specific team, list them and ask. Otherwise skip and the personal team will be used.
3. `create_project({ name, teamId? })`: returns `projectSlug` and `editorUrl`
4. *(Recommended)* `update_project_config({ projectSlug, config })`: define the shared design system (palette and fonts) before styling slides, so every slide stays consistent.
5. `create_slides({ projectSlug, slides: [{ name }, ...], editing: true })`: ONE call with every slide you plan to add, in order. Returns the new `slideId`s. Never call this in a loop or in parallel for the same project.
6. For each returned `slideId` (in parallel or sequentially):
   1. `update_slide({ projectSlug, slideId, files })`: writes `face.tsx`, `face.content.json`, `face.controls.json`
   2. `view_slide({ projectSlug, slideId })`: screenshot the slide to verify it renders as intended
   3. `finish_slide_editing({ projectSlug, slideId })`: clears the loading gradient
7. Share the `editorUrl` with the user.

## Editing an existing slide

1. *(Optional)* `get_user_state`: discover which project and slide the user currently has open
2. `get_slide_guide`: if you haven't read it yet
3. `start_slide_editing(slideId)`
4. `get_slide(slideId)`: read current files
5. `update_slide(slideId, files)`
6. `view_slide(slideId)`: verify the result visually
7. `finish_slide_editing(slideId)`

## Authentication

The MCP server uses **OAuth** for authentication. When you first connect, you'll be prompted to sign in to your Faces account through your browser. No API key management needed; the OAuth flow handles everything automatically.

Because MCP is authenticated per-account (not per-team), new projects default to your **personal team** unless the assistant passes an explicit `teamId` from `list_teams`. This differs from the REST API and CLI, which authenticate with team-scoped API keys and always create projects under the key's team. (For the CLI, the team is chosen at `faces login --team <slug-or-id>`; for REST API keys, at **Settings > Developers** in the dashboard.)

## Access control

The MCP tools respect the same permission model as the rest of Faces:

* **Project owner**: full read/write access.
* **Team-shared projects**: any member of the owning team can read and edit.
* **Invited collaborators**: can read; only collaborators with edit permission can update slides.

If the signed-in user does not have access to a project, the MCP tools return "Project not found or you don't have access".
