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

# Tools

> Discover, register, and invoke tools (both backend-managed and locally registered).

`ToolsRepository` is the registry that holds every tool an agent can call. There are two kinds:

* **Backend tools**: connectors and OpenAPI imports configured in the Workbench. Loaded automatically when you `agents.aget(...)` an agent.
* **Local tools**: Python functions you decorate with [`@register_tool`](/developers/sdk-reference/tools#register_tool-classmethod). Registered in-process and merged into the agent's tool list.

You don't usually instantiate `ToolsRepository` directly: accessing `agent.tools` gives you the configured instance for that agent.

```python theme={"dark"}
agent = await agents.aget("agent-123")

# All tools (backend + local) merged into one list
for tool in agent.tools.list:
    print(tool.id, tool.name, tool.is_local)

# Look up by id or name
weather = agent.tools.get_tool_by_id("get_weather")
weather2 = agent.tools.get_tool_by_name("Weather")

# Framework-ready callables (used internally by Backend.aget_args)
fns = agent.tools.functions
```

## Class layout

| Type                       | Reference                                                                                              |
| -------------------------- | ------------------------------------------------------------------------------------------------------ |
| `ToolsRepository`          | This page                                                                                              |
| `Tool`                     | [Tool class](/developers/sdk-reference/tools#tool-class)                                               |
| `@register_tool` decorator | [register\_tool](/developers/sdk-reference/tools#register_tool-classmethod)                            |
| MCP types                  | [MCP](/developers/sdk-reference/tools#mcp-types)                                                       |
| Tool lifecycle hooks       | [`@on_tool_*`](/developers/sdk-reference/decorators#@on_tool_before-/-@on_tool_after-/-@on_tool_error) |

## Constructor

```python theme={"dark"}
ToolsRepository(
    configuration: Optional[Configuration] = None,
    tools: List[Tool] = [],
    agent_graph: Optional[AgentGraph] = None,
    is_async: bool = True,
)
```

| Parameter       | Type            | Default           | Description                                          |
| --------------- | --------------- | ----------------- | ---------------------------------------------------- |
| `configuration` | `Configuration` | `Configuration()` | SDK configuration.                                   |
| `tools`         | `list[Tool]`    | `[]`              | Backend-managed tools to seed the repository.        |
| `agent_graph`   | `AgentGraph`    | `None`            | Owning agent's graph (for schema overrides).         |
| `is_async`      | `bool`          | `True`            | Whether `.functions` should produce async callables. |

In practice, `agents.aget(...)` constructs a `ToolsRepository` for you with all four arguments populated.

## Properties

### `list`

```python theme={"dark"}
agent.tools.list -> list[Tool]
```

A merged list of backend tools and locally-registered tools (de-duplicated by `id`). Each tool has its `Configuration` set and any agent-graph schema overrides applied.

### `functions`

```python theme={"dark"}
agent.tools.functions -> list[Callable[..., Any]]
```

Framework-ready callables for every tool in `.list`. Each callable accepts a single `payload` argument validated against the tool's auto-generated Pydantic schema. The callable's `__name__` is the tool id and its `__doc__` includes a usage example.

This is what `Backend.aget_args` injects into your framework agent's `tools=...` parameter: you rarely need it directly. Useful when binding tools to a custom framework manually.

## Methods

### `get_tool_by_id`

```python theme={"dark"}
agent.tools.get_tool_by_id("get_weather") -> Tool | None
```

Returns the tool with the matching `id`, or `None`.

### `get_tool_by_name`

```python theme={"dark"}
agent.tools.get_tool_by_name("Weather") -> Tool | None
```

Returns the tool with the matching `name` (display name), or `None`.

### `register_tool` (classmethod)

```python theme={"dark"}
ToolsRepository.register_tool(tool: Tool)
```

Adds a tool to the global local registry. Used by the `@register_tool` decorator: you don't usually call this directly.

### `should_sync_local_tools`

```python theme={"dark"}
agent.tools.should_sync_local_tools() -> bool
```

Returns `True` when at least one local tool is marked `should_add_to_graph=True` and hasn't been synced yet.

### `get_local_tools_for_sync`

```python theme={"dark"}
agent.tools.get_local_tools_for_sync() -> list[Tool]
```

Returns local tools awaiting sync (used internally during `agent.aload`).

### `aload_tool_by_id`

```python theme={"dark"}
await agent.tools.aload_tool_by_id("<connector_id>_<operation_id>") -> None
```

Standalone tool loading: looks up a tool by its `<connector_id>_<operation_id>` form and seeds it into the repository. Useful when you want to invoke a single tool without loading an agent.

The id is the connector's UUID and the operation's catalog id joined by a single underscore, for example `"07687ac7-d474-4d24-8ec6-6debac476b00_6a581606eaade0ae8b8f2c9c"`. Both halves come from [List Connector Operations](/api-reference/v1/tools/list-connector-operations).

The sync sibling is `load_tool_by_id`.

## Patterns

### Iterate every tool

```python theme={"dark"}
agent = await agents.aget("agent-123")

for tool in agent.tools.list:
    print(f"- {tool.id}: {tool.description[:60]}")
```

### Bind tools to a custom framework

```python theme={"dark"}
fns = agent.tools.functions   # list of callables, each accepts `payload`

# Pass directly to a framework that expects callables:
my_framework_agent = MyAgent(tools=fns)
```

### Standalone invocation (no agent context)

```python theme={"dark"}
from xpander_sdk import ToolsRepository

repo = ToolsRepository()
await repo.aload_tool_by_id("07687ac7-d474-4d24-8ec6-6debac476b00_6a581606eaade0ae8b8f2c9c")
tool = repo.list[0]

invoke = tool.get_invocation_function(is_async=True)
result = await invoke({"channel": "#general", "text": "Hello"})
```

This pattern is how you call a single platform connector without going through an agent.

## @register\_tool

`@register_tool` turns a Python function into a tool the agent can invoke. The SDK extracts type hints and the docstring to build a schema and description automatically: there's no manual schema writing.

```python theme={"dark"}
from xpander_sdk import register_tool

@register_tool
def calculate_sum(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
```

After this runs (typically at module import), `calculate_sum` is available to every `Agents().aget(...)` call as a local tool with id `"calculate_sum"`.

### Decorator forms

```python theme={"dark"}
@register_tool                                    # plain
def fn(...): ...

@register_tool(add_to_graph=True)                 # with options
def fn(...): ...
```

| Parameter      | Type   | Default | Description                                                                                                                                                      |
| -------------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `add_to_graph` | `bool` | `False` | When `True`, the tool is pushed into the agent's execution graph (so the platform routes calls through it). When `False`, the tool is only available in-process. |

### What it does

1. Inspects the function's parameters and type hints.
2. Builds a Pydantic model (`<FunctionName>Args`) from the parameters: required if no default, optional otherwise.
3. Constructs a `Tool` with:
   * `id` = function name
   * `name` = function name
   * `description` = function docstring
   * `parameters` = generated JSON schema
   * `fn` = the function
   * `is_local = True`
   * `should_add_to_graph = add_to_graph`
4. Registers the tool in `ToolsRepository`'s class-level `_local_tools` list (process-wide singleton).
5. Returns the original function unchanged: `calculate_sum(2, 3)` still works as plain Python.

### Examples

#### Async functions are supported

```python theme={"dark"}
@register_tool
async def fetch_url(url: str) -> str:
    """Fetch the body of a URL."""
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
    return response.text
```

When the agent calls this tool, the SDK awaits the coroutine.

#### Optional parameters

```python theme={"dark"}
@register_tool
def search(query: str, limit: int = 10, region: str = "US") -> list[str]:
    """Search the catalog. Returns matching SKUs."""
    ...
```

Defaults become optional fields in the JSON schema.

#### Push to the agent graph

```python theme={"dark"}
@register_tool(add_to_graph=True)
async def analyze_data(data: list, analysis_type: str) -> dict:
    """Analyze incoming data; returns aggregated metrics."""
    ...
```

`add_to_graph=True` makes the tool visible in the platform's graph view for the next `agents.aget(...)`: the SDK sync runs in the background after `aget` returns.

#### Use the function directly

```python theme={"dark"}
@register_tool
def add(a: int, b: int) -> int:
    """Add two ints."""
    return a + b

# Still callable as a regular function
add(2, 3)   # 5
```

### Visibility rules

The decorator registers tools at module-import time on a process-wide registry. That means:

* Every agent loaded by `Agents().aget(...)` in this process inherits the local tools.
* Tools defined inside a function body register the **first** time the function runs, but stay registered for the rest of the process's lifetime.
* If you `del` or rename a function after decoration, the registered `Tool` keeps its captured reference (the `fn` attribute): it doesn't unregister itself.

For per-invocation tools, use `Backend.aget_args(tools=[fn])` instead: that adds callables to one specific `aget_args` call without polluting the global registry.

### Schema details

The generated schema uses Pydantic's `model_json_schema(mode="serialization")`. Parameter names, types, and defaults map directly:

```python theme={"dark"}
@register_tool
def book_flight(origin: str, destination: str, date: str = "tomorrow") -> dict:
    """Book a flight."""
    ...
```

generates roughly:

```json theme={"dark"}
{
  "properties": {
    "origin": {"type": "string", "title": "Origin"},
    "destination": {"type": "string", "title": "Destination"},
    "date": {"type": "string", "title": "Date", "default": "tomorrow"}
  },
  "required": ["origin", "destination"],
  "title": "BookFlightArgs",
  "type": "object"
}
```

The agent's LLM sees this schema; the docstring becomes the tool's description. Write docstrings the way you'd write a tool description: that's exactly what the LLM reads.

### Related

* [`Tool` class](/developers/sdk-reference/tools#tool-class): what the decorator creates.
* [`@on_tool_before` / `@on_tool_after` / `@on_tool_error`](/developers/sdk-reference/decorators#@on_tool_before-/-@on_tool_after-/-@on_tool_error): lifecycle hooks around any tool invocation.

## Tool class

`Tool` represents a single callable an agent can invoke: a connector operation, an OpenAPI endpoint, an MCP tool, or a local Python function registered with `@register_tool`. You normally get one from `agent.tools.list` or `agent.tools.get_tool_by_id(...)`.

```python theme={"dark"}
agent = await agents.aget("agent-123")
tool = agent.tools.get_tool_by_id("get_weather")

print(tool.id, tool.name, tool.description)
print(tool.parameters)        # raw JSON schema
print(tool.schema)            # Pydantic model class generated from parameters
```

### Attributes

| Attribute             | Type                           | Description                                                                     |
| --------------------- | ------------------------------ | ------------------------------------------------------------------------------- |
| `id`                  | `str`                          | Tool identifier (used as the function name when invoked by the LLM).            |
| `name`                | `str`                          | Display name.                                                                   |
| `method`              | `str`                          | HTTP method for remote invocation (`"POST"`, `"GET"`, …).                       |
| `path`                | `str`                          | Endpoint path.                                                                  |
| `description`         | `str`                          | Human-readable description (used by the LLM to decide when to call).            |
| `parameters`          | `dict`                         | JSON schema for the input payload.                                              |
| `is_local`            | `bool`                         | `True` when the tool is a `@register_tool` Python function.                     |
| `is_synced`           | `bool`                         | For local tools: `True` after the tool has been pushed to the platform's graph. |
| `is_standalone`       | `bool`                         | `True` when loaded via `aload_tool_by_id` (no agent context).                   |
| `should_add_to_graph` | `bool`                         | Whether this tool should be added to agent execution graphs.                    |
| `connector_id`        | `str \| None`                  | For platform tools: the connector id.                                           |
| `operation_id`        | `str \| None`                  | For platform tools: the operation id.                                           |
| `schema_overrides`    | `AgentGraphItemSchema \| None` | Per-agent input/output schema overrides.                                        |
| `fn`                  | `Callable \| None`             | The Python function for local tools. Excluded from serialization.               |
| `configuration`       | `Configuration`                | SDK configuration.                                                              |

### Computed properties

#### `schema`

```python theme={"dark"}
tool.schema -> type[BaseModel]
```

A dynamically-generated Pydantic model class derived from `tool.parameters`. The model's name is `{ToolIdPascalCase}PayloadSchema`. If the agent has schema overrides for this tool, they're applied here.

```python theme={"dark"}
WeatherSchema = tool.schema
WeatherSchema(city="NYC")          # validates input
```

#### `payload_schema`

```python theme={"dark"}
tool.payload_schema -> type[BaseModel]
```

A wrapper schema with a single `payload` field whose type is `tool.schema`. Useful when a framework expects every tool call to be wrapped in `{"payload": {...}}`.

```python theme={"dark"}
PayloadModel = tool.payload_schema
PayloadModel(payload={"city": "NYC"})
```

### Methods

#### `ainvoke` / `invoke`

Invoke the tool. Validates the payload against `tool.schema`, executes locally (`is_local=True`) or remotely, and returns a `ToolInvocationResult`.

```python theme={"dark"}
result = await tool.ainvoke(
    agent_id="agent-123",
    payload={"city": "Tokyo"},
)
print(result.result, result.is_success)
```

| Parameter           | Type            | Required | Default       | Description                                                                                                                                                            |
| ------------------- | --------------- | -------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent_id`          | `str`           | Yes      | –             | Agent invoking the tool.                                                                                                                                               |
| `payload`           | `Any`           | Yes      | –             | Tool input. Validated against `tool.schema`.                                                                                                                           |
| `agent_version`     | `str`           | No       | `None`        | Pin to a specific agent version.                                                                                                                                       |
| `payload_extension` | `dict`          | No       | `{}`          | Extra fields deep-merged into the payload (headers, auth, etc.).                                                                                                       |
| `configuration`     | `Configuration` | No       | Tool's config | Override SDK configuration.                                                                                                                                            |
| `task_id`           | `str`           | No       | `None`        | Associate with a task (for activity logging).                                                                                                                          |
| `tool_call_id`      | `str`           | No       | `None`        | Correlation id matching the LLM's tool-call id.                                                                                                                        |
| `report_activity`   | `bool`          | No       | `False`       | Push `ToolCallRequest` / `ToolCallResult` events to the task activity log. Set this when invoking outside the framework's tool hook (so events aren't double-emitted). |

Returns a `ToolInvocationResult` (see below). Sync sibling: `tool.invoke(...)`.

`ainvoke` runs the configured [tool lifecycle hooks](/developers/sdk-reference/decorators#@on_tool_before-/-@on_tool_after-/-@on_tool_error) (`@on_tool_before` / `@on_tool_after` / `@on_tool_error`) around the call.

#### `acall_remote_tool` / `call_remote_tool`

Lower-level: makes the API call without local-tool fallback or hook execution. Used internally by `ainvoke` when `is_local=False`. Use directly only for advanced cases (preflight checks, custom invocation pipelines).

```python theme={"dark"}
result = await tool.acall_remote_tool(
    agent_id="agent-123",
    payload={"city": "Tokyo"},
)
```

#### `agraph_preflight_check` / `graph_preflight_check`

Asks the platform to validate a hypothetical tool invocation without executing it. Used internally for graph routing.

#### `get_invocation_function`

```python theme={"dark"}
tool.get_invocation_function(
    is_async: bool = False,
    configuration: Optional[Configuration] = None,
) -> Callable
```

Factory that returns a pre-configured invocation function bound to this tool's connector + configuration. Use for standalone invocation (no agent context):

```python theme={"dark"}
invoke = tool.get_invocation_function(is_async=True)
result = await invoke({"city": "NYC"})   # ToolInvocationResult
```

This skips agent-id and task-id binding: the underlying call hits the connector via `connector_id_operation_id` and returns the raw response wrapped in `ToolInvocationResult`. Use it after `ToolsRepository.aload_tool_by_id(...)` to call a single connector without an agent.

### `ToolInvocationResult`

The shape returned by `ainvoke` and the standalone invocation function.

| Field          | Type          | Description                    |
| -------------- | ------------- | ------------------------------ |
| `tool_id`      | `str`         | The tool's id.                 |
| `tool_call_id` | `str \| None` | The correlation id, if passed. |
| `task_id`      | `str \| None` | The task id, if passed.        |
| `payload`      | `Any`         | The payload that was sent.     |
| `status_code`  | `int`         | HTTP status (200 by default).  |
| `result`       | `Any`         | The tool's response.           |
| `is_success`   | `bool`        | `True` on 2xx.                 |
| `is_error`     | `bool`        | `True` on failure.             |
| `is_local`     | `bool`        | `True` for local tools.        |

`is_success` and `is_error` are mutually exclusive in normal operation: check `is_error` first to branch on failure cases.

### Examples

#### Inspect tool schema

```python theme={"dark"}
tool = agent.tools.get_tool_by_id("get_weather")

# Pydantic schema (from parameters)
schema = tool.schema
print(schema.model_json_schema())

# Validate a payload before sending
schema.model_validate({"city": "NYC"})   # raises ValidationError on bad shape
```

#### Per-call configuration override

```python theme={"dark"}
from xpander_sdk import Configuration

custom_config = Configuration(api_key="other-key", organization_id="other-org")

result = await tool.ainvoke(
    agent_id="agent-123",
    payload={"city": "Berlin"},
    configuration=custom_config,
)
```

#### Reporting activity for non-Agno callers

When invoking a tool outside an Agno-driven flow (e.g. from your own agent loop), set `report_activity=True` so the call shows up in the task's activity log:

```python theme={"dark"}
result = await tool.ainvoke(
    agent_id="agent-123",
    task_id=task.id,
    payload={...},
    report_activity=True,
)
```

Inside Agno-driven flows, leave it at `False`: the Agno hook reports the call automatically and you'd otherwise double-emit events.

## MCP types

The Model Context Protocol (MCP) lets agents connect to external tool servers. The SDK exposes `MCPServerDetails` for declaring servers per task, and a few enums for the supported transports and auth types.

```python theme={"dark"}
from xpander_sdk import MCPServerDetails, MCPServerType, MCPServerAuthType

server = MCPServerDetails(
    type=MCPServerType.Remote,
    name="internal-docs",
    url="https://mcp.internal.acme.com",
    auth_type=MCPServerAuthType.APIKey,
    api_key="...",
)
```

You typically pass a list of these to `Agent.acreate_task(mcp_servers=[...])` to attach servers for a single run; servers configured in the Workbench are added automatically.

### `MCPServerDetails`

```python theme={"dark"}
MCPServerDetails(
    type: MCPServerType = MCPServerType.Remote,
    name: Optional[str] = None,
    command: Optional[str] = None,
    url: Optional[str] = None,
    transport: MCPServerTransport = MCPServerTransport.HTTP_Transport,
    auth_type: MCPServerAuthType = MCPServerAuthType._None,
    api_key: Optional[str] = None,
    use_secrets_manager: bool = False,
    client_id: Optional[str] = None,
    client_secret: Optional[str] = None,
    headers: Dict = {},
    env_vars: Dict = {},
    allowed_tools: List[str] = [],
    additional_scopes: List[str] = [],
    share_user_token_across_other_agents: bool = True,
)
```

| Field                                  | Type                 | Description                                                                                  |
| -------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------- |
| `type`                                 | `MCPServerType`      | `Local` (stdio) or `Remote` (HTTP/SSE).                                                      |
| `name`                                 | `str`                | Server name (used in logs and the dashboard).                                                |
| `command`                              | `str`                | For `Local` servers: the binary to run.                                                      |
| `url`                                  | `str`                | For `Remote` servers: the server endpoint.                                                   |
| `transport`                            | `MCPServerTransport` | `STDIO`, `SSE`, or `HTTP_Transport` (default).                                               |
| `auth_type`                            | `MCPServerAuthType`  | `_None`, `APIKey`, `OAuth2`, or `CustomHeaders`.                                             |
| `api_key`                              | `str`                | For `APIKey` auth.                                                                           |
| `use_secrets_manager`                  | `bool`               | Resolve the API key from the platform's secrets manager.                                     |
| `client_id` / `client_secret`          | `str`                | For `OAuth2`.                                                                                |
| `headers`                              | `dict`               | Custom headers (used with `CustomHeaders` auth or always-on).                                |
| `env_vars`                             | `dict`               | Env vars to set when launching `Local` servers.                                              |
| `allowed_tools`                        | `list[str]`          | Whitelist of tools to expose from this server. Empty = all.                                  |
| `additional_scopes`                    | `list[str]`          | Extra OAuth scopes.                                                                          |
| `share_user_token_across_other_agents` | `bool`               | When `True`, end users only authenticate once even when multiple agents use the same server. |

### Enums

#### `MCPServerType`

| Value                 | Meaning                                                                |
| --------------------- | ---------------------------------------------------------------------- |
| `Local` (`"local"`)   | Run a local stdio process. Requires `command` and optional `env_vars`. |
| `Remote` (`"remote"`) | Connect to a remote HTTP / SSE server. Requires `url`.                 |

#### `MCPServerTransport`

| Value                                  | Meaning                                      |
| -------------------------------------- | -------------------------------------------- |
| `STDIO` (`"stdio"`)                    | Standard input/output (Local servers).       |
| `SSE` (`"sse"`)                        | Server-sent events.                          |
| `HTTP_Transport` (`"streamable-http"`) | HTTP streaming (default for remote servers). |

#### `MCPServerAuthType`

| Value                                | Meaning                                                                        |
| ------------------------------------ | ------------------------------------------------------------------------------ |
| `_None` (`"none"`)                   | No auth.                                                                       |
| `APIKey` (`"api_key"`)               | Static API key in `api_key`.                                                   |
| `OAuth2` (`"oauth2"`)                | OAuth2 with `client_id` / `client_secret`. Triggers `auth_event`s during runs. |
| `CustomHeaders` (`"custom_headers"`) | Send `headers` as auth.                                                        |

### Examples

#### Local server (stdio)

```python theme={"dark"}
local_server = MCPServerDetails(
    type=MCPServerType.Local,
    name="my-local-mcp",
    command="python -m my_mcp_module",
    transport=MCPServerTransport.STDIO,
    env_vars={"DEBUG": "1"},
)
```

#### Remote with API key

```python theme={"dark"}
remote_server = MCPServerDetails(
    type=MCPServerType.Remote,
    name="docs-mcp",
    url="https://mcp.docs.acme.com",
    transport=MCPServerTransport.HTTP_Transport,
    auth_type=MCPServerAuthType.APIKey,
    api_key="sk-...",
)
```

#### Remote with OAuth2

```python theme={"dark"}
oauth_server = MCPServerDetails(
    type=MCPServerType.Remote,
    name="github-mcp",
    url="https://mcp.github.com",
    auth_type=MCPServerAuthType.OAuth2,
    client_id="...",
    client_secret="...",
    additional_scopes=["repo", "read:user"],
)
```

OAuth2 servers fire `auth_event`s during a run when end-user authorization is required. Register an [`@on_auth_event`](/developers/sdk-reference/decorators#@on_auth_event) handler to route the OAuth URL to your UI.

#### Per-task server attachment

```python theme={"dark"}
task = await agent.acreate_task(
    prompt="Search our internal docs for the Q4 roadmap.",
    mcp_servers=[remote_server],
)
```

These are appended to the agent's persistent MCP config for this task only.

#### Limit which tools are exposed

```python theme={"dark"}
restricted = MCPServerDetails(
    type=MCPServerType.Remote,
    name="github-mcp",
    url="https://mcp.github.com",
    auth_type=MCPServerAuthType.OAuth2,
    client_id="...",
    client_secret="...",
    allowed_tools=["search_repositories", "get_issue"],
)
```

Only `search_repositories` and `get_issue` will be exposed to the agent: every other tool the server offers is hidden.

### OAuth response types (for `@on_auth_event` payloads)

When OAuth flows fire, `event.data` is shaped like one of these:

| Type                       | Field                        | Description                |
| -------------------------- | ---------------------------- | -------------------------- |
| `MCPOAuthGetTokenResponse` | `type: MCPOAuthResponseType` | Discriminator.             |
|                            | `data: ...`                  | One of the variants below. |

| Variant                                 | Fields                             |
| --------------------------------------- | ---------------------------------- |
| `MCPOAuthGetTokenLoginRequiredResponse` | `url`, `server_url`, `server_name` |
| `MCPOAuthGetTokenTokenReadyResponse`    | `access_token`                     |
| `MCPOAuthGetTokenGenericResponse`       | `message`                          |

`MCPOAuthResponseType` values: `not_supported`, `login_required`, `token_issue`, `token_ready`.

Use these in your auth handler to display the right state to the end user (login URL vs. "we're working on it" vs. success).
