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

# Build a monday.com assistant

> A ~100-line Claude agent that answers questions about your monday.com account through Engini tools.

Claude answers questions about your boards, items, users, and teams by calling Engini tools - it never sees a monday API key or an HTTP request. This is the canonical Engini agent loop; every other agent recipe is a variation of it.

**Prerequisites:** an Engini API key (`ENGINI_API_KEY`), an Anthropic API key (`ANTHROPIC_API_KEY`), and a monday.com connection on your account (`engini connect monday` creates one).

## 1. Client + toolset

The whole app-specific surface is three SDK calls:

```python theme={null}
from engini import Engini
from engini.providers.anthropic import AnthropicProvider

client = Engini(provider=AnthropicProvider())   # reads ENGINI_API_KEY

# Use the first monday connection on the account
conn_id = next(c.connection_id for c in client.connections.list(application="monday"))

toolset = client.toolset(connections={"monday": conn_id})
tools = toolset.tools()          # monday tools as Anthropic tool dicts
```

## 2. One helper Anthropic needs

`handle_tool_calls` returns one result message **per tool call**; Anthropic expects all `tool_result` blocks for one assistant turn in a single user message:

```python theme={null}
def merge_tool_results(result_messages: list[dict]) -> dict:
    blocks = []
    for message in result_messages:
        blocks.extend(message["content"])
    return {"role": "user", "content": blocks}
```

## 3. The agent loop

```python theme={null}
import anthropic

SYSTEM_PROMPT = (
    "You are a helpful assistant connected to the user's monday.com account "
    "through Engini tools. Use the tools to answer questions about their "
    "boards, items, users, and teams. When you list things, keep it concise. "
    "If a tool needs an ID you don't have yet, call a tool to look it up first."
)

llm = anthropic.Anthropic()
messages = [{"role": "user", "content": "What boards do I have in monday?"}]

while True:
    reply = llm.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        system=SYSTEM_PROMPT,
        tools=tools,
        messages=messages,
    )

    for block in reply.content:
        if block.type == "text" and block.text.strip():
            print(f"Assistant: {block.text}")
        elif block.type == "tool_use":
            print(f"  · calling {block.name}({block.input})")

    if reply.stop_reason != "tool_use":
        break

    # Engini resolves the connection, runs each call, and formats results.
    messages.append({"role": "assistant", "content": reply.content})
    results = toolset.handle_tool_calls(reply)
    messages.append(merge_tool_results(results))
```

That's the whole agent: think → call tools → observe → repeat, until the model stops asking for tools. Keep appending to the same `messages` list across user turns and you have a chat assistant with memory.

## Run the original

The runnable version (interactive chat loop, `.env` handling, connection pinning via `ENGINI_MONDAY_CONNECTION`) lives at [`python/examples/monday-agent/`](https://github.com/engini/engini-sdk/tree/main/python/examples/monday-agent):

```bash theme={null}
cd python/examples/monday-agent
uv run agent.py "What boards do I have in monday?"
```

## Try another app

Swap `"monday"` for any connector on your account (`salesforce`, `gmail`, `shopify`...) - list what's connected:

```python theme={null}
for c in client.connections.list():
    print(c.application_slug, c.connection_id, c.connection_name)
```

The [Priority ERP recipe](/examples/priority-erp-agent) is exactly this substitution.
