# Skills, MCP and plugins

Source: https://heyparlour.app/docs/skills

> Teach the house its rules in markdown, add tools over MCP, or bundle both in a plugin.

The house already knows how to call tools. What it does not know is that
goodnight means the porch light stays on and the rest go off, or that the car
should only charge after eleven. Those are house rules. They change whenever
someone rearranges a room, so writing one should not mean writing a provider.

There are three ways to add them, in the order you are likely to want them.
A **skill** is a markdown file. An **MCP server** is somebody else's tools.
A **plugin** is one package that brings several of these at once.

## Skills

A skill is a markdown file in `~/.config/parlour/skills`:

```md
---
name: bedtime
description: What goodnight means in this house
---

Turn off the kitchen, hall and lounge lights, leave the porch light on,
and set the thermostat to 17. Say "goodnight" and nothing else.
```

```sh
parlour skills new bedtime          # writes the file, with the frontmatter filled in
parlour skills list                 # the rules, and what each one is for
parlour skills show bedtime         # the body, as the model reads it
parlour skills path                 # where they live
parlour skills write bedtime < f.md # replace it, checked before it is written
parlour skills remove bedtime       # delete one of the house's own
parlour restart                     # the agent reads them at startup
```

Only the name and description go in the system prompt. The model fetches the
body with the `read_skill` tool when it decides the rule applies. A local
model with a small context cannot carry every house rule on every turn, and
most turns are "turn the kitchen light off". Write the description as the
trigger ("what goodnight means"), not as a title.

The frontmatter is optional. Without it, the filename is the name and the
first line is the description, so a one-paragraph rule can be just that
paragraph. A name is lower case letters, digits, `-` and `_`, and when two
skills share one, the first found wins. A skill with files of its own can be
a directory instead, with the rule in `SKILL.md`:

```text
~/.config/parlour/skills/
  bedtime.md
  shopping/
    SKILL.md
    list.txt
```

Write the body the way you would brief a house sitter: which tools to call,
in what order, and how short the spoken reply should be. `parlour doctor`
counts the skills it found, names any file it could not use and why, and
warns when a body (over 4,000 characters) is long enough to slow every
answer that uses it.

To keep your rules somewhere else, such as a git repository, point
`config.json` at that directory with `"skills": { "dir": "/house/rules" }`.
`"skills": { "enabled": false }` turns skills off entirely, tool and all.

## MCP servers

Anything that speaks [MCP](https://modelcontextprotocol.io) can give the
house new tools. Every tool it advertises becomes one the house can call:

```sh
parlour mcp add weather --url https://weather.example/mcp
parlour mcp add notes --token-env NOTES_TOKEN -- npx -y @someone/notes-mcp
parlour mcp list
parlour mcp remove weather
```

The first adds a server on the network, sending the `--token-env` variable,
if you give one, as a bearer token. The second adds one this machine starts
and talks to over stdin. Each tool arrives named after its server, such as
`notes_search`, which is the name to use in a skill. Either way, the entry
lands under `integrations.mcp.servers` in `config.json`, and you can write it
there by hand instead:

```json
{
  "integrations": {
    "mcp": {
      "servers": {
        "notes": {
          "transport": "stdio",
          "command": "npx",
          "args": ["-y", "@someone/notes-mcp"],
          "tokenEnv": "NOTES_TOKEN"
        }
      }
    }
  }
}
```

A stdio server gets `PATH`, `HOME`, `LANG` and `TMPDIR`, the one variable
`tokenEnv` names, and its own `env` block. It does not inherit the rest of
the environment. Every secret in `secrets.env` lives there, and a server that
wants the calendar has no business reading the Anthropic key. Store the token
with `parlour secrets set NOTES_TOKEN` and name it with `tokenEnv`, rather
than writing it into `config.json`.

If a server will not start, `parlour doctor` reports it and the rest of the
house keeps working. A dead weather server should never stop the lights going
off. A remote server that wants you to sign in with a browser is a connector,
not an MCP entry. `parlour connectors add <name> <url>` does the OAuth and
keeps the tokens in the Keychain, or in a file on a machine without one.

## Plugins

You can add everything above one piece at a time, and for a single piece
that is simpler. A plugin is for pieces that only make sense together: a
package for a car, say, with a provider, two skills that know its tool names,
and the MCP server both of them talk to.

```sh
npm install -g parlour-plugin-car   # where parlour can resolve it
parlour plugins add parlour-plugin-car
parlour plugins list
parlour restart
parlour plugins remove parlour-plugin-car   # takes it out of config
```

`parlour plugins add` loads the package before it writes the name into
config. A missing package is caught then, not at the next start with the
microphone live. An absolute path works too, so you can try a plugin from a
checkout.

A plugin is an npm package whose default export is a `Plugin`:

```ts
import { definePlugin } from "parlour";
import { join } from "node:path";
import { carVoice, carTools } from "./providers.js";

export default definePlugin({
  name: "car",
  description: "The car: charging, climate and where it is",

  /** Registered before any slot is filled, so config can name them. */
  providers: [carVoice, carTools],

  /** A directory of markdown skill files, shipped with the package. */
  skillsDir: join(import.meta.dirname, "skills"),

  /** Or skills written in code, the same shape as a file. */
  skills: [
    { name: "charging", description: "When to charge", body: "Only after 23:00.", source: "car" },
  ],

  /** Config for the integrations it needs, merged under the house's own. */
  integrations: {
    "car-tools": {},
    mcp: { servers: { car: { transport: "stdio", command: "car-mcp" } } },
  },

  /** Anything it wants to do once, before the agent is built. */
  async setup(context) {
    context.log.info(`using ${context.paths.home}`);
  },
});
```

Plugins load first, before any provider is resolved. So by the time a slot
in `config.json` names `car-voice`, it is already registered. The
`integrations` block is merged *underneath* your own config, key by key. A
plugin can bring an MCP server, and you can still change or remove it in
`config.json`. For the same reason, a plugin skill loses to one of the same
name in your own directory.

A plugin that cannot be loaded is fatal, not skipped. A house missing the
plugin that holds its skills would look just like one whose model has
stopped listening. The one exception is a provider
whose name is already registered: the registered one wins, and
`parlour doctor` says so.

`parlour plugins list` shows what each plugin brings without setting any of
them up, so checking what is installed never opens a connection.
