> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sudoiq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Register server tools

> End-to-end guide for registering tool schemas and handling tool calls

Tools have two parts that must stay aligned:

* **Server registry**: JSON schema metadata so models know tool names and arguments.
* **App-defined tool**: your code that runs when the model asks to call that tool.

This guide covers both the **registration** step and the **runtime handler** contract for SSE and webhook flows.

## Register one tool

This example defines a `parse_pdf` tool with a single required string argument `file_path`.

<CodeGroup dropdown>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  from agentsapi.models.tools import ToolDefinition, ParametersSchema, ParameterProperty

  definitions = [
      ToolDefinition(
          name="parse_pdf",
          namespace="main",
          description="Extract text from a PDF given file_path.",
          schema_data=ParametersSchema(
              properties={
                  "file_path": ParameterProperty(
                      type="string",
                      description="Reference from run-input store or path.",
                  ),
              },
              required=["file_path"],
          ),
      ),
  ]

  await client.tools.register_tools(definitions)
  ```

  ```typescript Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // Coming soon — POST /api/tools/register with the same JSON body.
  export {};
  ```

  ```bash CLI theme={"theme":{"light":"github-light","dark":"github-dark"}}
  # Save definitions to tools.json, then:
  agentservice tools push -f tools.json
  ```
</CodeGroup>

## List tools

<CodeGroup dropdown>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  page = await client.tools.get_tools(namespace="main", limit=50)
  ```

  ```typescript Node.js theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // Coming soon
  export {};
  ```

  ```bash CLI theme={"theme":{"light":"github-light","dark":"github-dark"}}
  agentservice tools list
  ```
</CodeGroup>

Use listing to confirm registration or to debug missing-tool errors during runs.

## App-defined tool contract

For Python integrations, handlers use this signature:

* **Signature:** `async def tool(args: dict, agent_id: str, task_id: int) -> str`
* **Return:** a **string**. For structured output, return JSON text (for example `json.dumps({...})`).

Why all three inputs matter:

* **`args`**: model-supplied arguments for that tool call.
* **`agent_id`**: which workflow invoked the tool (lets one handler support multiple agents).
* **`task_id`**: the specific run id for logging, tracing, and run-scoped lookups.

## SSE runtime path

For streaming runs, register handlers with `set_tools` before `execute_agent` / `run_agent`. The client invokes them automatically when the run pauses for tool calls.

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
import json

async def parse_pdf(args: dict, agent_id: str, task_id: int) -> str:
    path = (args or {}).get("file_path", "")
    # ... read the file reference and extract text ...
    return json.dumps({"text": "..."})

client.set_tools({
    "parse_pdf": (None, parse_pdf),
})
```

## Webhook runtime path

If you run with webhooks, parse webhook payloads and submit tool responses explicitly:

1. `tool_results = await client.process_webhook_tools(request, tools_map)`
2. `await client.send_tool_responses(request.agent_id, request.task_id, tool_results.results)`

Minimal sketch:

```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
from agentsapi import AgentGraphRunStatusResponse

async def handle_agent_webhook(body: dict, client):
    request = AgentGraphRunStatusResponse.model_validate(body)
    if request.status != "awaiting_tool_calls":
        return {"ok": True}

    tool_results = await client.process_webhook_tools(request, {
        "parse_pdf": (None, parse_pdf),
    })
    await client.send_tool_responses(
        request.agent_id,
        request.task_id,
        tool_results.results,
    )
    return {"ok": True, "status": "tool_responses_sent"}
```

Do not use polling loops to discover tool calls; use SSE or webhooks as documented.

## See also

* [Async runs and tool calls](/agents/sse-and-local-tools)
* [Webhooks for long-duration agents](/agents/webhooks)
* [PDF demo tutorial](/tutorials/pdf-demo-app)
