Repository navigation
Sharing Custom Tools
Here's one suggested way to publish Custom Tools for other people to use.
Keryx has no registry, marketplace or install-from-URL command. keryx tools only lists
and checks the files already in your tools directory. keryx plugins install knows only
Keryx's own four plugins. What follows is a convention that works with those commands as
they exist today. It isn't a feature.
A tool file is code that runs inside the Keryx service, at the start of every call.
It can reach everything the service can: your files, your signed-in agents, secrets.toml
and the network. The loader checks who can write the file. It can't check what the file
does. Installing someone's tool is like running their script as yourself, so:
- Read the file before you install it, and read it again for each update.
- Pin a commit. Install from a commit you've read, not from whatever a branch points to today.
-
Prefer tools that keep
needs_pin=True. Aneeds_pin=Falsetool answers anyone who calls your number. It should only read information that's public anyway, and its README should explain why.
A public git repository with one self-contained .py file per tool works well:
keryx-tools/
├── README.md
├── .gitignore # *.key, and any filled-in settings
├── tides.py
├── tides.toml # settings template: defaults only, a comment on every key
└── countdown.py
Keep each tool to one file. The loader reads only *.py files directly in the tools
directory and imports each as a separate module. A tool can't import a helper that sits
beside it, and a subdirectory is ignored. Use only the standard library and what Keryx
already depends on (httpx, numpy, pydantic). The owner's Keryx environment has
nothing else, and they shouldn't install packages into it for a tool. From Keryx, import
only custom_tool and ToolUnavailable from keryx.tools.custom. Nothing else in Keryx
is a stable interface.
For each tool, the README should say:
- What it does and what it reads. Include every URL it calls and every file it opens.
- Whether it needs the PIN, and if it doesn't, why that's safe.
-
What it needs from the owner: settings, and which keys to put in its
.toml. It should also name each secret and the file it belongs in, such astides.keywith mode 0600 beside the tool. -
Its tool name, so the owner can check it doesn't clash with a built-in tool, a
plugin or one of their own tools. A distinctive name, such as
harbor_tidesrather thantides, avoids that. When two files define the same name, the first file in alphabetical order wins and the second file is refused.
Never publish a secret, including in the .py file, the settings template, a test or
the history. A tool reads its key from a file the owner creates. .gitignore should exclude
*.key and any filled-in settings. A tool that's missing its key can raise
ToolUnavailable with a sentence that says where the key goes. keryx tools then
reports that sentence instead of a traceback.
git clone https://github.com/SOMEONE/keryx-tools ~/src/keryx-tools
cd ~/src/keryx-tools
git checkout <commit you have read>
install -m 600 tides.py tides.toml ~/.local/share/keryx/tools/
$EDITOR ~/.local/share/keryx/tools/tides.toml # fill in the settings
# any key goes in its own file, 0600, as the README says
cd <your Keryx checkout> && uv run keryx toolsKeep the clone outside the tools directory. Copy only the tool files, not the whole
repository. Every .py file in that directory is imported, including a setup.py or a
test file. Don't turn the tools directory into a git checkout. keryx tools should list
the new tool with the PIN setting you expected and exit 0. The tool is available from your
next call.
To update, git fetch, read the diff (git diff <old>..<new> -- tides.py), check out the
new commit and copy the file again. Your settings .toml and key files are already in the
tools directory, so don't copy the template over your filled-in settings.
A symlink loads because the loader follows it. It checks the file the link points to, which
must be yours and not writable by anyone else. It doesn't check the directory that file is
in. More importantly, a symlink into a clone means a git pull changes the code your next
call runs, with no step where you read the change. A copy makes each update a deliberate
step, as the approval bridge does for its hook. If you use a symlink anyway, point it at a
checkout that only you can write to, and never pull without reading.
A tool that almost everyone would want belongs in Keryx itself, as a plugin with tests.
Open an issue or a pull request;
CONTRIBUTING.md says how.
User documentation for Keryx.
For the threat model, see SECURITY.md;
for settings, the coding agents, and the tools, see docs/.
Apache-2.0, Copyright 2026 William Koch.
Setting up
Running it
Security and data
Tools and advanced features