Skip to content

AI Connection Tools (Per Instance)

Connection tools let an AI agent call actions on specific connection instances — for example, send an email from a specific Gmail account, list events from a specific Google Calendar, or send a Telegram message via a specific bot token.

Unlike internal tools, which use a static catalog of Operations CRUD actions, connection tools are bound per connection instance. Each connection the operator grants to an agent gets its own set of namespaced tools.

What and Why

An AI agent needs access to external integrations (email, calendar, spreadsheets, messaging) to take meaningful action on behalf of users. Connection tools bridge that gap:

  1. An operator connects their external accounts via the Connections module (Gmail, Google Workspace, Telegram, SMTP).
  2. The operator opens the agent's Herramientas → Conexiones tab and selects which tools to enable for each connection instance.
  3. At chat time, ConnectionsToolProvider.build() reads the grants from ai.agent_connection_tools and injects the enabled tools into the Vercel AI SDK ToolSet, namespaced by connection slug.

Each granted connection gets an independent, namespaced tool entry in the LLM's tool set — so an agent with two Gmail accounts gets work-gmail__send_gmail_message and personal-gmail__send_gmail_message as separate tools.

Architecture

graph TD
    subgraph HTTP Layer
        CTL["AgentConnectionToolsController\nGET catalog / PUT upsert"]
    end

    subgraph Application Layer
        QH["GetAgentConnectionToolCatalogHandler"]
        CH["UpsertAgentConnectionToolsHandler"]
        CTP["ConnectionsToolProvider\n(IToolProvider)"]
    end

    subgraph Infrastructure Layer
        REPO["AgentConnectionToolRepositoryImpl\n(ai.agent_connection_tools)"]
        CONN_REPO["IConnectionRepository"]
    end

    subgraph Tool Map
        TPM["tool-provider-map.ts\nTOOL_TO_PROVIDERS + slugify + computeSlugs"]
    end

    CTL --> QH
    CTL --> CH
    CH --> REPO
    QH --> CONN_REPO
    QH --> REPO
    QH --> TPM
    CTP --> REPO
    CTP --> CONN_REPO
    CTP --> TPM

Key design decisions:

  • The connectionId is captured in a closure at build time — it never appears in the LLM-facing Zod input schema.
  • Authorization context (orgId, userId) also comes from the verified JWT, never from LLM input.
  • ConnectionsToolProvider is a top-level IToolProvider (not an internal-tools provider). It runs independently of the InternalToolProvider pipeline.

Tool Naming Convention

Every tool exposed to the LLM is named as:

<slug>__<toolName>

Where <slug> is derived from the connection's name field:

  • Lowercased, non-alphanumeric characters replaced with -, leading/trailing dashes stripped.
  • If two connections produce the same base slug, subsequent ones get a numeric suffix: -2, -3, etc. (ordered by created_at ASC, id ASC).
  • The first occurrence always keeps the bare slug.

Examples:

Connection nameSlug
Work Gmailwork-gmail
Work Gmail (second connection with same name)work-gmail-2
My Bot Tokenmy-bot-token

The full namespaced tool seen by the LLM:

work-gmail__send_gmail_message
work-gmail__list_calendar_events
my-bot-token__send_telegram_message

The slug the LLM sees in the tool set is the same slug returned by the GET catalog endpoint. The panel UI also shows the slug pill so operators know exactly how the tool will be named.

Tool Catalog

The 30 available tools are organized by provider type:

Telegram

Tool NameDescription
send_telegram_messageSend a message to a Telegram chat

SMTP

Tool NameDescription
send_smtp_emailSend an email via SMTP

Google Gmail (google_gmail)

Tool NameDescription
send_gmail_messageSend a Gmail message
list_gmail_messagesList Gmail messages
get_gmail_messageGet the body of a Gmail message
reply_gmail_messageReply to a Gmail thread
create_gmail_draftCreate a Gmail draft
get_threadRetrieve a full Gmail conversation thread by threadId
search_threadsSearch Gmail threads using Gmail search syntax
list_draftsList Gmail drafts for the authenticated account
list_labelsList all Gmail labels (system + user-created)
label_messageAdd labels to a Gmail message
unlabel_messageRemove labels from a Gmail message
label_threadAdd labels to an entire Gmail thread
unlabel_threadRemove labels from an entire Gmail thread

Google Workspace (google_workspace) — Gmail capability

