Agent Connectors

A connector gives an agent the tools of an MCP server. Connect it once, then choose which agents may use it.

A connector is added to an agent as a tool of type mcp; the call that creates the agent is on Agents. Every field: API reference.

Connectors (MCP servers)

A connector is an MCP server you run. Declare it on the agent version and its tools join the run: 2kw.ai lists them, offers them to the model, calls the ones the model picks and gates every call.

{
  "type": "mcp",
  "server_label": "acme",
  "server_url": "https://mcp.example.com/mcp",
  "server_description": "The acme service desk",
  "allowed_tools": { "tool_names": ["search_tickets", "create_ticket"] },
  "require_approval": { "never": { "read_only": true } }
}

server_label is 1 to 24 characters of a-z, 0-9 and _, with no doubled __ and no leading or trailing _, and must be unique on the agent: it is what says which server a call belongs to, and the doubled underscore is what separates the label from the tool in the name below — so a label carrying one could make two different servers' tools share a name. The model sees each tool as mcp__{server_label}__{tool} — mcp__acme__search_tickets above. That prefix is reserved, so a function tool or a catalog tool named mcp__… is refused when the agent is saved.

FieldMeaning
allowed_toolsWhich of the server's tools exist at all. An array of names, or a filter object. Absent allows every tool the server lists
require_approval"always", "never", or an object with an always and a never filter. Absent means { "never": { "read_only": true } } — read tools run, everything else asks

A filter is { "tool_names": [...], "read_only": true } and its two halves combine with AND: that one matches a tool only if it is on the list and the server annotates it read-only. read_only: false filters nothing.

Together they give each tool one of three levels. Left out of allowed_tools is deny — the tool is not offered to the model at all. Matching always is ask: the run pauses and emits an approval request. Matching never is allow. Anything else asks, and a tool matching both sides asks.

That level is the author's ceiling, not the verdict. It joins the approval policy and the platform floor, and the strictest of them wins: a connector can tighten what the policy allows, never loosen it.

The floor reads the server's own MCP annotations, which are claims and not proof. readOnlyHint: true reads; a destructive hint destroys; everything else, including a tool with no annotations at all, is a write. A tool claiming to be both read-only and destructive counts as destructive, because that is the safer way to be wrong. On top of that: a write is never run without a human, and a destructive tool is never run at all. require_approval: "never" does not change either.

One consequence is worth knowing in advance: in Auto mode a connector's write tool reaches the automatic approver only when the server itself declares it destructiveHint: false and openWorldHint: false. Otherwise the run pauses for a person. A connector tool is named mcp__<server_label>__<tool> in policy rules, so renaming the entry's server_label drops the rules written for it.

A run with connectors carries a few extra items. The first turn of a conversation chain gets one mcp_list_tools item per connector — a catalogue, repeated on a later turn only if that server's tools have changed in between. Every call the model makes appears as an mcp_call with its output or its error. A call that needs approval appears as an mcp_approval_request, and you answer it with an mcp_approval_response (backbone:approval_response is accepted too) on the continuation.

Approving re-lists the server first, so the call runs against the tools it offers now. If the server cannot be reached at that moment the answer is 503, nothing runs, and the approval stays pending — send the same decision again once it is back. If the server answers and no longer offers the tool, the call fails as withdrawn.

For Outlook mail, calendar and contacts there is a ready-made connector; see Microsoft 365.

Images in tool results

When a connector's tool answers with images (PNG, JPEG, WebP or GIF, at most 20 MB each), the model sees them as pictures on its next step. The connector's overall answer size limit applies first: an answer that exceeds it fails as a whole, so in practice an image has to be smaller than that limit. The call's output keeps its text and names each image in the place it came in:

the scan
[image 1 attached (1200×800)]

The images are stored as files of the conversation, under the same hold as the conversation's other outputs, and the response carries one backbone:tool_image item per image right after the mcp_call, with the same fields as for viewed images. The id of a connector image is timgc<n>_<call id>, n counting the call's images from 1. Read the bytes through the Files API.

An image the model is not shown says why in its line, for example [image 5 not shown: this call returned more than 4 images]. The reasons are: more than four images in one call, not a PNG, JPEG, WebP or GIF, over 20 MB, unreadable image data, and an image that could not be stored. A call without a conversation, such as a request chained only by previous_response_id, shows no images. A call that fails shows none either: its images are named as omitted.

