Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.

## Unreleased

### A package ships its skills, so tool selection works on a clone

Tool selection narrows a Bot's tools to the ones its matching skills declare, and a deployment starts
with no skills at all. There was no `skills.yaml`, nothing seeded any, and nothing ever created one
— so on every fresh clone there was nothing to match against and the narrowing never switched on.
Left to a screen it would have stayed that way until somebody sat down and mapped tools to skills by
hand, in each deployment, again after each new connector.

A tenant package may now carry `skills.yaml`. Each skill has a slug, a title, a summary, its
instructions, and the `serverId/toolName` refs it needs. They are seeded on boot as deployment
skills, everybody sees them in the `/` menu, and connecting a connector is the only step left.

`skills.yaml` is optional, so every existing package loads unchanged and ships no skills. A package
may declare tools for a connector nobody has added: an unknown ref sits inert, because the offer is
still intersected with the Bot's grants. Naming a tool in a package grants nothing, exactly as
before.

A skill somebody wrote in the deployment keeps its name. If a package ships a slug a person already
took, theirs stands, the package loses that one, and the deployment starts — a name is not worth
refusing to boot over.

The example package ships four: `/find-a-document`, `/whats-changed`, `/who-owns-this` and
`/check-a-claim`.

## 0.0.3

### A Bot is offered the tools its message needs, not every tool it holds
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ Every MCP call checks the grant first, then evaluates the same action policy eng

A model picks the right tool reliably out of about ten, and unreliably out of thirty. A deployment that connects two vendors passes that point on its first afternoon, so a Bot holding more than a handful of tools is offered, per run, only the tools of the skills that match the message.

Skills come from two places: a person writes one, or the tenant package ships one in `skills.yaml`. Package skills are seeded on boot as deployment skills, carrying the tool refs they need, which is what lets narrowing work on a fresh clone instead of waiting for somebody to map tools to skills by hand. A slug a person already took stays theirs and the package loses that skill rather than the deployment refusing to start.

A skill declares the tools it needs (`skill_tools`). Before the run starts, the deployment asks its own model which skills the message needs, and the Bot is built with those skills' tools plus every granted tool no skill claims. A declaration grants nothing: the offer is always intersected with what the Bot was already granted, so writing a skill can never hand anybody a tool.

This narrows the offer. It is not a boundary, and it never substitutes for one. The grant, the policy and the audit row decide what may happen; this decides only what the model can see. Every way it can fail — no skills declared, a model that cannot answer, a message that matches nothing, twelve tools or fewer — leaves the whole catalogue offered, because a narrowing that failed closed would remove capability an administrator granted, silently. `mcp.tools_discovered` records what was offered, out of how much, and why.
Expand Down
29 changes: 27 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,15 +260,16 @@ Set `OPENBOT_ONE_COMPUTER_EACH=false` when using `start.sh` to run all Bots agai

## Tenant package

The tenant package contains five required YAML files:
The tenant package contains five required YAML files, and one optional:

```text
examples/fintech/
├── brand.yaml
├── agents.yaml
├── channels.yaml
├── model.yaml
└── knowledge.yaml
├── knowledge.yaml
└── skills.yaml (optional)
```

### `brand.yaml`
Expand Down Expand Up @@ -383,6 +384,30 @@ sources:

Supported source types are `google-drive` and `microsoft-onedrive`.

### `skills.yaml` (optional)

```yaml
skills:
- slug: find-a-document
title: Find a document
summary: Search the connected document sources for a file and read what it says.
instructions: >-
Search first, then read the file you found rather than answering from its title.
tools:
- google-drive/search_files
- google-drive/read_file_content
```

Each skill becomes a deployment skill on boot: everybody sees it in the `/` menu, and which Bots carry it is decided in Admin like any other.

`tools` is why this file matters beyond the instructions. A Bot holding more than twelve tools is offered, per run, only the tools of the skills that match the message, so the matching needs skills to match against. Shipping the declaration with the skill is what makes connecting a connector the only step; without it a deployment has no skills, nothing matches, and the narrowing never switches on.

Refs are `serverId/toolName`, the same form a grant is written in. A package may name tools for a connector nobody has added — the ref sits inert until that connector exists, because what a Bot is offered is always intersected with what it was granted. **Naming a tool here grants nothing.**

Slugs are lowercase letters, digits and hyphens. If a package ships a slug somebody in the deployment already wrote a skill under, theirs keeps the name, the package loses that skill, and startup continues.

Omit the file entirely for a package with no skills.

## Change workflow

1. Edit the relevant `.env` value or tenant YAML file.
Expand Down
68 changes: 68 additions & 0 deletions examples/fintech/skills.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Skills this deployment ships, and the tools each one needs.
#
# A skill is a named instruction anybody here can invoke with `/`. It adds no capability: it can only
# ask a Bot to use what that Bot was already granted, and every one of those calls is still decided,
# policy-checked and recorded.
#
# `tools:` is the second thing a skill is for, and the reason this file exists rather than a screen.
# A Bot holding more than a handful of tools is offered, per run, only the tools of the skills that
# match the message, because a model picks the right tool reliably out of about ten and unreliably
# out of thirty. That matching needs skills to match against, and a deployment starts with none — so
# left to a screen, the narrowing is switched off on every clone until somebody maps tools to skills
# by hand, in each deployment, again after each new connector. Shipping the declaration with the
# skill is what makes connecting a connector the only step.
#
# NAMING A TOOL HERE GRANTS NOBODY ANYTHING. What a Bot may call is its grants, and the offer is
# always intersected with them: a skill naming a tool its Bot does not hold loads nothing. So this
# file may name tools for connectors this deployment has not added, and should — that is what makes
# them work the moment somebody does.
#
# Refs are `<serverId>/<toolName>`, the same form a grant is written in. `google-drive` is the
# catalogue connector; a server an administrator adds by URL uses the id it was given.
skills:
- slug: find-a-document
title: Find a document
summary: Search the connected document sources for a file and read what it says.
instructions: >-
Find the document the person is asking about before answering anything about its contents.
Search first, then read the file you found rather than answering from its title. If the search
returns nothing, say so and say what you searched for, rather than guessing at what the
document might contain. Name the document you used.
tools:
- google-drive/search_files
- google-drive/read_file_content
- google-drive/get_file_metadata

- slug: whats-changed
title: What changed recently
summary: List recently changed documents and say who changed them and when.
instructions: >-
List what has changed recently in the sources you can reach, newest first. Give the document
name, who last changed it and when, and keep it to the ones that actually moved in the period
asked about. Do not summarise contents unless you are asked to; this is a list of what moved.
tools:
- google-drive/list_recent_files
- google-drive/get_file_metadata

- slug: who-owns-this
title: Who owns this
summary: Find who owns a document and when they last touched it.
instructions: >-
Identify the owner of the document being asked about, and say when it was last changed. Search
for the document first if you were given a name rather than a link. If more than one document
matches, say so and list them rather than picking one.
tools:
- google-drive/search_files
- google-drive/get_file_metadata

- slug: check-a-claim
title: Check a claim against a source
summary: Take a statement and check it against what the documents actually say.
instructions: >-
You are checking a claim, not answering a question. Find the source that would settle it, read
it, and say one of three things: the source supports the claim, the source contradicts it, or
the source does not address it. Quote the sentence you are relying on. Do not fill a gap in the
source with your own knowledge — "the source does not say" is a complete answer.
tools:
- google-drive/search_files
- google-drive/read_file_content
Loading