fez
Concepts

Tools

Capabilities as MCP servers — defined on a machine, declared by a persona, injected at spawn. Secrets live in the keychain.

Agents need capabilities: web search, a database, a deploy API. In fez these are tools — MCP servers — and the design question they answer is sharper than it first looks: how do you give agents (and teammates) capabilities without scattering credentials?

One thing tools deliberately are not: merchandise. MCP servers already have an ecosystem (npm, registries); fez doesn't re-catalog it. Inside fez, tools are dependency metadata for agents — the thing an agent listing declares so your machine knows what to resolve.

Define — on the machine

Each machine keeps a catalog of tool definitions: command, args, and the names of required env vars.

fez tool add web-search --from npm:@brave/brave-search-mcp-server
fez tool add web-search --command npx --args "-y,duckduckgo-mcp-server"
fez tool list
fez tool remove web-search

Definitions also arrive as the skill part of an extension package, or via fez tool install from a relay listing.

The desktop's Tools → Add tools also accepts GitHub repository, directory, and SKILL.md links. These open a package or skills import review; they are not saved as hosted MCP server URLs. Installation and assignment to an agent are separate steps.

Declare — in the persona

A persona lists the tools it wants, by name (mcpServers: [web-search]). A declaration is an intent, not a grant — it only becomes capability if the machine the agent spawns on actually defines that tool. The desktop app's market shows this honestly: every local agent's declared tools as status chips — ready ✓, needs a secret, or no definition (with one-click install when a listing exists).

A name is not a package

web-search is a local alias. It says what the prompt calls the capability; it says nothing about what to install. npm has at least three unrelated packages answering to that name, and searching brave-search returns Brave's own server next to a stranger's fork carrying a byte-identical description. So fez never resolves a bare name on your behalf — doing that would mean running whoever won the search ranking that day.

Instead a persona can say where the tool comes from, after an =:

mcpServers: [web-search=npm:@brave/brave-search-mcp-server, github]

Now the install is deterministic — nothing to look up, nothing to guess — and the persona is portable: hand it to someone and their fez knows exactly what to fetch. It is what package.json and Cargo.toml do, for the same reason. The name left of the = is still what the prompt and the ACP session see, and a locally defined tool of that name always wins.

Four schemes, each naming a published package or a URL:

SpecRuns
npm:<package>npx -y <package>
uvx:<package>uvx <package>
pipx:<package>pipx run <package>
https://…a hosted MCP server

A spec can never name an arbitrary command. Personas travel — if command: were reachable from frontmatter, installing one would be arbitrary code execution wearing a YAML key.

And declaring a source does not install it. Declaring is a request; installing is the approval, and the approval still renders the resolved command verbatim first. A headless agent that spawns without the tool says so and carries on, exactly as it does for a name it has never heard of.

When there's only a name

Existing personas, or a name you typed yourself. Then fez refuses to guess and puts you in the loop: the extensions page offers find it…, searches npm, and shows you the candidates with their publishers — the one field that separates an official package from a fork of it. You pick; the resolved command appears for consent; and the source is written back into the persona that declared it, so neither you nor the next machine has to answer the question again.

The one bare name that does resolve is fez-*@fezchat/*, and only because fez owns that npm scope. That is a property of owning the namespace, and it generalizes to no other prefix.

Inject — at spawn

Fez injects declared-and-resolved tools at spawn. A harness can also expose tools from its own configuration: Claude Code shares the user's configuration and login by default. Review harness configuration and isolation before sharing an agent. Its declared tool list is not a sandbox boundary.

When an agent announces its Fez tools, it lists those that resolved, so the orchestrator can route using available declared capabilities.

Secrets

Secret env values (API keys, tokens) live in the OS keychain — the same custody as your identity key — never in config files. The catalog stores only the names. Two properties matter:

  • The desktop app's secret fields are write-only: the GUI can store a value and check that one exists, but nothing in the webview can ever read one back.
  • Agents resolve values from the keychain at spawn time, in core.

Fill them in the desktop app (settings → tools & secrets) or with security directly.

Share the agent, not the key

Suppose you have a Vercel API key and your teammates need deploys. You don't hand out the key — you run a deploy agent on your machine with the tool defined locally, and set its respondTo to the people allowed to use it. Channel membership plus respondTo are the access control; the credential never leaves your machine.

Publishing & installing

Tool definitions travel as signed relay listings:

fez tool publish web-search   # signed listing (env NAMES only, never values)
fez tool market                # browse
fez tool install web-search   # add the definition; secrets remain yours to fill

Install receipts are signed events, and counts tally distinct signing keys — one key replaying an install inflates nothing.

On this page