# https://github.com/Oaklight/toolregistry-hub Project Manual

Generated at: 2026-06-25 23:43:06 UTC

## Table of Contents

- [Overview and Architecture](#page-1)
- [Core Tools Library](#page-2)
- [Server, CLI, and Configuration](#page-3)
- [Deployment, Distribution, and Operations](#page-4)

<a id='page-1'></a>

## Overview and Architecture

### Related Pages

Related topics: [Core Tools Library](#page-2), [Server, CLI, and Configuration](#page-3), [Deployment, Distribution, and Operations](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)
- [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)
- [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)
- [src/toolregistry_hub/server/banner.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/banner.py)
- [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)
- [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)
- [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py)
- [src/toolregistry_hub/websearch/base.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/base.py)
- [src/toolregistry_hub/websearch/websearch_tavily.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_tavily.py)
- [src/toolregistry_hub/websearch/websearch_scrapeless.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_scrapeless.py)
- [src/toolregistry_hub/websearch/google_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/google_parser.py)
- [src/toolregistry_hub/_vendor/readability/readability.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/readability/readability.py)
- [src/toolregistry_hub/_vendor/soup/soup.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/soup/soup.py)
- [src/toolregistry_hub/_vendor/httpclient.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/httpclient.py)
- [src/toolregistry_hub/_vendor/validate.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/validate.py)
</details>

# Overview and Architecture

## Purpose and Scope

`toolregistry-hub` is a curated collection of commonly used, ready-to-register tools for the [ToolRegistry](https://github.com/Oaklight/ToolRegistry) ecosystem. Rather than being a single tool, it is a "hub" that bundles calculators, datetime helpers, file-system utilities, web search and web fetch tools, unit converters, and a small set of auxiliary helpers into one installable package. The README describes the project as a hub providing "utility tools designed to support common tasks" and groups them into categories such as *Calculator Tools*, *Date Time Tools*, *File Operations Tools*, *File System Tools*, *Web Search Tools*, *Unit Converter Tools*, *Other Tools*, *Server Mode*, and *Docker Deployment* [Source: [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)].

The top-level package surface is intentionally flat so that consumers can import any tool directly. `__init__.py` re-exports `BashTool`, `BaseCalculator`, `Calculator`, `DateTime`, `FileOps`, `FileReader`, `FileSearch`, `PathInfo`, `ThinkTool`, `UnitConverter`, `Fetch`, `FetchError`, `SearchResult`, `BraveSearch`, `SearXNGSearch`, `SerperSearch`, `TavilySearch`, and `CronTool` [Source: [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)]. The docstring in the same file shows the intended usage pattern: instantiate a tool, then call methods on the instance and feed the result to a model that understands the registered tool surface.

Two extra delivery channels sit on top of the library API: a server mode that exposes registered tools over OpenAPI and MCP transports, and a Docker image for self-hosted deployment. The conda-forge submission tracked in [#151](https://github.com/Oaklight/toolregistry-hub/issues/151) shows the project is also moving toward wider distribution beyond PyPI.

## Module Layout and Architectural Layers

The package is organized into three concentric layers — vendored zero-dependency primitives, tool implementations, and the server façade.

```mermaid
graph TD
    A[User / LLM Agent] --> B[toolregistry_hub package]
    B --> C[Server Layer<br/>server/cli.py, server/app.py]
    C --> D[ToolRegistry / FastAPI / MCP]
    B --> E[Tool Layer<br/>calculator, file_ops, fetch, websearch/*]
    E --> F[Utils<br/>configurable, api_key_parser, annotation_helpers]
    E --> G[Vendored zerodep<br/>readability, soup, httpclient, validate, structlog]
    C --> H[External API Providers<br/>Tavily, Scrapeless, Brave, SearXNG, Serper]
    E --> H
```

**Vendored zero-dependency layer.** The `_vendor/` tree inlines small libraries that have no runtime dependencies of their own, so Hub can ship a self-contained extraction pipeline. `readability.py` is a vendored port of Mozilla's Readability algorithm that exposes `extract(html, url=None) -> ReadabilityResult` and `is_probably_readable(html, min_score=20.0, min_content_length=140)` [Source: [src/toolregistry_hub/_vendor/readability/readability.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/readability/readability.py)]. The parser relies on a vendored HTML parser declared in `soup/soup.py`, which provides `Soup`, `Tag`, and `SELF_CLOSING_TAGS` and stores the `class` attribute as a list while keeping other attributes as strings [Source: [src/toolregistry_hub/_vendor/soup/soup.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/soup/soup.py)]. Network I/O is implemented in a tiny HTTP client exposed through `_vendor/httpclient.py`, where `StreamingResponse` is constructed via classmethods `_from_sync` / `_from_async` and uses `zlib._Decompress` for content decoding [Source: [src/toolregistry_hub/_vendor/httpclient.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/httpclient.py)]. A companion `_vendor/validate.py` offers `json_schema(tp, *, title=None)` for emitting JSON Schema fragments from type annotations [Source: [src/toolregistry_hub/_vendor/validate.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/validate.py)].

**Tool layer.** Domain tools live as sibling modules to `__init__.py` and are wired together by the `utils/` package. The `Configurable` runtime-checkable protocol defines a single hook `_is_configured() -> bool` that the registry builder uses to auto-disable a tool when its required API keys or URLs are missing [Source: [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)]. `APIKeyParser` is a generic parser that accepts a comma-separated key string, an environment variable name, or a token file, and exposes round-robin selection with configurable `rate_limit_delay` [Source: [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)]. `bind_literal` rewrites a function's `__annotations__` so that a parameter with a runtime-known value set is exposed as `Literal[...]` in the generated JSON schema, which is how search-engine names and unit-conversion function names become enums in MCP/OpenAPI listings [Source: [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry_hub/utils/annotation_helpers.py)].

**Web search and fetch subsystem.** Web search providers share a common `BaseSearch` defined in `websearch/base.py`, which encapsulates result retrieval and a `_retrieve_webpage_content` helper that delegates to `Fetch` and falls back to a placeholder string on failure [Source: [src/toolregistry_hub/websearch/base.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/base.py)]. Provider-specific modules build on top: `TavilySearch` is gated by the `TAVILY_API_KEY` environment variable through the `requires_env` decorator and uses the vendored `Client` and `Response` types [Source: [src/toolregistry_hub/websearch/websearch_tavily.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_tavily.py)], and `ScrapelessSearch` similarly routes through the DeepSERP API using the vendored HTTP client [Source: [src/toolregistry_hub/websearch/websearch_scrapeless.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_scrapeless.py)]. A provider-agnostic `GoogleResultParser` unifies the JSON shapes returned by Bright Data and Scrapeless, parameterised by a `GoogleAPIConfig` dataclass that lists candidate `url_keys` and `description_keys` in priority order [Source: [src/toolregistry_hub/websearch/google_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/google_parser.py)].

## Server Architecture and CLI

The server surface re-uses the standalone `toolregistry-server` package introduced in v0.7.0 and further refactored in v0.9.1 ([#148](https://github.com/Oaklight/toolregistry-hub/issues/148)). `HubCLI` subclasses `toolregistry_server.cli.CLI` and injects a `HubApp` so that the upstream banner, version checking, and dispatch logic can be reused; Hub only contributes the banner text, the `tools_config_path` and `enable_discovery` arguments, and the dispatch into `HubApp` [Source: [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)]. The ASCII banner itself is defined in `server/banner.py` as a module-level constant `BANNER_ART` [Source: [src/toolregistry_hub/server/banner.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/banner.py)].

This thin-extension pattern keeps Hub focused on tool *content* while inheriting OpenAPI/MCP adapter behaviour, authentication, and `registry_builder` plumbing from `toolregistry-server`. Open issue [#144](https://github.com/Oaklight/toolregistry-hub/issues/144) describes the current limitation that `tools.jsonc` is consumed once at startup, motivating a future hot-reload layer that all three service modes (OpenAPI, MCP streamable-http, MCP SSE) can subscribe to.

## Configuration and Extensibility

Configuration flows from three sources: environment variables (e.g. `TAVILY_API_KEY`, `SCRAPELESS_API_KEY`), the built-in `tools.yaml` manifest that ships with the package and carries namespace/tag metadata for profile filtering, and a user-supplied `tools.jsonc` consumed by `build_registry()` at startup [Source: [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)]. The `Configurable` protocol and `APIKeyParser` together let tools self-report readiness and the registry transparently skip unconfigured ones, so a partial install — for example, a Hub deployment with only `TAVILY_API_KEY` set — still boots cleanly with whichever providers are usable.

## See Also

- [ToolRegistry core](https://github.com/Oaklight/ToolRegistry) — upstream registry and adapter framework that Hub builds on.
- [toolregistry-server](https://github.com/Oaklight/toolregistry-server) — standalone package hosting the App class, Adapter ABC, and CLI reused by Hub.
- Project README — installation, quick-start, and changelog notes for the most recent releases (v0.5.5 through v0.9.1).
- Issue [#148](https://github.com/Oaklight/toolregistry-hub/issues/148) — adopted the toolregistry-server 0.4.0 architecture in v0.9.1.
- Issue [#144](https://github.com/Oaklight/toolregistry-hub/issues/144) — proposed hot-reload of `tools.jsonc` across all service modes.

---

<a id='page-2'></a>

## Core Tools Library

### Related Pages

Related topics: [Overview and Architecture](#page-1), [Server, CLI, and Configuration](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)
- [src/toolregistry_hub/server/registry.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/registry.py)
- [src/toolregistry_hub/server/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/__init__.py)
- [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)
- [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)
- [src/toolregistry_hub/utils/requirements.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/requirements.py)
- [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py)
- [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)
- [src/toolregistry_hub/websearch/google_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/google_parser.py)
- [src/toolregistry_hub/websearch/websearch_scrapeless.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_scrapeless.py)
- [src/toolregistry_hub/websearch/websearch_tavily.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_tavily.py)
- [src/toolregistry_hub/_vendor/readability/readability.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/_vendor/readability/readability.py)
- [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)
</details>

# Core Tools Library

## Overview

The **Core Tools Library** is the in-process tool surface that ships with `toolregistry-hub` and is registered into a `toolregistry.ToolRegistry` so that LLM/MCP/OpenAPI clients can call common utilities (arithmetic, date/time, file I/O, web search, unit conversion, etc.) without users having to hand-author each tool. The package's top-level `__init__.py` explicitly bundles the tools as importable classes — `Calculator`, `BaseCalculator`, `DateTime`, `FileOps`, `FileReader`, `FileSearch`, `PathInfo`, `ThinkTool`, `BashTool`, `CronTool`, `TodoList`, `UnitConverter`, and the web tools `Fetch`, `BraveSearch`, `SearXNGSearch`, `SerperSearch`, `TavilySearch` — and exposes a `FetchError` exception. The module docstring demonstrates the typical usage pattern: import a class, instantiate it, and call the methods that the registry will later expose as JSON-schema functions. Source: [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)

The library is intentionally **content-only** with respect to server infrastructure. As stated in `server/__init__.py`, server adapters, routing, and authentication live in the upstream `toolregistry-server` package; Hub only contributes the tool catalog and Hub-specific registry/CLI glue. This separation was formalized in v0.7.0 (server code migrated to `toolregistry-server`, see [Oaklight/toolregistry-hub#56](https://github.com/Oaklight/toolregistry-hub/issues/56)) and further realigned in v0.9.1, when Hub adopted the new `toolregistry-server` 0.4.0 `App` / `Adapter` / `registry_builder` architecture ([Oaklight/toolregistry-hub#148](https://github.com/Oaklight/toolregistry-hub/issues/148)). Source: [src/toolregistry_hub/server/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/__init__.py)

The current version is `0.9.1`, declared as `__version__` in the package `__init__`. Source: [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)

## Tool Categories

The bundled tools are grouped by namespace inside the default registry, which is exposed as a list of `toolregistry.config.PythonSource` entries in `server/registry.py`. The list is the single source of truth for "which tools are available out of the box" and is consumed by `build_registry()` when the CLI or server starts. Source: [src/toolregistry_hub/server/registry.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/registry.py)

| Category | Example Tools | Namespace | Notes |
|----------|---------------|-----------|-------|
| Calculator | `Calculator`, `BaseCalculator` | `calculator` | Arithmetic via `Calculator.evaluate("2 + 2 * 3")` |
| Date / Time | `DateTime` | `datetime` | Timezone-aware helpers |
| File I/O | `FileOps`, `FileReader` | `file_ops`, `reader` | v0.8.0 replaced diff-based editing with exact string replacement ([#70](https://github.com/Oaklight/toolregistry-hub/pull/70)) |
| File System | `FileSearch`, `PathInfo` | `file_search`, `path_info` | Glob/grep/tree and unified path metadata ([#71](https://github.com/Oaklight/toolregistry-hub/pull/71), [#72](https://github.com/Oaklight/toolregistry-hub/pull/72)) |
| Shell / Tasks | `BashTool`, `CronTool`, `TodoList` | `bash`, `cron`, `todo` | Execution and scheduling helpers |
| Reasoning | `ThinkTool` | `think` | Unifies `reason`/`think` into a single endpoint (v0.5.5) |
| Web Search | `BraveSearch`, `SearXNGSearch`, `SerperSearch`, `TavilySearch`, `ScrapelessSearch` | `websearch` | Provider-agnostic via `BaseSearch` and `SearchResult` |
| Web Fetch | `Fetch` | `web/fetch` | Markdown negotiation, CDP rendering (v0.8.3), structured return (PR #140) |
| Conversion | `UnitConverter` | `unit_converter` | Uses `bind_literal` to expose enum-style choices to LLM clients |

### Web Search Provider Family

The web search subsystem is normalized through a `BaseSearch` abstract class and a common `SearchResult` dataclass. Each concrete provider (Tavily, Brave, SearXNG, Serper, Scrapeless) accepts a comma-separated key string or a file path through the shared `APIKeyParser` utility, which provides round-robin key selection and per-key rate-limit delay. Source: [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)

For Google-shaped APIs, the `google_parser.py` module defines a `GoogleAPIConfig` dataclass that describes how to extract organic results, URLs, and descriptions from heterogeneous providers (Bright Data, Scrapeless, etc.) and unifies them into `SearchResult` objects via `GoogleResultParser`. Source: [src/toolregistry_hub/websearch/google_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/google_parser.py)

## Tool Registration and Configuration

### Default Tool Manifest

`_DEFAULT_TOOLS` in `server/registry.py` is the built-in manifest: a `list[PythonSource]` whose entries reference each Hub tool class by dotted `class_path` and assign it a namespace (e.g. `toolregistry_hub.bash_tool.BashTool` → `bash`, `toolregistry_hub.fetch.Fetch` → `web/fetch`). A companion `_TOOL_METADATA` dict applies namespace-level tag and defer overrides post-registration, so the same source list can ship different runtime policies per deployment without code changes. Source: [src/toolregistry_hub/server/registry.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/registry.py)

A **configurable hook** runs after registration and inspects each instance for the `Configurable` protocol. If a class does not implement `_is_configured()` returning `True` it is auto-disabled, so missing API keys never produce a broken tool at call time. Source: [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)

### Declaring Required Environment Variables

Search providers mark their dependencies declaratively with the `@requires_env("TAVILY_API_KEY")` class decorator from `utils/requirements.py`. The decorator simply attaches a `_required_envs` list to the class so the registry builder (or any `Configurable` consumer) can verify the environment without importing the provider-specific constants. Source: [src/toolregistry_hub/utils/requirements.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/requirements.py)

### Schema-Time Literal Binding

Some parameters only have a known value set at runtime (e.g. the names of unit-conversion functions, or the set of configured search engines). `bind_literal()` in `utils/annotation_helpers.py` returns a shallow copy of a function whose `param` annotation is replaced with `Literal[*choices]`, so the generated JSON schema for MCP/OpenAPI surfaces an `enum` instead of an unconstrained `string`. The original callable is left untouched. Source: [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py)

## Runtime Architecture

```mermaid
flowchart LR
    A["Hub CLI (HubCLI)"] --> B["HubApp"]
    B --> C["build_registry()"]
    C --> D["_DEFAULT_TOOLS<br/>(PythonSource list)"]
    D --> E["ToolRegistry"]
    E --> F["@requires_env + Configurable<br/>hook"]
    F --> G["_TOOL_METADATA<br/>(tag/defer overrides)"]
    G --> H["OpenAPI / MCP adapters<br/>(toolregistry-server)"]
    E -.direct import.-> I["User code:<br/>Calculator(), Fetch() ..."]
```

The CLI is `HubCLI` in `server/cli.py`, which subclasses `toolregistry_server.cli.CLI`, attaches a `HubApp`, and overrides `print_banner()` / `get_version_string()` so that Hub prints a PyPI version-check banner before dispatching to `openapi` or `mcp` subcommands. Source: [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)

## See Also

- [Oaklight/toolregistry-hub#148 — adopt toolregistry-server 0.4.0 architecture](https://github.com/Oaklight/toolregistry-hub/issues/148)
- [Oaklight/toolregistry-hub#150 — toolregistry 0.11.2 compatibility failure](https://github.com/Oaklight/toolregistry-hub/issues/150)
- [Oaklight/toolregistry-hub#144 — Hot-reload `tools.jsonc` across service modes](https://github.com/Oaklight/toolregistry-hub/issues/144)
- [Release v0.9.1](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.9.1) — current version
- [Online documentation](https://toolregistry-hub.readthedocs.io/en/latest/)

---

<a id='page-3'></a>

## Server, CLI, and Configuration

### Related Pages

Related topics: [Overview and Architecture](#page-1), [Core Tools Library](#page-2), [Deployment, Distribution, and Operations](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/toolregistry_hub/server/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/__init__.py)
- [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)
- [src/toolregistry_hub/server/banner.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/banner.py)
- [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)
- [src/toolregistry_hub/utils/requirements.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/requirements.py)
- [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py)
- [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)
- [src/toolregistry_hub/utils/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/__init__.py)
- [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)
</details>

# Server, CLI, and Configuration

The `toolregistry-hub` package exposes a curated set of tools (Calculator, Fetch, FileOps, web-search adapters, etc.) and an opt-in server mode that can serve those tools over **OpenAPI** or **MCP** (streamable-http / SSE). Server-mode infrastructure — adapters, routing, authentication, and the bulk of the CLI — was migrated to a sibling package, [`toolregistry-server`](https://github.com/Oaklight/toolregistry-server), in v0.7.0 ([release notes](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.7.0)). Starting in v0.9.1, the hub adopts the upstream **0.4.0 architecture** (`App` class, `Adapter` ABC, `registry_builder`, unified auth, reusable CLI) and now only contributes Hub-specific glue on top of that framework ([issue #148](https://github.com/Oaklight/toolregistry-hub/issues/148), [release v0.9.1](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.9.1)).

## Server Module Layout

The `server` sub-package is intentionally thin. Its package docstring states that it provides "Hub-specific server components" (`registry`, `cli`) and directs users to import adapters, routing, and auth utilities directly from `toolregistry_server` ([source: `src/toolregistry_hub/server/__init__.py:1-8`](#)).

The top-level hub module re-exports the tool classes that the server is expected to host — `Calculator`, `DateTime`, `FileOps`, `Fetch`, `BraveSearch`, `SearXNGSearch`, `SerperSearch`, `TavilySearch`, `BashTool`, `CronTool`, `TodoList`, `UnitConverter`, and others — so that downstream callers and `build_registry()` can locate them through a single import path ([source: `src/toolregistry_hub/__init__.py:1-46`](#)).

## Hub CLI

The command-line entry point lives in [`src/toolregistry_hub/server/cli.py`](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py). The `HubCLI` class subclasses [`toolregistry_server.cli.CLI`](https://github.com/Oaklight/toolregistry-server) and wires it to a `HubApp` instance:

```python
class HubCLI(CLI):
    def __init__(self) -> None:
        super().__init__(app=HubApp())
```

Source: [`src/toolregistry_hub/server/cli.py:1-19`](#).

It overrides three hooks of the base CLI:

| Override | Purpose |
| --- | --- |
| `configure_subparsers(subparsers)` | Adds Hub-specific arguments (`--config`, `--tool-discovery`, etc.) to every subcommand parser |
| `get_version_string()` | Returns a Hub-flavoured version string, including a PyPI update check via `_get_version_string()` |
| `print_banner()` | Prints the Hub ASCII banner with update-check output via `_print_hub_banner()` |
| `dispatch(parsed)` | Routes parsed args into `HubApp` while injecting Hub-only kwargs (`tools_config_path`, `enable_discovery`, ...) |

Source: [`src/toolregistry_hub/server/cli.py:20-66`](#).

The banner itself is defined in a separate module to keep `cli.py` focused on dispatch logic. The ASCII-art header is stored as the `BANNER_ART` constant in [`src/toolregistry_hub/server/banner.py`](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/banner.py), and `print_banner()` combines it with version metadata.

### End-User Subcommands

`HubCLI` inherits the two service subcommands from `toolregistry_server.cli.CLI` — `openapi` and `mcp` — so operators can launch either transport with Hub defaults applied. The community has flagged a follow-up enhancement in [issue #144](https://github.com/Oaklight/toolregistry-hub/issues/144) to **hot-reload** `tools.jsonc` across `OpenAPI`, `MCP streamable-http`, and `MCP SSE` modes behind a unified gateway without restarting the service; this is not yet implemented and currently requires a process restart to pick up config changes.

## Configuration System

Hub supports three orthogonal configuration mechanisms that compose at different layers:

### 1. Declarative tool selection via `tools.jsonc`

Introduced in v0.6.0, `tools.jsonc` allows users to declare which tools are loaded at startup, with **allowlist/denylist modes** and **hierarchical namespace matching** ([release v0.6.0](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.6.0)). The path to this file is passed through the CLI as `--config` and forwarded into `HubApp` via `tools_config_path`. As of v0.8.1, a built-in `tools.yaml` ships with the package, providing default namespace/tag metadata that enables profile filtering without requiring user config ([release v0.8.1](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.8.1)).

### 2. Per-method metadata overrides via `_TOOL_METADATA`

From v0.8.2, `tool_metadata` config overrides are applied when `build_registry()` constructs the hub registry, and method-level tag/defer overrides from a class-level `_TOOL_METADATA` mapping are supported ([release v0.8.2](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.8.2)). Deferred-tool filtering added in `toolregistry-server>=0.3.1` is correctly reflected in MCP and OpenAPI listings.

### 3. Runtime-config / environment introspection

The `Configurable` protocol in [`src/toolregistry_hub/utils/configurable.py`](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py) defines a structural contract for tool classes:

```python
@runtime_checkable
class Configurable(Protocol):
    def _is_configured(self) -> bool: ...
```

Source: [`src/toolregistry_hub/utils/configurable.py:13-19`](#). Any class implementing `_is_configured()` automatically satisfies the protocol (no inheritance required). `build_registry()` uses this to **auto-disable** a tool whose required configuration (API keys, base URLs) is missing.

Complementary to that, the `@requires_env(*envs)` decorator in [`src/toolregistry_hub/utils/requirements.py`](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/requirements.py) attaches a `_required_envs` class attribute listing the environment variables a tool needs:

```python
@requires_env("MY_API_KEY")
class MyTool: ...
MyTool._required_envs  # ['MY_API_KEY']
```

Source: [`src/toolregistry_hub/utils/requirements.py:14-38`](#).

The [`APIKeyParser`](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py) utility generalises this further by accepting multiple API keys (from a literal string, an environment variable, or a token file), cycling through them in **round-robin** order, and applying a configurable `rate_limit_delay` between requests — useful for web-search adapters that frequently hit per-key quotas. Source: [`src/toolregistry_hub/utils/api_key_parser.py:11-55`](#).

Finally, [`bind_literal`](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py) in `annotation_helpers.py` rewrites a function's `__annotations__` so a runtime-known set of values is exposed to MCP/LLM clients as a `Literal[...]` enum, improving tool-input schema quality. Source: [`src/toolregistry_hub/utils/annotation_helpers.py:13-49`](#).

## Architecture Summary

```mermaid
flowchart LR
  A[HubCLI] -->|dispatch| B[HubApp]
  B --> C[build_registry]
  C --> D[tools.jsonc / tools.yaml]
  C --> E[Configurable & _is_configured]
  C --> F[requires_env & APIKeyParser]
  B --> G[toolregistry_server.App]
  G --> H[OpenAPI Adapter]
  G --> I[MCP Adapter<br/>streamable-http / SSE]
```

## See Also

- [Issue #148 — adopt toolregistry-server 0.4.0 architecture](https://github.com/Oaklight/toolregistry-hub/issues/148)
- [Issue #144 — hot-reload tools.jsonc config across service modes](https://github.com/Oaklight/toolregistry-hub/issues/144)
- [Release v0.9.1](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.9.1) · [v0.8.2](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.8.2) · [v0.8.1](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.8.1) · [v0.7.0](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.7.0) · [v0.6.0](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.6.0)
- Companion package: [`toolregistry-server`](https://github.com/Oaklight/toolregistry-server)

---

<a id='page-4'></a>

## Deployment, Distribution, and Operations

### Related Pages

Related topics: [Overview and Architecture](#page-1), [Server, CLI, and Configuration](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)
- [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)
- [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)
- [src/toolregistry_hub/server/banner.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/banner.py)
- [src/toolregistry_hub/utils/requirements.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/requirements.py)
- [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)
- [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)
- [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py)
- [src/toolregistry_hub/websearch/websearch_scrapeless.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_scrapeless.py)
- [src/toolregistry_hub/websearch/google_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/google_parser.py)
</details>

# Deployment, Distribution, and Operations

## Overview

`toolregistry-hub` is the **application-layer package** of the three-part ToolRegistry ecosystem. While [`toolregistry`](https://github.com/Oaklight/ToolRegistry) provides core tool-registration primitives and [`toolregistry-server`](https://github.com/Oaklight/toolregistry-server) provides the transport adapters, the hub ships the actual tool implementations and a runnable server entrypoint that operators deploy [Source: [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)]. The package is published to multiple distribution channels — PyPI, Docker Hub, and (in progress) conda-forge — and exposes a `toolregistry-hub` CLI for daily operations.

This page covers everything required to **distribute, install, run, and observe** the hub: package surfaces, server CLI semantics, environment-variable conventions, API-key rotation, and upstream-compatibility signals that affect CI.

## Distribution Channels

### PyPI (primary)

`toolregistry-hub` is published on PyPI as a regular Python wheel/sdist under the same name used in imports:

```bash
pip install toolregistry-hub
```

The top-level import surface is intentionally flat, so that downstream tooling (and the registry builder) can enumerate all built-in tools with one statement [Source: [src/toolregistry_hub/__init__.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/__init__.py)]:

```python
from toolregistry_hub import (
    BashTool, Calculator, DateTime, FileOps, FileReader, FileSearch,
    PathInfo, ThinkTool, UnitConverter, Fetch, FetchError,
    SearchResult, BraveSearch, SearXNGSearch, SerperSearch, TavilySearch,
)
```

### Docker Hub

A companion image, `oaklight/toolregistry-hub-server`, is published alongside the PyPI release. The README positions it as the deployment target when users want REST or MCP servers without managing a Python environment [Source: [README.md](https://github.com/Oaklight/toolregistry-hub/blob/main/README.md)]. Release notes from v0.5.5 onward reference `REGISTRY_MIRROR` build arguments and a slimmer `.dockerignore` that excludes `references/` — both intended to keep image size and build time low in CI.

### conda-forge (pending)

Issue #151 tracks a **conda-forge submission** for the package, with the staged-recipes PR #33859 already passing CI and awaiting a reviewer [Source: [community context](https://github.com/Oaklight/toolregistry-hub/issues/151)]. Until it is merged, conda users must install via `pip` inside a conda environment or wait for the recipe to land on the `conda-forge` channel.

## CLI and Server Operations

### `HubCLI` — single entrypoint, multiple subcommands

Since v0.7.0 the server-side code (OpenAPI adapter, MCP adapter, CLI commands) lives in `toolregistry-server`; the hub ships a thin subclass that injects Hub-specific behavior [Source: [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)]:

```python
class HubCLI(CLI):
    def __init__(self) -> None:
        super().__init__(app=HubApp())

    def configure_subparsers(self, subparsers):
        for sub_parser in subparsers.values():
            _add_hub_arguments(sub_parser)
```

The class overrides four hooks on the base `CLI`:
- **`configure_subparsers`** — injects Hub-only flags into every subparser.
- **`get_version_string`** — produces a Hub-flavored version string with an **update check** against PyPI.
- **`print_banner`** — prints the ASCII banner and the PyPI version comparison.
- **`dispatch`** — forwards Hub-specific kwargs such as `tools_config_path` and `enable_discovery` to `HubApp` [Source: [src/toolregistry_hub/server/cli.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/cli.py)].

Usage mirrors upstream:

```bash
toolregistry-hub openapi [OPTIONS]
toolregistry-hub mcp     [OPTIONS]
```

### Banner and version display

A dedicated module, `src/toolregistry_hub/server/banner.py`, owns the ASCII art so it can be reused by banner printers and release scripts [Source: [src/toolregistry_hub/server/banner.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/server/banner.py)]. This separation lets the banner be regenerated without touching CLI dispatch logic.

## Operational Concerns

### Environment-variable requirements

Tools that depend on external APIs declare their prerequisites with the `@requires_env(...)` decorator. The decorator attaches a `_required_envs` list to the class, which the registry builder consults at startup [Source: [src/toolregistry_hub/utils/requirements.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/requirements.py)]:

```python
@requires_env("MY_API_KEY")
class MyTool: ...
MyTool._required_envs  # ['MY_API_KEY']
```

A complementary structural protocol, `Configurable`, lets any class with an `_is_configured()` method participate in readiness checks — used by `build_registry()` to auto-disable tools whose configuration is missing [Source: [src/toolregistry_hub/utils/configurable.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/configurable.py)]. Operators should ensure required env vars are exported in the container/host environment before launch.

### API-key rotation

Search backends (Tavily, Serper, Brave, SearXNG, Scrapeless, Bright Data, …) all funnel their credentials through a single utility, `APIKeyParser` [Source: [src/toolregistry_hub/utils/api_key_parser.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/api_key_parser.py)]. It supports three load paths — direct argument, environment variable, and a tokens file — and applies **round-robin selection** with a configurable `rate_limit_delay`. This is the recommended pattern for multi-key rotation in production:

```python
parser = APIKeyParser(
    env_var_name="TAVILY_API_KEYS",
    api_tokens_file="/etc/toolregistry/keys.txt",
    rate_limit_delay=1.0,
)
```

Backends that proxy through an external gateway (e.g. Scrapeless) document their own setup; the comment header in `websearch_scrapeless.py` is the canonical reference [Source: [src/toolregistry_hub/websearch/websearch_scrapeless.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/websearch/websearch_scrapeless.py)].

### Runtime annotation overrides

Some tools expose parameters whose valid values are only known at runtime (e.g. unit-conversion function names, configured search engines). To keep LLM-facing schemas precise, `bind_literal()` rewrites a function's `__annotations__` to a `Literal[...]` type **without mutating the original** [Source: [src/toolregistry_hub/utils/annotation_helpers.py](https://github.com/Oaklight/toolregistry-hub/blob/main/src/toolregistry_hub/utils/annotation_helpers.py)]. This is an internal hook operators rarely touch, but it is critical for correct JSON-schema generation in MCP/OpenAPI listings.

### Upstream-compatibility signals

Because the hub re-exports the core `toolregistry` API surface, every bump in the upstream library can break downstream imports. Issue #150 documents exactly this failure mode: toolregistry **0.11.2** broke hub tests and required CI investigation [Source: [community context](https://github.com/Oaklight/toolregistry-hub/issues/150)]. Operators pinning the hub in production should mirror the same upper-bound testing discipline — track the CI status badge in the README and treat minor-version bumps of `toolregistry` as a potential compatibility event.

## Deployment architecture

```mermaid
flowchart LR
    A[Developer / CI] -->|git push| B(GitHub Actions)
    B -->|build + test| C{PyPI}
    B -->|docker build| D[Docker Hub<br/>oaklight/toolregistry-hub-server]
    B -->|staged-recipes| E[conda-forge<br/>pending #151]
    C -->|pip install| F[Operator host / K8s pod]
    D --> F
    E -->|conda install| F
    F -->|toolregistry-hub openapi| G[HubApp]
    F -->|toolregistry-hub mcp| G
    G -->|build_registry| H[Configured ToolRegistry]
    H -->|OpenAPI / MCP| I[LLM clients]
```

## See Also

- [ToolRegistry core](https://github.com/Oaklight/ToolRegistry) — registry and schema primitives
- [toolregistry-server](https://github.com/Oaklight/toolregistry-server) — adapter and CLI base classes (`App`, `Adapter`, `CLI`)
- [Issue #151 — conda-forge publishing](https://github.com/Oaklight/toolregistry-hub/issues/151)
- [Issue #148 — Adopt toolregistry-server 0.4.0 architecture](https://github.com/Oaklight/toolregistry-hub/issues/148)
- [Issue #144 — Hot-reload `tools.jsonc` config](https://github.com/Oaklight/toolregistry-hub/issues/144)
- [Release v0.9.1 — Server 0.4.0 adoption](https://github.com/Oaklight/toolregistry-hub/releases/tag/v0.9.1)

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: Oaklight/toolregistry-hub

Summary: Found 8 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Capability evidence risk - Capability evidence risk requires verification.

## 1. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/Oaklight/toolregistry-hub

## 2. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/Oaklight/toolregistry-hub/issues/150

## 3. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/Oaklight/toolregistry-hub

## 4. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/Oaklight/toolregistry-hub

## 5. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/Oaklight/toolregistry-hub

## 6. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/Oaklight/toolregistry-hub/issues/148

## 7. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/Oaklight/toolregistry-hub

## 8. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/Oaklight/toolregistry-hub

<!-- canonical_name: Oaklight/toolregistry-hub; human_manual_source: deepwiki_human_wiki -->
