---
updatedAt: 2026-08-19T07:59:50.000Z
agentTools:
  projectIndex: https://developer.ixon.cloud/llms.txt
---

# How to make a Cloud Function agentic

Expose a Cloud Function as an agent tool, and design its parameters, output and errors so the agent calls the right tool with the right arguments.

Cloud functions can be exposed as tools to an AI agent. This document explains the requirements, design guidelines, and patterns for writing agent-friendly cloud functions.

# Enabling agent access

To expose a cloud function as an agent tool, use `@FunctionContext.expose(is_agentic=True)`:

```python
@FunctionContext.expose(is_agentic=True)
def my_function(context: FunctionContext, ...) -> ...:
    """
    Clear description of what this function does and when to use it.
    """
    ...
```

Without `is_agentic=True`, the function is still exposed as a regular cloud function endpoint but will not be registered as an agent tool.

# Requirements

All four of the following are required for the agent to call the function correctly and interpret the response.

## 1. Typed input and output

Use Pydantic models for all input parameters and return types. The `expose` decorator automatically derives the JSON schema from these types, which the agent uses to understand how to call the function and interpret its response.

Manually providing `input_schema` or `output_schema` is **possible but discouraged**: it bypasses schema validation and makes maintenance harder.

## 2. A meaningful docstring and verbose function name

The function's name and docstring becomes the tool description **visible to the agent**. It must clearly explain:

* What the function does
* When to use it (and when *not* to — point to alternative functions if relevant)
* What each parameter means if it is not self-evident

## 3. Structured, self-describing output

The agent cannot apply UI-side transformations such as unit conversions, formatting, or factor corrections. Return data exactly **as it should be communicated** to the user.

Always use `ErrorResponse` for failure cases so the agent can reliably distinguish between success and failure. For success, return the typed data directly — no wrapper needed.

```python
# Unstructured return: the agent cannot infer the shape and errors carry no context.
def get_value(...) -> dict[str, object]:
    if not valid:
        return []  # caller has no idea what went wrong
    return result  # caller has no idea what structure to expect


# Prefer a typed return annotation so the agent knows the exact response schema.
# Add a descriptive error so the agent can self-correct or surface a meaningful message to the user.
def get_value(...) -> MyResultModel | ErrorResponse:
    if not valid:
        return ErrorResponse(
            message="Tag 'foo' not found. Valid tag IDs are: ['tag1', 'tag2']"
        ).model_dump(mode="json", by_alias=True)
    return result.model_dump(mode="json", by_alias=True)
```

<Callout icon="⚠️" theme="warn">
  ### Please note

  The IXON SDK does not automatically serialize Pydantic models. Always call `.model_dump(mode="json", by_alias=True)` on every return value — including `ErrorResponse` — to produce a **JSON-serialisable dict**.
</Callout>

## 4. Informative error responses

Error messages must be **actionable**. Instead of a generic `"Internal error"`, tell the agent what went wrong and how to recover:

```json
{"success": false, "message": "Invalid tag_id 'foo'. Valid tag IDs are: ['tag1', 'tag2']"}
```

# Design guidelines

Not every existing Cloud Function is a good agent tool: many are written to **serve specific UI components** with inputs and outputs tailored to that component's behaviour. Consider creating a dedicated agentic method when:

* The existing function requires **too many inputs** that the agent cannot reasonably know upfront.
* The output format depends on **UI-side processing** (factors, labels, formatting).
* The failure modes are **not communicated clearly enough** for the agent to self-correct.

A good agentic function has:

| Property      | Guidance                                                                          |
| ------------- | --------------------------------------------------------------------------------- |
| **Inputs**    | Fewer, higher-level parameters with sensible defaults                             |
| **Outputs**   | Verbose, human-readable, no post-processing required                              |
| **Errors**    | Specific messages that allow the agent to retry or ask the user for clarification |
| **Docstring** | Explains intent, typical use cases, and relationships to sibling tools            |

## Examples: Settings Tracking

The following examples are taken from the Settings Tracking cloud function. Each pair shows an existing UI-oriented function alongside its agent-friendly counterpart.

## Example 1: latest setting values

**UI-oriented function** (not agentic):

```python
@FunctionContext.expose
def get(
    context: FunctionContext,
    tags: list[DataTagPublicId],
    range_from: UnixTimestampMillis | None = None,
    range_to: UnixTimestampMillis | None = None,
) -> dict[str, object]:
    """
    Get the Settings Tracking items for given tags in a time range.
    """
    ...
```

This function requires the caller to know all tag IDs upfront and uses Unix millisecond timestamps, which are not natural for an agent to produce or reason about.

**Agent-friendly function** (`is_agentic=True`):

```python
@FunctionContext.expose(is_agentic=True)
def get_latest_changes(
    context: FunctionContext,
    since_days: int = 30,
) -> list[SettingChange] | ErrorResponse:
    """
    Get the latest (most recent) setting value for each tag within the last {since_days} days.

    Returns at most one element per tag_id — the current value and what it changed from.
    Tags with no changes in the window are excluded.

    Use this when you need: "what are the current settings?" or
    "what did each setting change to recently?".
    For "how many times did the setting change?" or full change history,
    use get_settings_history instead.
    """
    ...
```

Key differences:

* A single optional `since_days` parameter replaces three required low-level inputs.
* Returns a typed `list[SettingChange]` instead of an untyped `dict`.
* The docstring disambiguates this tool from `get_settings_history`, helping the agent pick the right one.

## Example 2: full change history

**UI-oriented function** (not agentic):

```python
@FunctionContext.expose
def get_details(
    context: FunctionContext,
    tag: DataTagPublicId,
) -> dict[str, TagDetails]:
    """
    Get all Settings Tracking entries for a given tag before a specific point in time.
    """
    ...
```

The output contains raw time-series entries without context, and only accepts a single tag.

**Agent-friendly function** (`is_agentic=True`):

```python
@FunctionContext.expose(is_agentic=True)
def get_settings_history(
    context: FunctionContext,
    range_from: TimestampISOFormat,
    range_to: TimestampISOFormat | None = None,
    tag_ids: list[DataTagPublicId] | None = None,
) -> list[SettingsTrackingEntry] | ErrorResponse:
    """
    Get the full history of setting changes for the given tags within a time range.

    Returns all change events (multiple per tag possible). Use this when you need to know
    how many times a setting changed, or to see the complete timeline of changes.
    If range_to is not provided, it defaults to the current time.
    """
```

Key differences:

* Uses ISO 8601 timestamps (`TimestampISOFormat`) instead of Unix milliseconds, which are natural for an agent to produce.
* Accepts multiple tags in a single call (`tag_ids: list[...] | None`).
* Returns specific error strings per tag when a tag ID is invalid or has no data in the requested window, so the agent can surface precise feedback to the user:

  ```python
  setting_histories[tag] = (
      f"{tag} is not a valid tag, "
      "ensure to check if that is a tag id, and not a tag name"
  )
  ```

# `expose` decorator reference

```python
@FunctionContext.expose(
    is_agentic=True,                    # Register as an agent tool
    requires_human_permission=False,    # Require explicit user approval before execution
    description=None,                   # Override the docstring as the tool description
    examples=None,                      # Example invocation strings shown to the agent
    input_schema=None,                  # Override auto-derived input JSON schema
    output_schema=None,                 # Override auto-derived output JSON schema
    meta=None,                          # Arbitrary metadata attached to the tool definition
    exclude_param_types=None,           # Parameter types to hide from the agent schema
    exclude_param_names=None,           # Parameter names to hide from the agent schema
)
```

`FunctionContext` is automatically excluded from the schema regardless of `exclude_param_types`.