Skip to content

Extensions

Extensions add specialized tools to community assistants beyond what YAML can auto-generate. Use extensions when you need external API calls, CLI tool integration, or complex processing logic.

When to Use Extensions

Need Solution
Fetch documentation pages Built-in (auto-generated from documentation config)
Search GitHub issues/PRs Built-in (auto-generated from github config)
Search academic papers Built-in (auto-generated from citations config)
Search code docstrings Built-in (auto-generated from docstrings config)
Search mailing list FAQ Built-in (auto-generated from mailman + faq_generation config)
Call an external validation API Python plugin
Run a CLI tool Python plugin
Connect to an MCP server MCP server extension

Python Plugins

Python plugins are the primary extension mechanism. Each plugin is a Python module containing functions decorated with LangChain's @tool.

Writing a Plugin

Create a tools.py file in your community directory:

# src/assistants/my-tool/tools.py
"""Specialized tools for My Tool community."""

import httpx
from langchain_core.tools import tool


@tool
def validate_config(config_text: str, version: str = "2.0") -> dict:
    """Validate a My Tool configuration file.

    Args:
        config_text: The configuration content to validate.
        version: Schema version to validate against.

    Returns:
        Dict with 'valid' boolean and 'errors' list if invalid.
    """
    try:
        response = httpx.post(
            "https://my-tool.org/api/validate",
            json={"config": config_text, "version": version},
            timeout=15.0,
        )
        response.raise_for_status()
        return response.json()
    except httpx.HTTPError as e:
        return {"valid": False, "errors": [f"Validation service error: {e}"]}


@tool
def search_examples(query: str, limit: int = 5) -> list[dict]:
    """Search the My Tool examples database.

    Args:
        query: Search query describing the desired example.
        limit: Maximum number of results to return.

    Returns:
        List of matching examples with title, description, and code.
    """
    response = httpx.get(
        "https://my-tool.org/api/examples",
        params={"q": query, "limit": limit},
        timeout=10.0,
    )
    response.raise_for_status()
    return response.json().get("results", [])

Plugin Requirements

  1. Use @tool decorator - This is the LangChain standard for tool definitions
  2. Clear docstring - Becomes the tool description the LLM sees; be specific about what the tool does and when to use it
  3. Type hints - All parameters must have type annotations
  4. Return JSON-serializable data - Dicts, lists, strings, numbers, booleans
  5. Handle errors gracefully - Return error information rather than raising exceptions

Registering the Plugin

Reference your plugin module in config.yaml:

extensions:
  python_plugins:
    - module: src.assistants.my-tool.tools
      tools:
        - validate_config
        - search_examples

If tools is omitted, all names exported in the module's __all__ list that are valid tool objects are loaded:

extensions:
  python_plugins:
    - module: src.assistants.my-tool.tools
      # All tools listed in __all__ are loaded

Your module must define __all__ listing the tools to export:

# src/assistants/my-tool/tools.py
__all__ = ["validate_config", "search_examples"]

Examples from Implemented Communities

HED - External API integration:

Tool Purpose External Dependency
validate_hed_string Validate HED annotations hedtools.org REST API
suggest_hed_tags Natural language to HED tags hed-lsp CLI tool
get_hed_schema_versions List available schema versions hedtools.org REST API

BIDS - Specialized knowledge lookup:

Tool Purpose Data Source
lookup_bep Look up BIDS Extension Proposals Synced BEP database

EEGLAB - Community-scoped wrappers:

Tool Purpose Data Source
search_eeglab_code_docs Search MATLAB/Python function docs Synced docstrings database
search_eeglab_faq Search mailing list FAQ entries LLM-generated FAQ database

These tools provide domain-specific functionality that cannot be replicated in YAML configuration alone.

Best Practices

Do:

  • Keep tools focused on a single task
  • Return structured data the LLM can interpret
  • Include usage guidance in the docstring (when to use, expected workflow)
  • Handle network timeouts and errors
  • Log errors for debugging (logging.getLogger(__name__))

Don't:

  • Create tools for documentation retrieval (use the documentation config instead)
  • Include hardcoded secrets (use environment variables)
  • Make tools that produce very long outputs (the LLM has limited context)
  • Raise exceptions to the LLM (return error dicts instead)

Tool Discovery

The CommunityAssistant loads plugin tools during initialization:

  1. Imports the specified module
  2. Filters for functions with the @tool decorator
  3. If specific tools names are listed, only those are loaded
  4. Tools are added to the LLM's tool list alongside auto-generated tools

MCP Servers

MCP (Model Context Protocol) servers provide an alternative extension mechanism: tools served by a separate process instead of a python_plugins module in this repository. This is implemented: src/tools/mcp_client.py discovers a server's tools and wraps each as a LangChain tool, and the NEMAR community uses it in production for its six dataset-discovery tools (see NEMAR Tools).

Every tool name is prefixed with the server's own name, so a server named nemar serving a tool called search_datasets is exposed to the assistant as nemar_search_datasets. Discovery runs against a 20-second budget and is cached for 5 minutes; a server that is unreachable or times out yields no tools and a log line rather than failing assistant startup.

Configuration

extensions:
  mcp_servers:
    # Remote server (connects via URL)
    - name: remote-service
      url: https://mcp.my-tool.org

The McpServer model (src/core/config/community.py) also accepts a command: [...] field for a local, subprocess-started server, and requires exactly one of command or url. Only url (remote, Streamable HTTP) servers are wired up at runtime today: discover_mcp_tools declines a command-only entry explicitly, logs a warning, and returns no tools for it, rather than attempting to spawn a process.

Local vs Remote

Type Config Status
Local command: [...] Accepted by the schema; not implemented in the runtime
Remote url: https://... Implemented; e.g. NEMAR's https://mcp.nemar.org/mcp

Exactly one of command or url must be provided for each server, even though only url currently does anything.

Extension Loading Order

When a CommunityAssistant is created, tools are loaded in this order:

  1. Knowledge tools from YAML config:
    • search_{community}_discussions - GitHub issues/PR search (if github.repos configured)
    • list_{community}_recent - Recent GitHub activity (if github.repos configured)
    • search_{community}_papers - Academic paper search (if citations configured)
    • search_{community}_code_docs - Code docstring search (if docstrings configured)
    • search_{community}_faq - Mailing list FAQ search (if mailman configured)
  2. Documentation retrieval - retrieve_{community}_docs
  3. Page context - fetch_current_page (if enable_page_context: true)
  4. Python plugin tools from extensions.python_plugins
  5. MCP server tools from extensions.mcp_servers

All tools are available to the LLM simultaneously. The system prompt should guide the LLM on when to use each tool.