Audio, other binary content and resource links are not fetched or shown. Each is one line naming its type and size, and audio is never shown to the model.

Files in tool results

When a connector's tool answers with a file (a PDF, a mail as .eml, a spreadsheet: any binary resource that is not an image), the file is stored and the model gets its id. The call's output keeps its text and ends with one line per file:

the mail
[file 1 attached: Rechnung 123.pdf · application/pdf · 84.2 KB · id=file_abc]

The model can hand that id on:

  • read_file and extract_fields read the file, in agents that have them.
  • A connector's tool, or a tool in your application such as a relay tool, receives the bytes when the model sets a base64 argument to backbone:file:{id}, up to 5 MiB of files per connector call. A function tool refuses it. See Using a File in a Turn for whose files a connector call may send and what the model reads when one cannot be sent.
  • Deliver releases the file when the conversation holds its outputs. The agent does not need Bash for any of this.

The mcp_call item lists the stored files in output_files, the same refs a viewed or produced file has, and the chat shows each one as a chip.

Who can open it. Only the person (or embedded panel) whose request ran the call. A connector file is a copy of something that person read with their own access, so other members and admins of the organization cannot open or list it, and neither can the model in someone else's turn; they get "not found". Admins can delete it. A request authenticated by an API key without a user does not match a person and cannot open it either.

Limits. At most 10 files per call and 25 MB per file; a file over the limit, with unreadable data, or past the tenth is named in its line instead, for example [file 3 not stored: too large (limit 25 MB)]. A call without a conversation stores nothing ([file 1 not stored: files need a conversation]). A call that returns an answer after a pause, such as after a question the server asked you, shows the files as not stored: call the tool again. A call that fails stores none. Disconnecting a connector does not delete files already stored.

Approved hosts

Before any connector can be reached, an organization admin approves its host. Approving a host approves every path and every account on it, so it is a trust decision rather than a routing detail:

2kw connectors public-hosts add mcp.example.com     # --port for anything but 443
2kw connectors public-hosts list
2kw connectors public-hosts remove {id}

Because it is a trust decision, it takes a person: add and remove need a signed-in organization admin — run 2kw auth login and sign in through the browser. A signed-in Member, Admin or Owner may list the hosts, which is what the console's agent editor offers an author to pick from. An API key is refused on all three whatever role it carries, so that a key leaked out of an automation cannot approve its own destination. Admins can also approve and remove hosts in the console under Connectors → Manage allowed hosts and OAuth clients.

A connector whose host is not on that list is refused before any request leaves 2kw.ai, and a withdrawal takes effect on the very next call — including calls in a run that is already under way.

A server that is unreachable, or too slow to finish listing its tools, offers no tools for that turn: the run goes on with everything else the agent has rather than failing.

Servers that need a sign-in

