How to Define Advanced Tool Descriptions with Google-Style Args in Docstrings

Use Google-style Args: sections in function docstrings to automatically populate JSON Schema descriptions for tools decorated with @tool in the Needle framework.

The needle framework transforms Python functions into discoverable agent tools by generating JSON schemas from type annotations and docstrings. By following Google-style conventions, you can attach rich, user-friendly descriptions to every parameter without writing additional boilerplate. This article explains how the schema generation works, where to find the core implementation, and how to combine docstring descriptions with Pydantic-style Field constraints for maximum expressiveness.


How Docstring Parsing Works in Needle

The automatic schema generation relies on two key functions in needle/agent/tools.py.

The _parse_doc Helper

The _parse_doc function (lines 94–107) scans a function's docstring and extracts two pieces of information:

  1. The free-form description (everything before the Args: section)
  2. A mapping of argument names to their descriptions from the Google-style block

It recognizes multiple section headers: Args:, Arguments:, Parameters:, or Params:.

The build_schema Integration

During schema construction (lines 116–125), build_schema merges these extracted descriptions into the final JSON schema:

schema["description"] = arg_docs[name]  # Merges docstring description into parameter

This happens automatically when you apply the @tool decorator—the parsed descriptions are attached to each parameter's schema without manual intervention.


Writing Effective Google-Style Args Sections

A properly formatted docstring follows this pattern:

"""
Brief description of what the function does.

Args:
    param_name: Description of the parameter.
        Can span multiple lines with consistent indentation.
    another_param: Second parameter description.
"""

The colon after each parameter name is optional but conventional. Indentation must be consistent for multi-line descriptions.


Code Examples

Basic Tool with Argument Descriptions

from needle.agent.tools import tool

@tool
def echo(message: str, repeat: int = 1) -> str:
    """
    Echoes a message a number of times.

    Args:
        message: The text to be echoed.
        repeat: How many times to repeat the message. Defaults to 1.
    """
    return " ".join([message] * repeat)

The generated schema automatically includes:

  • "description": "The text to be echoed." for message
  • "description": "How many times to repeat the message. Defaults to 1." for repeat
  • The default value 1 is captured from the function signature

Advanced Tool with Field Constraints and Docstrings

from needle.agent.tools import tool, Field

@tool
def resize(
    width: int = Field(ge=1, description="Target width in pixels."),
    height: int = Field(ge=1, description="Target height in pixels."),
    keep_aspect: bool = Field(default=True, description="Preserve aspect ratio.")
) -> None:
    """
    Resizes an image to the specified dimensions.

    Args:
        width: Desired image width.
        height: Desired image height.
        keep_aspect: If True, adjusts the other dimension to maintain the original aspect ratio.
    """
    # implementation omitted

This pattern combines two description layers:

Source Purpose
Field(description=...) Machine-readable schema annotation with validation rules (ge=1)
Docstring Args: section Human-readable explanation consumed by agent systems

Both descriptions are merged into the final schema, with Field metadata providing structural constraints and docstring text offering narrative context.

Inspecting the Generated Schema

Every @tool-decorated function stores its schema in the _needle_tool attribute:

print(echo._needle_tool)

Expected output:

{
  "name": "echo",
  "parameters": {
    "type": "object",
    "properties": {
      "message": {"type": "string", "description": "The text to be echoed."},
      "repeat": {"type": "integer", "description": "How many times to repeat the message. Defaults to 1.", "default": 1}
    },
    "required": ["message"]
  },
  "description": "Echoes a message a number of times."
}

Source File Reference

The following files in the cactus-compute/needle repository implement and expose this functionality:

  • needle/agent/tools.py — Core utilities for parsing Google-style docstrings, mapping Python types to JSON schema, and attaching schemas via @tool
  • needle/agent/__init__.py — Public export of the tool decorator for from needle.agent import tool
  • needle/cli.py — CLI tool registration and discovery demonstrating real-world usage
  • tests/test_tools.py — Unit tests verifying schema generation from docstrings

Summary

  • Google-style Args: sections in docstrings automatically populate parameter descriptions in generated JSON schemas
  • The _parse_doc function in tools.py extracts these descriptions and build_schema merges them into the final schema
  • Combine Field objects with docstrings to layer validation constraints on top of human-readable descriptions
  • Access any tool's schema via the _needle_tool attribute for debugging or inspection
  • No extra code is required—the @tool decorator handles all extraction and attachment transparently

Frequently Asked Questions

What section headers does Needle recognize for argument documentation?

Needle accepts Args:, Arguments:, Parameters:, or Params: as valid section headers. All are treated identically by the _parse_doc parser in needle/agent/tools.py.

Can I use both Field descriptions and docstring Args for the same parameter?

Yes. Both descriptions are preserved and merged into the final schema. The Field description typically carries validation metadata, while the docstring description provides expanded human-readable context. Agent systems may use either or both depending on their needs.

Does the docstring parser handle type annotations in the Args section?

No. Needle extracts type information from Python's native type hints in the function signature, not from docstring text. The Args: section should contain only parameter names and descriptions—types are inferred from annotations like message: str.

Where does the function-level description come from?

The overall tool description comes from the text in the docstring before the Args: section. Everything above the first recognized argument header becomes the tool's top-level description in the generated schema.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →