Custom tool development guide
Introduction#
Add a tool: define the schema, implement the function, register it, test bad inputs, document it. Naming, errors, security, and packaging.
Project structure#
The framework organizes tools under a dedicated tools/ namespace, integrates them into an agent tool registry, and exposes them through structured LangChain tools. Services, prompts, and sanitization utilities sit beside the tools at runtime.
graph TB subgraph "Tools" T1["tools/browser_use/tool.py"] T2["tools/google_search/seach_agent.py"] T3["tools/website_context/super_scraper.py"] T4["tools/youtube_utils/get_info.py"] T5["tools/gmail/send_email.py"] T6["tools/calendar/create_calender_events.py"] end subgraph "Agent Integration" A1["agents/react_tools.py"] end subgraph "Services" S1["services/browser_use_service.py"] end subgraph "Prompts" P1["prompts/website.py"] end subgraph "Utilities" U1["utils/agent_sanitizer.py"] end subgraph "Extension" E1["clients/browser-extension/.output/chrome-mv3-dev/background.js"] end T1 --> A1 T2 --> A1 T3 --> A1 T4 --> A1 T5 --> A1 T6 --> A1 A1 --> S1 A1 --> P1 A1 --> U1 S1 --> U1 A1 --> E1
Core components#
- StructuredTool wrappers: Tools are defined as LangChain StructuredTool instances with typed Pydantic schemas and coroutine implementations.
- Tool schemas: Pydantic models define input fields, constraints, and descriptions for validation and documentation.
- Tool implementations: Async coroutines orchestrate service calls, external APIs, or scraping utilities.
- Tool registry: A builder function composes tools dynamically, optionally injecting credentials or context.
- Service layer: Dedicated services encapsulate LLM prompting, sanitization, and domain-specific logic.
- Validation and sanitization: Utilities enforce safe JSON action plans and guardrails for scripts.
Architecture overview#
Where a new tool plugs in:
- Tool layer: Defines inputs and async logic.
- Agent layer: Composes tools and injects context.
- Service layer: Encapsulates LLM prompts, sanitization, and domain operations.
- Extension layer: Executes browser actions and interacts with the page.
sequenceDiagram participant User as "User" participant Agent as "Agent Tools" participant Tool as "StructuredTool" participant Service as "Service Layer" participant Ext as "Extension" User->>Agent : "Invoke tool with parameters" Agent->>Tool : "Call coroutine with validated inputs" Tool->>Service : "Perform operation (LLM, API, scrape)" Service->>Ext : "Execute browser action (optional)" Ext-->>Service : "Result or error" Service-->>Tool : "Processed result" Tool-->>Agent : "Formatted response" Agent-->>User : "Final output"
Detailed component analysis#
Tool creation pattern#
Follow this repeatable pattern to implement a new tool:
- Define a Pydantic input schema with Field constraints and descriptions.
- Implement an async coroutine that validates inputs, orchestrates work, and returns a normalized result.
- Wrap the coroutine in a StructuredTool with a descriptive name and schema.
- Register the tool in the agent builder function.
flowchart TD Start(["Define Schema"]) --> Impl["Implement Coroutine"] Impl --> Validate["Validate Inputs"] Validate --> Orchestrate["Orchestrate Work<br/>Service/API/Scraper"] Orchestrate --> Return["Return Normalized Result"] Return --> Wrap["Wrap in StructuredTool"] Wrap --> Register["Register in Agent Builder"] Register --> End(["Ready"])
Schema definition#
- Use Pydantic Field constraints (min_length, ge, le, HttpUrl, EmailStr) to enforce input validity early.
- Provide clear descriptions for each field to aid LLM reasoning.
- Prefer optional fields with sensible defaults when appropriate.
- Reuse shared schemas across related tools to maintain consistency.
Examples of schema patterns:
- URL and question-based tools:
WebsiteToolInput,YouTubeToolInput - OAuth-enabled tools:
GmailToolInput,CalendarToolInput - Action-focused tools:
BrowserActionInput
Function implementation patterns#
- Use asyncio.to_thread for blocking operations to avoid blocking the event loop.
- Normalize outputs to strings or structured JSON for downstream consumers.
- Handle missing credentials and return actionable error messages.
- Apply bounds checking for numeric parameters.
Examples:
- Web search pipeline:
web_search_pipeline - Website markdown fetcher:
clean_response - YouTube info extraction:
get_video_info - Gmail send:
send_email - Calendar event creation:
create_calendar_event
Registration and dynamic composition#
- Tools are registered via a builder that accepts context (e.g., tokens, session payloads).
- Partial functions inject default credentials to avoid requiring users to pass tokens every time.
- Conditional tools are added only when credentials are present.
Key references:
build_agent_toolsAGENT_TOOLS
Browser action tool flow#
The browser action tool demonstrates the full lifecycle: schema, service invocation, sanitization, and structured output.
sequenceDiagram
participant Tool as "browser_action_agent"
participant Service as "AgentService"
participant LLM as "LLM"
participant Sanitizer as "agent_sanitizer"
Tool->>Service : "generate_script(goal, target_url, dom_structure, constraints)"
Service->>LLM : "Prompt with DOM info"
LLM-->>Service : "Raw action plan"
Service->>Sanitizer : "sanitize_json_actions"
Sanitizer-->>Service : "Validated plan or problems"
Service-->>Tool : "Result {ok, action_plan or error}"
Tool-->>Caller : "StructuredTool result"Extension integration for browser actions#
The extension executes browser actions based on tool commands. It logs and routes tool types to specific handlers, returning structured results or errors.
sequenceDiagram
participant Agent as "Agent"
participant Ext as "Extension background.js"
Agent->>Ext : "executeAgentTool(actionType, params)"
Ext->>Ext : "Route to handler (e.g., OPEN_TAB, CLICK)"
Ext-->>Agent : "{success, result or error}"Dependency analysis#
- Tool schemas depend on Pydantic for validation.
- Tools depend on LangChain StructuredTool for registration.
- Agent builder composes tools and injects context.
- Services depend on prompts and sanitization utilities.
- Extension depends on browser APIs for action execution.
graph LR Pydantic["Pydantic Schemas"] --> Tools["StructuredTool Wrappers"] LangChain["LangChain Tools"] --> Tools Tools --> Agent["Agent Builder"] Agent --> Services["Services"] Services --> Prompts["Prompts"] Services --> Sanitizer["agent_sanitizer"] Agent --> Extension["Extension"]
Performance considerations#
- Use asyncio.to_thread for blocking I/O to prevent event loop stalls.
- Bound numeric inputs (e.g., max_results) to reasonable ranges.
- Limit DOM previews and interactive element listings to avoid token overhead.
- Cache or reuse expensive computations when feasible.
- Prefer lightweight scrapers and minimize network calls.
Troubleshooting guide#
Common issues and resolutions:
- Invalid JSON action plans: The sanitizer enforces required fields and safe patterns. Review validation messages and adjust tool outputs accordingly.
- Missing credentials: Tools return explicit error messages when tokens are absent; ensure context injection via the agent builder.
- Unexpected API responses: Wrap external calls in try/except blocks and normalize error messages.
- Extension errors: The extension logs tool types and errors; check the console for actionable diagnostics.
References:
sanitize_json_actions_gmail_toolexecuteAgentTool
Conclusion#
Schema first, service behind it, sanitizer if it touches the browser. Register through the agent builder and keep names stable.
Appendices#
Implementing a new tool#
- Define a Pydantic input schema with Field constraints.
- Implement an async coroutine that validates inputs, performs work, and normalizes output.
- Wrap the coroutine in a StructuredTool with a descriptive name and schema.
- Add the tool to the agent builder and inject defaults via context when applicable.
- Test with representative inputs and edge cases.
- Validate outputs with the sanitizer and extension integration.
Testing strategies#
- Unit tests for blocking operations: Mock external APIs and assert normalized outputs.
- Integration tests: Use the agent builder with mock context to exercise tool composition.
- Sanitization tests: Provide malformed JSON and invalid action types to verify reliability.
- Full-path tests: run browser actions through the extension and check results.
Validation approaches#
- Pydantic schema validation for inputs.
- JSON action plan validation and safety checks.
- Error wrapping and plain messages.
Debugging techniques#
- Log inputs and outputs at each stage.
- Use try/except around external calls and return structured error payloads.
- Inspect extension logs for tool routing and execution outcomes.
Practical notes#
- Naming: Use descriptive, consistent names (e.g., verb_noun).
- Error handling: Fail fast with clear messages; avoid leaking secrets.
- Documentation: Include field descriptions and constraints in schemas.
- Security: Avoid unsafe script patterns; sanitize inputs and limit permissions.
- Performance: Minimize blocking calls; bound resource usage.
Templates and examples#
- Browser action tool:
browser_action_agent - Web search tool:
websearch_agent - Website tool:
website_agent - YouTube tool:
youtube_agent - Gmail tools:
gmail_agent,gmail_send_agent,gmail_list_unread_agent,gmail_mark_read_agent - Calendar tools:
calendar_agent,calendar_create_event_agent - PyJIIT tool:
pyjiit_agent
Security considerations#
- Avoid dangerous script patterns in EXECUTE_SCRIPT actions.
- Validate and constrain inputs to prevent injection.
- Limit tool capabilities to least privilege.
- Sanitize and log only non-sensitive information.
Distribution and maintenance#
- Package tools under tools/
/ with clear module boundaries. - Keep schemas and tool names stable; deprecate gradually.
- Provide CLI entry points for manual testing where applicable.
- Maintain changelogs and update agent builder when adding/removing tools.