Tool NameDescription
send_gmail_messageSend a Gmail message
list_gmail_messagesList Gmail messages
get_gmail_messageGet the body of a Gmail message
reply_gmail_messageReply to a Gmail thread
create_gmail_draftCreate a Gmail draft
get_threadRetrieve a full Gmail conversation thread by threadId
search_threadsSearch Gmail threads using Gmail search syntax
list_draftsList Gmail drafts for the authenticated account
list_labelsList all Gmail labels (system + user-created)
label_messageAdd labels to a Gmail message
unlabel_messageRemove labels from a Gmail message
label_threadAdd labels to an entire Gmail thread
unlabel_threadRemove labels from an entire Gmail thread

Google Workspace — Sheets capability

Tool NameDescription
list_spreadsheetsList Google Sheets
get_spreadsheetGet spreadsheet metadata
read_spreadsheet_valuesRead values from a range
write_spreadsheet_valuesWrite values to a range
append_spreadsheet_valuesAppend rows to a spreadsheet

Google Workspace — Calendar capability

Tool NameDescription
list_calendar_eventsList calendar events
create_calendar_eventCreate a calendar event
update_calendar_eventUpdate a calendar event
delete_calendar_eventDelete a calendar event
get_calendar_eventGet a specific calendar event
list_calendarsList calendars accessible to the authenticated account
find_free_timeFind free/busy time windows across one or more calendars
respond_to_eventRespond (accept / tentative / decline) to a calendar event invitation

Acting identity for respond_to_event: The attendee whose response is updated is matched against the OAuth account's email (the email of the user who authorized the Google connection). The LLM cannot respond on behalf of other attendees — if the OAuth account is not in the event's attendee list, the call fails.

A single google_workspace connection can have tools from all three capability groups enabled simultaneously. The panel groups them visually by capability (Gmail / Sheets / Calendar), but they are stored in a single enabledTools array on one grant row.

Configuring Connection Tools (Panel)

  1. Open the AI agent edit dialog and navigate to step 7 Herramientas.
  2. Click the Conexiones tab.
  3. The catalog lists all connections accessible to the authenticated user in the org. Each card shows:
    • Connection name and provider badge.
    • Slug pill (this is the prefix that will appear in the LLM tool set).
    • Tool checkboxes (flat list for Telegram/SMTP/Gmail; grouped by capability for Google Workspace).
  4. Toggle the desired tools, then click Guardar to persist the full grant set.

Clicking Guardar performs a full-diff replace — it sends the complete current selection for every connection. There is no partial-save API.

Runtime Auth Semantics

At chat time, ConnectionsToolProvider.build() is called once per request. For each granted connection:

  1. The connection is loaded from the repository using the connectionId captured at build time.
  2. Accessibility is checked: the connection must still exist and be visible to the executing user (userId from JWT).
  3. If the connection is no longer accessible (deleted, revoked, or visibility lost), the tool is not injected into the tool set for that request — the LLM will not see it.
  4. If the connection passes the pre-flight check, each enabled tool is injected with its execute closure performing a runtime visibility re-check before dispatching. If the check fails at execution time, the tool returns { success: false, error: "Connection not accessible" } to the LLM — no HTTP 500, no silent success.

This means connection grants are evaluated at call time, not at agent-save time.

Data Model

Connection tool grants are stored in ai.agent_connection_tools:

ColumnTypeNotes
idUUID v7Primary key
agent_idUUIDFK → ai.agents(id) ON DELETE CASCADE
connection_idUUIDFK → connections.connections(id) ON DELETE CASCADE
enabled_toolsJSONBNon-empty array of tool names
created_at / updated_attimestamptzManaged by TypeORM

Unique constraint on (agent_id, connection_id) — one grant row per connection per agent.

Migration Note

The migration for agent_connection_tools also removes stale data from the previous approach:

sql
DELETE FROM "ai"."agent_internal_tools" WHERE "provider_key" = 'connections';

Any agent_internal_tools rows with provider_key = 'connections' that existed before this feature are no longer valid — connections are no longer an internal-tools provider. See AI Internal Tools for details on what the internal-tools system covers today.

API Reference

See Connection Tools API Reference for the full endpoint documentation.

Deferred / Out of Scope

  • findManyByIds optimization: the provider currently uses a per-connection-id lookup (N+1) instead of a batch query. This is correct but not optimal; a findManyByIds method on IConnectionRepository would eliminate the N+1 for agents with many connection grants.
  • Per-tool CASL permission checks and audit trail for tool-triggered mutations are planned for a future iteration.