Some MCP servers act as a person: they refuse an anonymous request and ask for an OAuth sign-in. 2kw.ai never shares one sign-in across the organization for such a server. Each member connects their own account, and each agent that should act as them needs their permission.

  • Connecting. A member connects once, on the Connectors page of the chat app (https://chat.2kw.ai/connectors), or from the card a paused chat shows. The server's sign-in page opens in a new tab and returns to https://chat.2kw.ai/connectors/callback, the one callback address 2kw.ai uses. After that, 2kw.ai renews the access in the background; when it cannot, the connection needs a reconnect.
  • Allowing an agent. A connection alone lets no agent use it. The member allows each agent separately, and is asked again when a new version of the agent reaches destinations it did not reach when they allowed it. Connecting from a paused chat's card also allows the agent that asked; connecting from the Connectors page stores the connection only.
  • Tightening. Per allowed agent, the member can make any tool stricter than the author set it — Needs approval or Blocked — and never looser. A change applies from the next response on.
  • Who the run acts as. A request with a member's session runs as that member, and a request with an API key as the key's owner. For n8n or the CLI, the key's owner therefore connects in the chat app; see Connecting as yourself for what an API client receives when nobody has. A user of an embedded panel connects from the panel, under their own account on the server, without being a member of the organization; their connection is theirs alone and usable by the installation's agent only.

2kw.ai signs in to the server's authorization server as its own OAuth client. Where that server supports it, the registration is automatic. Where it does not, Connect fails with a message to ask your administrator, and an organization admin registers 2kw.ai as an OAuth client there and enters the client ID, secret and authentication method in the console under Connectors → Manage allowed hosts and OAuth clients. Changing that registration later asks everyone connected through it to sign in again the next time their access is renewed. The authorization server has to be on an approved host, like the MCP server itself.

The page lists each connector as a row. Open one and its People tab shows how many members are connected and has a Disconnect everyone action: it deletes every member's connection to that server and every permission they gave, and revokes the tokens at the server where it can. An admin can remove connections but never sees or uses anyone's token.

In the console's agent editor, the Connectors section adds a server from an approved host and a path. Test connection lists every tool the server offers, including those the entry keeps from the model; a server that needs a sign-in lists nothing until you connect as yourself from the same section. A tool overview then shows each tool's own level as three icons: Always allow, Needs approval or Blocked for a read-only tool, Needs approval or Blocked for a write or a destructive tool. The Read-only and Write group controls set every tool in the group at once, and read Custom while its tools differ. The overview writes the entry's allowed_tools and require_approval and the hitl_policy.tools rules for mcp__<server_label>__…. An entry or rules it cannot express are shown as Custom (JSON), with a Reset to simple action.

Connectors on a request without an agent

A POST /v1/responses request that names a model and no agent can carry mcp entries of its own. 2kw.ai lists the server's tools, offers them to the model and runs the calls it picks, the same way it does for an agent's connector, and gates them the same way: a read runs, a write asks, a destructive tool never runs.

{
  "model": "openai/gpt-4o",
  "input": "How many invoices are overdue?",
  "tools": [
    {
      "type": "mcp",
      "server_label": "acme",
      "server_url": "https://mcp.example.com/mcp",
      "authorization": "<bearer token for that server>"
    },
    { "type": "function", "name": "lookup_customer", "parameters": { "type": "object" } }
  ]
}
  • Who may. A signed-in member, or an API key, which acts as its owner. A surface session is refused with 404 before it gets this far, and any caller 2kw.ai cannot name as a member is refused with 400 and mcp tools in a request need a member or an API key.
  • What is checked. The entry is held to the same rules as one saved on an agent: the host has to be approved, an entry may not name a single tool (name), and a function tool may not be called mcp__…. A refusal is a 400 and nothing is dialled.
  • What runs here. Only the connector's own calls. Your function tools in the same request still come back to you as function_call items, as they do without connectors, and you answer them on the continuation. Nothing else the model might ask for runs.
  • authorization. An optional bearer for that server, used for this request only. 2kw.ai sends it to the server as Authorization: Bearer … when it lists the tools and when it calls one, and never stores it, echoes it in the response's tools or writes it to a log. A server that needs a person's sign-in is reached through the member's connection instead: connect on the Connectors page, which stores the connection and allows no agent, and leave authorization out.
  • Approvals. A write pauses the response with an mcp_approval_request. Only the member who made the request can decide it, because the call runs with the bearer of the request that decides it; anyone else gets 400. The continuation has to carry the same mcp entry again, authorization included, since 2kw.ai kept none. Without it the answer is 400 and the approval stays pending, so you can send the decision again.
  • Waiting for a sign-in. If the member has not connected yet, the response pauses with a backbone:connector_auth_request, and continuing it, with the mcp entry again, picks up the connection once it exists.
  • Synchronous only. These requests run to their end or pause in the request itself; they are not a background run.

Not in this release

  • Servers on a private network, reached through a relay, cannot be added yet.
  • An unattended run is not held until someone connects, and nobody is notified by email: an API-key run ends with the pause described under Connecting as yourself, and continuing it is up to the caller. Connect on the Connectors page before an unattended run needs the server.
  • A request without an agent cannot run its connectors in the background, and an approval it raises does not appear in an agent's approvals list.
  • The CLI and the MCP server report a sign-in pause but cannot manage connections, permissions or OAuth clients; that happens in the chat app and the console.

Was this page helpful?

A request failed? See Errors for every status and code.