galet-tools
Self-contained generic tool handlers and the handler framework they run on.
Lineage
This package is extracted from the Lucy monorepo (src/handlers) as described
in the Lucy design doc software/ai/lucy/design/handlers-extraction.md and
the plan software/ai/lucy/plan/handlers-extraction.md.
It is a fresh repository with no shared git history. Handler logic moves
verbatim; the only intentional changes are the import namespace
(src.handlers.* -> galet_tools.*) and the configuration boundary: tools
depend on narrow port interfaces owned by this package (ConfigProvider,
StorageLocationResolver, SandboxRootResolver, SecurityPolicy, ...),
never on Lucy's ConfigManager. Lucy implements those ports at its
composition root.
Features
- Handler framework —
HandlerV2ABC,HandlerRegistry, schema handling, and permission filtering. - Generic tools —
file_load,file_save,patch_apply,execute_command,execute_command2,generate_svg, andgenerate_image, registered under their existing names.file_loadsupports bounded line-range reads. - Ports — sibling-owned interfaces the host implements, keeping the tools free of any host dependency.
- Handler discovery — installed packages can contribute handlers through Python package entry points without changing Lucy or galet-tools.
Quick start
python -m venv .venv
.venv/bin/pip install -e .
Run the suite:
python -m pytest -q
Using the registry in a host
Galet owns the handler contract and base registry. A host such as Lucy composes the generic handlers, its own host-specific handlers, and installed extension packages:
from galet_tools import HandlerRegistry, register_installed_handlers
from galet_tools.register_generic_defaults import register_generic_defaults
registry = HandlerRegistry()
register_generic_defaults(registry)
register_lucy_handlers(registry)
register_installed_handlers(registry)
Discovery adds installed capabilities to the registry; it does not grant them to an agent. The host should continue to apply its agent and context allowlists when selecting tool definitions.
Creating a handler extension
An extension package declares a registrar in its pyproject.toml:
[project.entry-points."galet_tools.handlers"]
web = "galet_web_tools:register"
The target is a callable that receives the shared registry:
def register(registry):
registry.register(SearchHandler)
registry.register(FetchHandler)
Entry points are loaded in deterministic name order. A plugin that cannot be
loaded, does not resolve to a callable, or fails during registration raises
HandlerPluginError with the responsible entry-point name. This is
intentionally fail-fast so a host cannot start with an unexpectedly incomplete
tool set.
Memory tools example
The episodic_memory handler takes an injected episodic store, and
curate_chat takes an injected curation service, and reset_session takes
the same episodic store. semantic_memory takes an injected
galet_memory.semantic.SemanticMemory. These tools are intentionally
not registered as a generic MCP default because the host must supply the
episodic store, digest generator, and trusted account.
Run a small SQLite session through creation, event append and read, digest
preview, archive, an active-session read, and an idempotent
archive retry, and a reset that clears events while retaining the session
metadata. It then writes and indexes the archived digest and recalls it
through VectorSemanticMemory in the digests namespace, using deterministic
embedding and index ports without an API key:
python -m pip install -e '.[memory]'
python examples/curate_chat.py
If galet-memory 0.1.5 is only available from a sibling checkout, install
that checkout first with python -m pip install -e ../galet-memory.
The example uses a deterministic digest generator and runs all four tool adapters
with one temporary session. The example explicitly writes the digest text,
embeds it, and inserts a StoredEmbedding into an in-memory index; curation
does not automatically publish to semantic memory. It checks that recall is
empty before publication and returns the digest afterward. In an application, inject
galet_memory.curation.CurationService with the application's digest generator
and construct CurateChatHandler(service) through a registry factory.
MCP server
An optional MCP (Model Context Protocol) server exposes the generic tools over streamable HTTP. Install the extra:
.venv/bin/pip install -e '.[mcp]'
Copy the example config and start the server:
cp galet-mcp.example.json galet-mcp.json
galet-tools-mcp
galet-tools-mcp reads galet-mcp.json from the current directory by
default; point it elsewhere with --config <path>.
Configuration
galet-mcp.json mirrors galet-mcp.example.json. Required keys are
account, storage_root, storage_namespace, and sandbox_root; a
null, blank, or (for the two path keys) relative value fails validation.
Optional keys left out or set to null fall back to their defaults:
allowlist— tools to expose; the default includesfile_load,file_save,patch_apply, legacyexecute_command, structuredexecute_command2,generate_svg, andgenerate_image.external_roots— named external roots the tools may access; default none.host— default127.0.0.1; loopback only.port— default8765.fonts— font paths for the generators; default none.
What it exposes
tools/list and tools/call over streamable HTTP at
http://127.0.0.1:8765/mcp.
Smoke-test from the CLI
galet-tools-smoke is the end-to-end CLI client: it connects over HTTP
(streamable HTTP) to a running galet MCP server using the official MCP SDK
and performs real tools/list and tools/call round trips.
galet-tools-smoke list
galet-tools-smoke list --json
galet-tools-smoke call <tool> '<json args>'
The default URL is http://127.0.0.1:8765/mcp; override it with --url.
The default read timeout is 30s; override it with --timeout.
Example round trip (storage root from galet-mcp.json):
galet-tools-smoke call file_save '{"path":"smoke/hello.txt","file_content":"hello galet smoke"}'
galet-tools-smoke call file_load '{"path":"smoke/hello.txt"}'
galet-tools-smoke call execute_command2 '{"mode":"process","executable":"python","arguments":["-c","print(123)"],"script":"","shell":"none","location":"sandbox","external_root":"","working_directory":".","timeout_seconds":30,"success_exit_codes":[0]}'
Exit codes: 0 ok; 1 tool reported an error (isError); 2 usage or
bad JSON; 3 could not reach the server.
Fail-closed behaviour
The server refuses to start when the allowlist is empty or has no overlap with the registered tools, and only the advertised tools are callable. Unknown tool names in the allowlist fail validation.
Security
Binding is loopback-only by design: host must resolve to a loopback
address. Remote access needs a separate reverse-proxy design.
License
MIT — see LICENSE.
Release files for galet-tools 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| galet_tools-0.1.7.tar.gz | 74.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| galet_tools-0.1.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 142.1 kB
Release files / galet_tools-0.1.7.tar.gz
| Download URL | galet_tools-0.1.7.tar.gz |
|---|---|
| Size | 74.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
545b466fe8e34bd9a7dea553077401df810de34195b2fd5e3f6d320cbf0401c0
|
|
BLAKE2b-256 checksum How to use checksums |
569bfc76b2349437682476a58bec79d21a988fd529dfc7f56c41b20136af907d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / galet_tools-0.1.7-py3-none-any.whl
| Download URL | galet_tools-0.1.7-py3-none-any.whl |
|---|---|
| Size | 68.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
37b7bb6f2a178b593a02fa5ab59a9f6beb7a626bf0421cfeebb26f8ca2e364ce
|
|
BLAKE2b-256 checksum How to use checksums |
cc0cdfcfa324b053b68cd979613435d5f453489bf0fbe233b8eb084db21aeff3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|