Add an AI-Q Tool
Use this skill when a developer wants to add a general-purpose tool to AI-Q — a
NeMo Agent Toolkit (NAT) function such as a web search, calculator, or code
helper. The tool is a package under sources/, registered with
@register_function and referenced directly in an agent's tools list.
Start Here
- Confirm this is a general utility tool. If it is domain-specific retrieval
that should appear as a toggleable source in the UI, use
aiq-add-data-source
instead — a data source is the same NAT function plus a data_source_registry
entry. This skill stops at wiring the tool into an agent.
- Read the authoritative files below before editing.
- Copy the closest existing tool package rather than inventing a new shape.
- Never print or commit API keys; resolve secrets at runtime via
SecretStr.
Authoritative References
docs/source/extending/adding-a-tool.md: canonical 8-step walkthrough; the
workflow below mirrors it.
sources/tavily_web_search/: minimal tool package.
sources/google_scholar_paper_search/: tool package with a separate client,
a graceful missing-secret stub, and tests.
docs/source/extending/adding-a-data-source.md: "Data Source vs. Tool" — a
data source is architecturally identical to a tool; only the registry wiring
differs.
Existing tools to model on: tavily_web_search, exa_web_search,
paper_search (Google Scholar), knowledge_retrieval.
Longer procedures live in this bundle:
- references/nat-function-pattern.md:
package layout, config class, registration, the missing-secret stub, the
pyproject.toml entry point, and wiring the tool into an agent in YAML.
- references/testing.md: unit-test pattern and the
install/test/lint validation commands.
Workflow
- Pick the closest existing package under
sources/ and inspect its layout.
- Create
sources/<my_tool>/ with src/register.py, a client module,
pyproject.toml, and tests/.
- Define a
FunctionBaseConfig subclass with a stable name= (this becomes the
YAML _type); resolve any API key via SecretStr.
- Register an async
@register_function that yields a FunctionInfo; yield a
graceful stub when a required secret is missing.
- Add the
[project.entry-points."nat.plugins"] entry and install the package
editable.
- Reference the tool in a config under
configs/ (under functions:, then in
an agent's tools: list).
- Add focused tests; run the validation commands below.
- Summarize changed files and paste the test/lint evidence.
Validation
Run the narrowest commands first; broaden only if the change touches shared code.
uv pip install -e ./sources/my_tool
uv run pytest sources/my_tool/tests
uv run ruff check sources/my_tool
uv run ruff format --check sources/my_tool
Expected: the package installs, its tests pass, and Ruff reports no lint or
format failures for the new tool package.
Common Mistakes
- Omitting the
[project.entry-points."nat.plugins"] entry in pyproject.toml,
so NAT never discovers the registration at import time.
- Crashing on a missing API key instead of yielding a stub that returns a clear
error string.
- Raising exceptions from the tool function; tools must return error messages as
strings so they never crash the agent.
- Weak docstrings: the LLM uses the function docstring as the tool description to
decide when to call it — state what it does, when to use it, and what it returns.
- Printing API keys or embedding secrets in YAML instead of using environment
variables or
SecretStr.
Related Skills
aiq-configure-workflow
aiq-add-data-source
aiq-release-qa
aiq-prepare-pr
1---2name: aiq-add-tool3description: Use when adding or changing a general-purpose AI-Q tool (a NeMo Agent Toolkit function) under sources/, defining its FunctionBaseConfig schema, registering it with @register_function, wiring it into an agent's tools list, or testing it.4license: Apache-2.05---67# Add an AI-Q Tool89Use this skill when a developer wants to add a general-purpose tool to AI-Q — a10NeMo Agent Toolkit (NAT) function such as a web search, calculator, or code11helper. The tool is a package under `sources/`, registered with12`@register_function` and referenced directly in an agent's `tools` list.1314## Start Here1516- Confirm this is a **general utility tool**. If it is domain-specific retrieval17 that should appear as a toggleable source in the UI, use `aiq-add-data-source`18 instead — a data source is the same NAT function plus a `data_source_registry`19 entry. This skill stops at wiring the tool into an agent.20- Read the authoritative files below before editing.21- Copy the closest existing tool package rather than inventing a new shape.22- Never print or commit API keys; resolve secrets at runtime via `SecretStr`.2324## Authoritative References2526- `docs/source/extending/adding-a-tool.md`: canonical 8-step walkthrough; the27 workflow below mirrors it.28- `sources/tavily_web_search/`: minimal tool package.29- `sources/google_scholar_paper_search/`: tool package with a separate client,30 a graceful missing-secret stub, and tests.31- `docs/source/extending/adding-a-data-source.md`: "Data Source vs. Tool" — a32 data source is architecturally identical to a tool; only the registry wiring33 differs.3435Existing tools to model on: `tavily_web_search`, `exa_web_search`,36`paper_search` (Google Scholar), `knowledge_retrieval`.3738Longer procedures live in this bundle:3940- [references/nat-function-pattern.md](references/nat-function-pattern.md):41 package layout, config class, registration, the missing-secret stub, the42 `pyproject.toml` entry point, and wiring the tool into an agent in YAML.43- [references/testing.md](references/testing.md): unit-test pattern and the44 install/test/lint validation commands.4546## Workflow47481. Pick the closest existing package under `sources/` and inspect its layout.492. Create `sources/<my_tool>/` with `src/register.py`, a client module,50 `pyproject.toml`, and `tests/`.513. Define a `FunctionBaseConfig` subclass with a stable `name=` (this becomes the52 YAML `_type`); resolve any API key via `SecretStr`.534. Register an async `@register_function` that yields a `FunctionInfo`; yield a54 graceful stub when a required secret is missing.555. Add the `[project.entry-points."nat.plugins"]` entry and install the package56 editable.576. Reference the tool in a config under `configs/` (under `functions:`, then in58 an agent's `tools:` list).597. Add focused tests; run the validation commands below.608. Summarize changed files and paste the test/lint evidence.6162## Validation6364Run the narrowest commands first; broaden only if the change touches shared code.6566```bash67uv pip install -e ./sources/my_tool68uv run pytest sources/my_tool/tests69uv run ruff check sources/my_tool70uv run ruff format --check sources/my_tool71```7273Expected: the package installs, its tests pass, and Ruff reports no lint or74format failures for the new tool package.7576## Common Mistakes7778- Omitting the `[project.entry-points."nat.plugins"]` entry in `pyproject.toml`,79 so NAT never discovers the registration at import time.80- Crashing on a missing API key instead of yielding a stub that returns a clear81 error string.82- Raising exceptions from the tool function; tools must return error messages as83 strings so they never crash the agent.84- Weak docstrings: the LLM uses the function docstring as the tool description to85 decide when to call it — state what it does, when to use it, and what it returns.86- Printing API keys or embedding secrets in YAML instead of using environment87 variables or `SecretStr`.8889## Related Skills9091- `aiq-configure-workflow`92- `aiq-add-data-source`93- `aiq-release-qa`94- `aiq-prepare-pr`