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

# Connector providers (MCP)

> Connect third-party providers through the ForceAI Connector, restrict each one to access groups, and read the audit trail of which action every call reached.

The ForceAI Connector is a single MCP server that fronts roughly 1090 third-party providers. Connect a provider once, and any model or agent with access can drive it as a tool. Because it is one server, ordinary per-server permissions cannot express "this team may use Slack but not Salesforce" — that is decided per provider, on the Providers page

<Info>
  One MCP server, many providers. The connector exposes four tools — `list_apps`, `search_actions`, `get_action_guide` and `execute_action` — and the provider is chosen by the `actionId` argument, for example `hackernews.get_top_stories`
</Info>

## Browse and connect a provider

Go to **MCP Servers** and choose **+ Add New MCP Server**. This opens the provider catalog rather than a form for registering another server, because in ForceAI the connector is already registered and adding a tool means connecting one of its providers

Click any provider to see what it actually does: its categories, its authentication type, and every action it exposes with an id, a description and the names of the input fields it accepts. Connecting is a button inside that view, so you choose a provider knowing what it can do rather than being asked for a credential first

<Tip>
  The input field names are the part most easily got wrong. An action called with an argument it does not declare is refused by the connector's schema check, and the error arrives from several hops away. `hackernews.get_top_stories` takes no `limit`, and the action list says so before you write the call
</Tip>

Providers differ in what they need:

| Authentication        | What to supply                                                                                   |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| `no_auth`             | nothing; the provider is reached anonymously and connecting only records that ForceAI may use it |
| `api_key` and similar | the credential fields the provider declares, entered once                                        |
| `oauth2`              | an OAuth client id and secret, then an authorization round trip                                  |

A credential submitted here is forwarded to the connector once and never stored, logged or echoed by the dashboard. Errors from the provider are surfaced as a code rather than a message, because some providers quote the submitted value back in the message text

## Restrict a provider to access groups

Every connected provider is usable by everyone until you narrow it. On a provider's row in the catalog, the access line reads **Access: open**; click it to assign one or more access groups, and it becomes **Restricted: \<groups>**

Restriction is deliberately opt-in. Connecting a provider is what makes it usable, and an admin narrows that on purpose rather than having to grant every provider individually. The failure mode is a team seeing a provider it did not need, not a credential reaching someone who should not have it

Access groups come from the caller's own grants, the same ones that flow SSO to team to key. A caller reaches a restricted provider when it holds at least one of the assigned groups. Proxy admins bypass the restriction

<Warning>
  A caller that holds no groups is refused a restricted provider. That is the intended behaviour: restriction means restriction, and the only way past it is the admin bypass
</Warning>

### Agents and per-task keys

An ephemeral agent runs on a short-lived key minted for its task, which has no object permission of its own. The access groups of whoever dispatched the task are stamped into that key when it is minted, so an agent reaches exactly the providers its dispatcher could

The agent orchestrator's sub-runners are the exception. They run as their own service principal rather than as the initiating user, so they carry no user groups and a restricted provider stays denied to them

## Read the audit trail

Every connector action is recorded, whether it ran or was refused:

| Verb                       | When                                                           |
| -------------------------- | -------------------------------------------------------------- |
| `connector_action_invoked` | the call was allowed and dispatched                            |
| `connector_access_denied`  | the provider was restricted to groups the caller does not hold |

Each row carries the provider, the tool, the **action id**, the caller's access groups and the groups the provider requires. The action id is the part that matters: the spend log records the tool name, which is `execute_action` for every one of the catalog's actions and so identifies nothing on its own

Refusals are worth their own row because a denied call leaves no other trace. The gate blocks before the tool runs, so nothing bills and no spend-log entry is written; without the audit row there is no record that a caller reached for a provider it may not use

Admin calls are audited too. They bypass the restriction, not the record

### Seeing what a tool returned

The result of a tool call is stored only when `FORCEAI_STORE_MCP_RESULTS` is set. A result is whatever the third-party provider returned — story ids today, but Slack messages, mail and CRM records once those providers are connected — so keeping it is a retention decision rather than a display one, gated the same way prompt content is

Stored results are capped in size, and a truncated result says so rather than being silently clipped

## Using the connector from a model

In the **Playground**, pick a model, add `forceai_connector` under **MCP Servers**, and ask for something the provider can do. The model discovers the tools and calls them; the MCP events panel shows each call and its result

Narrowing **Limit tools** to `execute_action` is worth doing when you already know the action, since it stops the model spending a round on discovery

### Tool rounds

A tool round is one completion in which the model may call a tool. The budget is not a single global number; it follows the servers the request can reach, because a server's shape decides whether chaining is intrinsic to it:

| What the request holds              | Rounds                                                                             |
| ----------------------------------- | ---------------------------------------------------------------------------------- |
| an ordinary single-purpose server   | 1; call the tool, answer                                                           |
| the connector                       | 4, because `search_actions` then `execute_action` is a chain its own design forces |
| several servers                     | the most demanding of them, since the request may use any                          |
| a server with `max_tool_rounds` set | that number, capped at 10                                                          |

Every round is a further billed completion, and rounds are not equal: each one re-sends the tool definitions, the question and every prior tool result, so a fourth round can cost several times the first

The budget is checked before a round starts and never inside one. A round that has begun runs to completion and bills in full; the next simply does not begin. On the last round the budget allows, the request is sent without tools, so the model answers in prose with what it gathered rather than announcing a call nothing will run

<Tip>
  `FORCEAI_MCP_TOOL_ROUNDS` still overrides everything when set, which is how you pin the number down to bound spend. Leave it unset to let the budget follow the servers
</Tip>

## Configuration

| Variable                            | Default                         | Purpose                                                    |
| ----------------------------------- | ------------------------------- | ---------------------------------------------------------- |
| `FORCEAI_CONNECTOR_URL`             | `http://forceai_connector:3000` | where the connector is reached                             |
| `FORCEAI_CONNECTOR_ADMIN_TOKEN`     | —                               | admin token for the connector's own API; stays server-side |
| `FORCEAI_CONNECTOR_MCP_SERVER_NAME` | `forceai_connector`             | the MCP server name the access gate recognises             |
| `FORCEAI_STORE_MCP_RESULTS`         | off                             | store what a tool call returned                            |
| `FORCEAI_MCP_TOOL_ROUNDS`           | unset                           | pins tool rounds, overriding the per-server budget         |

## Known limitations

Tool policies do not currently apply to MCP tools. Policy reads tool names from OpenAI `function` specs, and an MCP tool arrives in a different shape, so connector traffic is governed by per-provider access and the audit trail rather than by [tool policies](/tools/tool-policies)

Non-streaming requests run a single tool round regardless of `FORCEAI_MCP_TOOL_ROUNDS`
