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

# Using Task-Specific Tools

> Define tools that are specific to individual tasks

## Goals

* Understand the difference between shared tools and task-specific tools
* Use `@tool(shared=False)` for tools that should only be available within a session
* Use `list_task_tools()` for fully dynamic tools generated based on the current task

## Prerequisites

* An OpenReward [account](https://openreward.ai/)
* An OpenReward [API key](https://openreward.ai/keys)
* Completion of the [Your First Environment](/environments/your-first-environment) tutorial

## Introduction

By default, all methods decorated with `@tool` are **shared** — they are returned by `environment.list_tools()` and are available to any client before a session even starts. This works well for tools like `bash` or `read_file` that are the same regardless of the task.

But some environments need to present different tool interfaces depending on the task. For example, an environment that tests an agent's ability across multiple different configurations might give the agent a `bash` tool for some tasks, a `select_option` tool for multiple-choice tasks, and a completely different set of tools for other task types. The tool interface itself is part of what varies across tasks.

Task-specific tools solve this by making tools available only through `session.list_tools()`, which requires an active session with a task loaded. This lets you vary the set of tools an agent sees on a per-task basis.

## Using `@tool(shared=False)`

The simplest way to create a task-specific tool is to pass `shared=False` to the `@tool` decorator. These tools are not returned by `environment.list_tools()` but are returned by `session.list_tools()`.

```python theme={null}
from openreward.environments import Environment, tool, ToolOutput, TextBlock
from pydantic import BaseModel, Field

class SearchParams(BaseModel):
    query: str = Field(..., description="Search query")

class SubmitParams(BaseModel):
    answer: str = Field(..., description="Your answer")

class ToolVariantEnvironment(Environment):
    # Shared tool — returned by environment.list_tools()
    @tool
    async def submit_answer(self, params: SubmitParams) -> ToolOutput:
        """Submit the final answer"""
        correct = params.answer.strip() == self.task_spec["answer"]
        return ToolOutput(
            blocks=[TextBlock(text="Correct!" if correct else "Incorrect.")],
            reward=1.0 if correct else 0.0,
            finished=True,
        )

    # Task-specific tool — only returned by session.list_tools()
    # Some tasks give the agent a search tool, others don't
    @tool(shared=False)
    async def search(self, params: SearchParams) -> ToolOutput:
        """Search a knowledge base"""
        results = search_kb(params.query)  # your own search implementation
        return ToolOutput(
            blocks=[TextBlock(text=results)],
        )

    @classmethod
    def list_splits(cls):
        return ["train"]

    @classmethod
    def list_tasks(cls, split):
        return [
            {"question": "What is the capital of France?", "answer": "Paris", "tools": ["search"]},
            {"question": "What is 2+2?", "answer": "4", "tools": []},
        ]

    def get_prompt(self):
        return [TextBlock(text=self.task_spec["question"])]
```

In this example, `submit_answer` is shared and always visible. The `search` tool is task-specific — it only appears when a session is active. Combined with `list_task_tools()` (below), you can control exactly which task-specific tools appear for each task.

## Using `list_task_tools()`

For fully dynamic tools whose definitions depend on the task, override the `list_task_tools()` instance method. This lets you generate tool specifications at runtime based on `self.task_spec` — controlling exactly which tools the agent sees for each task.

```python theme={null}
from openreward.environments import Environment, tool, ToolOutput, TextBlock
from openreward.environments.types import ListToolsOutput, ToolSpec
from pydantic import BaseModel, Field

class SearchParams(BaseModel):
    query: str = Field(..., description="Search query")

class SelectOptionParams(BaseModel):
    option: str = Field(..., description="Selected option (A, B, C, or D)")

class SubmitParams(BaseModel):
    answer: str = Field(..., description="Your answer")

class MultiInterfaceEnvironment(Environment):
    """An environment that presents different tool interfaces per task."""

    @tool
    async def submit_answer(self, params: SubmitParams) -> ToolOutput:
        """Submit the final answer"""
        correct = params.answer.strip() == self.task_spec["answer"]
        return ToolOutput(
            blocks=[TextBlock(text="Correct!" if correct else "Incorrect.")],
            reward=1.0 if correct else 0.0,
            finished=True,
        )

    def list_task_tools(self) -> ListToolsOutput:
        """Present different tools depending on the task type."""
        tools = []
        task_type = self.task_spec.get("type")

        if task_type == "multiple_choice":
            tools.append(ToolSpec(
                name="select_option",
                description="Select an answer option (A, B, C, or D)",
                input_schema=SelectOptionParams.model_json_schema(),
            ))
        elif task_type == "open_book":
            tools.append(ToolSpec(
                name="search",
                description="Search a knowledge base for relevant information",
                input_schema=SearchParams.model_json_schema(),
            ))

        return ListToolsOutput(tools=tools)

    @classmethod
    def list_splits(cls):
        return ["train"]

    @classmethod
    def list_tasks(cls, split):
        return [
            {"type": "multiple_choice", "question": "Capital of France?", "answer": "B"},
            {"type": "open_book", "question": "Explain the photoelectric effect.", "answer": "..."},
            {"type": "closed_book", "question": "What is 2+2?", "answer": "4"},
        ]

    def get_prompt(self):
        return [TextBlock(text=self.task_spec["question"])]
```

In this example, the agent sees different tool interfaces depending on the task:

* **Multiple-choice tasks**: `submit_answer` + `select_option`
* **Open-book tasks**: `submit_answer` + `search`
* **Closed-book tasks**: `submit_answer` only

Note that `list_task_tools()` is an **instance method** (not a classmethod) because it needs access to `self.task_spec` to determine which tools to generate.

When `list_task_tools()` is used, the returned tools are combined with any `@tool(shared=False)` tools and all shared tools when a client calls `session.list_tools()`.

For a real-world example of this pattern, see [GeneralReasoning/toolathon-gym](https://openreward.ai/GeneralReasoning/toolathon-gym) — an environment that presents different tool configurations per task to test agents across varied interfaces.

## How Clients Access Task-Specific Tools

The SDK provides two different `list_tools()` methods depending on whether you have a session:

```python theme={null}
from openreward import OpenReward

client = OpenReward(api_key="your-api-key")
environment = client.environments.get(name="username/my-environment")

# Environment-level: returns shared tools only
shared_tools = environment.list_tools()
print(f"Shared tools: {[t.name for t in shared_tools]}")
# → Shared tools: ['submit_answer']

# Session-level: returns shared tools + task-specific tools
with environment.session(split="train", index=0) as session:
    all_tools = session.list_tools()
    print(f"All tools: {[t.name for t in all_tools]}")
    # → All tools: ['submit_answer', 'select_option']
```

Under the hood:

* `environment.list_tools()` calls the `/tools` endpoint, which returns only shared tools
* `session.list_tools()` calls the `/task_tools` endpoint, which returns shared tools combined with task-specific tools (both `@tool(shared=False)` and those from `list_task_tools()`)

## Next Steps

<Card title="Using Toolsets" icon="toolbox" href="/environments/using-toolsets">
  Create reusable tool collections and compose them into environments
</Card>
