--- description: Tool development patterns for investigation and chat tools globs: - tools/** - integrations/**/tools/** --- # Tool Development ## Where tools live After the V0.2 Phase 1 refactor there are two locations: - `integrations//tools/` — vendor-specific tools (one folder per vendor) Example: `integrations/grafana/tools/` - `tools/` — non-vendor tools only (fleet monitoring, SRE guidance, investigation helpers, etc.) Do NOT create new vendor tool directories directly under `tools/`. ## Two Registration Patterns ### 1. Function + `@tool` decorator (preferred for simple tools, required for vendor tools) - Vendor tools: add `@tool`-decorated functions in `integrations//tools/__init__.py` - Non-vendor tools: single file under `tools/` ```python from tools.tool_decorator import tool @tool( name="my_tool_name", source="grafana", # Required: EvidenceSource literal description="What this tool does.", use_cases=["When to use it"], requires=["api_key"], input_schema={...}, # Optional: inferred from signature if omitted is_available=my_check_fn, # (sources: dict) -> bool extract_params=my_extract, # (sources: dict) -> dict surfaces=("investigation",), # Default; add "chat" to expose in chat mode ) def my_tool_name(param: str) -> dict: ... ``` ### 2. `BaseTool` subclass (for complex non-vendor tools with helpers) Package directory: `tools//__init__.py`. Instantiate at module level. ```python from tools.base import BaseTool class MyToolName(BaseTool): name = "my_tool_name" source = "grafana" description = "..." input_schema = { "type": "object", "properties": {...}, "required": [...], } use_cases = [...] requires = [...] outputs = {"field": "description"} def is_available(self, sources: dict) -> bool: ... def extract_params(self, sources: dict) -> dict: ... def run(self, param: str, **_kwargs) -> dict: ... my_tool_name = MyToolName() ``` ## Rules - `source` is **required** — must be a valid `EvidenceSource` literal from `core/domain/types/evidence.py` - Tool packages under `tools/` use **snake_case** directory names — no PascalCase, no `Tool` suffix in the dir name - The registry auto-discovers tools via `pkgutil.iter_modules` - Do NOT add your module to `_SKIP_MODULE_NAMES` in `registry.py` - Module names ending in `_test` are auto-skipped by the registry - `surfaces` defaults to `("investigation",)` — add `"chat"` explicitly if needed - For `BaseTool`: instantiate the class at module level so the registry can find it - Return `dict` from `run()` with an `"error"` key on failure, structured data on success ## Valid `EvidenceSource` Values `storage`, `batch`, `tracer_web`, `cloudwatch`, `aws_sdk`, `knowledge`, `grafana`, `datadog`, `honeycomb`, `coralogix`, `eks`, `github`, `sentry`, `google_docs`, `vercel`, `opsgenie`, `elasticsearch`