MCP connectors for ibl.ai agents, end to end — register MCP servers (featured cross-organization sharing, enable/disable, OAuth service linkage), manage credential connections with the auth_type × scope matrix (org / agent / per-user), wire servers onto an agent, run the OAuth connected-service lifecycle, handle per-user in-chat OAuth (the oauth_required / oauth_connection_resolved chat events), and debug MCP auth failures (401s, missing OAuth prompts, agents ignoring servers). Use when wirin...
Scanned 9/20/2026
Install to Claude Code
npx -y skills add iblai/vibe --skill iblai-api-agent-mcp --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Iblai Api Agent Mcp?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/iblai-iblai-api-agent-mcp)More formats (shields.io, HTML) on the badges page.
---
name: iblai-api-agent-mcp
description: MCP connectors for ibl.ai agents, end to end — register MCP servers (featured cross-organization sharing, enable/disable, OAuth service linkage), manage credential connections with the auth_type × scope matrix (org / agent / per-user), wire servers onto an agent, run the OAuth connected-service lifecycle, handle per-user in-chat OAuth (the oauth_required / oauth_connection_resolved chat events), and debug MCP auth failures (401s, missing OAuth prompts, agents ignoring servers). Use when wiring an agent to external MCP servers and tools, or designing/troubleshooting MCP auth.
metadata:
kind: api
---
# iblai-api-agent-mcp
Configure external MCP tool access for agents. The platform models MCP with
three objects, and a working integration always requires all three:
1. **MCP server** — metadata for the external MCP endpoint (name, URL,
transport, `auth_type`, `auth_scope`).
2. **MCP server connection** — the credential binding (a static token, or a
reference to an OAuth connected service), at `platform`, `mentor` (agent),
or `user` scope.
3. **Agent wiring** — the agent's settings must have the `mcp-tool` slug in
`tool_slugs` AND the server id in `mcp_servers`.
A missing step 3 is the most common failure mode: server and connection exist
but the agent never calls them. Always finish by re-reading the agent's
settings.
## Auth & conventions
- **Base URL:** `https://api.iblai.app/dm` — the **`/dm` prefix is
required**. MCP endpoints live under
`/api/ai-mentor/orgs/{org}/users/{username}/...` and OAuth connector
endpoints under `/api/ai-account/...`, appended to it. (`…` in the endpoint
lists below abbreviates `https://api.iblai.app/dm/api/ai-mentor/orgs/{org}`.)
- The backend also accepts an `agent`-spelled twin of every agent route
(`/api/ai-agent/...`, `agents/` for `mentors/`); the `mentor` spelling is
canonical and used here, matching the other skills.
- **Header:** `Authorization: Api-Token $IBLAI_API_KEY` on every request.
- **Path vars:** `{org}` = `$IBLAI_ORG`, `{username}` = `$IBLAI_USERNAME`,
`{mentor}` = the agent's unique id (UUID, e.g.
`d17dc729-60fd-4363-81a0-f67d9318b03e`).
- **On the wire the agent noun is `mentor`**: body fields (`mentor`,
`mentor_unique_id`) and the scope enum value `mentor` refer to an agent.
- DELETE / destructive calls: confirm with the user first. Never echo
credentials or tokens back into chat, and never commit them to files.
- Not connected yet? Run **`/iblai-api-login`** first to populate
`IBLAI_ORG`, `IBLAI_USERNAME`, and `IBLAI_API_KEY`.
## Choosing the auth pattern
The `auth_type` × `auth_scope` decision on the **server** drives everything
downstream. `auth_type` = *how* credentials go on the wire (`none | token |
oauth2`). `auth_scope` = *whose* credentials are used (`platform | mentor |
user`). They are orthogonal.
| Pattern | Server fields | Connection setup | End user prompted? |
|---|---|---|---|
| No auth | `auth_type=none` | connection with no credentials | No |
| Shared key for the whole org | `auth_type=token`, `auth_scope=platform` | one platform-scoped connection holding the key | No |
| Per-agent key | `auth_type=token`, `auth_scope=mentor` | one `mentor`-scoped connection per agent | No |
| Pre-provisioned per-user | `auth_scope=user` | admin creates user-scoped connections up front | No |
| **In-chat OAuth** (each user connects their own account) | `auth_type=oauth2` **and** `auth_scope=user` | none up front — created automatically when the user completes OAuth mid-chat | **Yes** |
Facts to keep straight:
- `auth_type="oauth2"` + `auth_scope="user"` is the **only** combination that
triggers the in-chat OAuth prompt; `oauth2` alone is not enough.
- Any `oauth2` connection (at any scope) **requires** a `connected_service`
id — an existing OAuth grant (see the connected-service lifecycle below).
- In-chat OAuth servers must also link an `oauth_service` (an OAuth service
record id) on the server.
## Runtime credential resolution
When an agent invokes an MCP server, credentials resolve in this order —
first match wins:
1. User-scoped connection for (server, user)
2. Agent-scoped (`mentor`) connection for (server, agent)
3. Platform-scoped connection for (server, org)
4. Featured-server global fallback
5. No connection → the call fails 401 — **or** the in-chat OAuth prompt
fires if the server is `auth_scope="user"` + `auth_type="oauth2"`
OAuth-backed connections auto-refresh access tokens near expiry server-side;
no client action is needed.
## OAuth connected services (lifecycle)
Terminology: an **OAuth provider** is the vendor (`google`, `dropbox`); an
**OAuth service** is one surface of it (`drive`, `calendar`); a **connected
service** is a user's persisted token grant for one service — unique on
(user, provider, org, service).
Prerequisite: the org must have a credential named `auth_{provider}`
(containing `client_id`, `client_secret`, `redirect_uri`) in the credential
store before any flow can start — `400 "No credentials found"` on the start
call means it is missing (install it via the integration-credential
endpoints, see `/iblai-api-integration`).
Flow: **discover** enabled services → **start** (returns an `auth_url`; open
it in a new tab — providers block iframes; the flow's state entry expires
after **1 hour**) → the vendor redirects the user's browser to the
**callback**, which exchanges the code and returns the connected service →
use its `id` as `connected_service` on an MCP connection. If a grant already
existed for the same (user, provider, org, service), it is updated in place.
## Reads
- **GET** `…/users/{username}/mcp-servers/?include_global=true&mentor_unique_id={mentor}&is_featured={true|false}&search={q}&transport={…}&page={n}&page_size=12`
— list MCP servers (paged; `include_global=true` surfaces org-wide
connectors; `search` and `transport` narrow results).
- **GET** `…/users/{username}/mcp-server-connections/` — list connections:
scope, `is_active`, `server_name`, `platform_key`, the owning `user`
(username) with `user_email` and `user_full_name`, masked `credentials`
(e.g. `sup****key`), masked `extra_headers`, `connected_service_summary`
(`{id, provider, service, user, platform_key}`). A server row likewise
carries `owner` (username) plus `owner_email` and `owner_full_name`; all six
are read-only and `null` when the row has no user.
- **GET** `…/users/{username}/mentors/{mentor}/settings/` — the agent's
active `tool_slugs`, `mcp_servers` (serialized server objects), and
`can_use_tools`.
- **GET** `https://api.iblai.app/dm/api/ai-account/orgs/{org}/oauth-services/`
— enabled OAuth services: `id`, `oauth_provider`, `name`, `display_name`,
`description`, `scope`, `image`, `created_at`, `updated_at`.
- **GET** `https://api.iblai.app/dm/api/ai-account/orgs/{org}/oauth-services/{service_name}/scopes/`
— the scopes a service requests.
- **GET** `https://api.iblai.app/dm/api/ai-account/connected-services/orgs/{org}/users/{username}/`
— the user's connected services (token grants).
- **GET** `https://api.iblai.app/dm/api/ai-account/connected-services/orgs/{org}/users/{username}/{provider}/{service}/`
— start an OAuth flow; returns `{ "auth_url": "..." }`. The state entry it
primes expires after 1 hour.
- **GET** `https://api.iblai.app/dm/api/ai-account/connected-services/callback/?code=...&state=...`
— the OAuth callback. Hit by the user's browser after provider consent —
relay the vendor's query params unmodified (never decode or alter `state`);
do not call it directly with fabricated values. Success returns the
connected service (`id`, `provider`, `service`, `expires_at`, `scope`
(raw scope string), `scope_names` (canonical ids, e.g. `["drive"]`),
`scopes` (full scope strings), `token_type`, `service_info`
(`{id, name, display_name, logo}`), `username`, `user_email`,
`user_full_name`) — the same `ConnectedServiceSerializer` the
connected-services list read returns.
## Writes
### MCP servers
- **POST** `…/users/{username}/mcp-servers/` — register a server (JSON, or
`multipart/form-data` with `image`):
```json
{
"name": "Google Drive MCP",
"url": "https://drive-mcp.example.com",
"transport": "sse|websocket|streamable_http",
"auth_type": "none|token|oauth2",
"auth_scope": "platform|mentor|user",
"description": "string",
"mentor": "uuid|null",
"credentials": "string",
"extra_headers": { "x-custom": "value" },
"oauth_service": "number|null",
"is_enabled": true,
"is_featured": false,
"clean_output": true,
"image": "File"
}
```
- `name`, `url`, `transport`, `auth_type` are required; `auth_scope`
defaults to `platform`.
- Server-level `credentials` must be the **full authorization value**
(`<scheme> <credentials>`); it takes priority over `extra_headers`.
- `is_featured=true` makes the server available to **other orgs** to
create their own connections against (multi-organization sharing); the owning
org keeps control of the metadata.
- `is_enabled=false` is a hard off-switch — disabled servers are skipped
at runtime.
- `oauth_service` links the OAuth service and is required for in-chat
OAuth servers.
- `clean_output` (default true) strips HTML from server responses;
disable it for documentation servers whose formatting must survive.
- Capture the returned `id` — the connection and the agent wiring both
need it.
- **PATCH | PUT** `…/users/{username}/mcp-servers/{id}/` — edit a server (e.g. flip an
existing server to in-chat OAuth with
`{"auth_scope": "user", "auth_type": "oauth2", "oauth_service": 12}`).
- **DELETE** `…/users/{username}/mcp-servers/{id}/` — delete a server. Destructive — confirm
with the user first.
### MCP server connections
- **POST** `…/users/{username}/mcp-server-connections/` — create a
connection. Common fields: `server` (id, required), `scope`
(`platform|mentor|user`, default `user`), `auth_type` (`none|token|oauth2`),
`credentials`, `authorization_scheme`, `extra_headers`,
`connected_service` (id), `mentor` (agent unique id).
**Platform scope (token):**
```json
{ "server": 9, "scope": "platform", "auth_type": "token",
"credentials": "super-secret-api-key", "authorization_scheme": "Bearer",
"extra_headers": { "x-mcp-client": "agent-ui" } }
```
**Mentor (agent) scope (token):** add `"mentor": "<agent unique id>"` and
`"scope": "mentor"` — different agents can present different credentials
to the same server (e.g. read/write vs read-only keys).
**User scope (OAuth2)** — finalizes an OAuth connection after the
connected-service flow:
```json
{ "server": 9, "scope": "user", "auth_type": "oauth2", "connected_service": 77 }
```
Validation rules the API enforces (per-field error messages):
- `platform` and `user` are **read-only**: the org comes from the request
context (`"Connections must be created for the current platform
context."` when they clash) and the user from the caller — do not send
them in the body.
- `scope=platform` — `mentor` is forbidden; the connection's org must
match the server's org **unless the server is featured**.
- `scope=mentor` — `mentor` is required and must belong to the same org
as the connection.
- `scope=user` — `mentor` is forbidden; requires the calling user or a
`connected_service`.
- `auth_type=oauth2` — always requires `connected_service`
(`"OAuth2 connections require a connected service."`), at every scope,
and the connected service must belong to the same org.
- `auth_type=token` — requires `credentials`
(`"Token based connections must include credentials."`).
Credential handling:
- `authorization_scheme` becomes the header prefix
(`Authorization: Bearer <credentials>`); omit it to send the raw value.
- `extra_headers` is merged into every outbound request; explicit
credentials override clashing headers.
- `credentials` and `extra_headers` are **masked on read** — when
PATCHing, only send `credentials` if actually rotating the secret;
never send a masked value back.
- **PATCH** `…/users/{username}/mcp-server-connections/{id}/` — update; prefer
`{"is_active": false}` over DELETE if the credential may return.
- **DELETE** `…/users/{username}/mcp-server-connections/{id}/` — delete a connection.
Destructive — confirm with the user first.
### Agent wiring
- **PATCH | PUT** `…/users/{username}/mentors/{mentor}/settings/` — enable /
disable connectors on the agent:
```json
{ "tool_slugs": ["ai-index", "mcp-tool"], "mcp_servers": [3, 9],
"can_use_tools": true }
```
**Critical semantics — these lists are replaced, not merged.** `[]` clears
everything; omitting a field leaves it untouched. Blindly sending
`{"tool_slugs": ["mcp-tool"]}` silently strips every other tool the agent
had. Safe procedure: **GET** the current settings, merge locally (keep
existing `tool_slugs`, ensure `mcp-tool` is present; keep existing
`mcp_servers`, append the new server id), then write back the full lists.
### OAuth connected services
- **DELETE** `https://api.iblai.app/dm/api/ai-account/connected-services/orgs/{org}/users/{username}/{id}/`
— revoke a user's OAuth grant / disconnect the service (`204`).
Destructive — confirm with the user first.
## In-chat OAuth (per-user consent at chat time)
For servers with `auth_type="oauth2"` + `auth_scope="user"`, the platform
prompts each user inside the chat stream the first time the agent needs the
tool. Events arrive as JSON on the **existing** chat WebSocket/SSE
connection — parse and switch on `type`; never close or refresh the
connection while waiting, resolution arrives on the same socket.
**Trigger conditions** (all must hold): server `auth_type="oauth2"`, server
`auth_scope="user"`, no valid connection for the current user + server, and
the chat user is authenticated (non-anonymous).
**Admin setup checklist** (before any prompt can fire): the OAuth provider
and OAuth service records exist, the `auth_{provider}` credential is in the
org's credential store, the MCP server is registered with
`auth_type="oauth2"`, `auth_scope="user"`, `is_enabled=true`, and a linked
`oauth_service`, and the server is attached to the agent (`tool_slugs` +
`mcp_servers`).
**Handshake:** the user sends a message → the backend fails to resolve a
user connection → it emits `oauth_required` (with `auth_url`) and polls
every 10s → the client opens `auth_url`; the user consents; the **backend**
callback creates the connected service + connection automatically (the
client does not process the callback) → the backend emits
`oauth_connection_resolved` and resumes the turn. On timeout (default 300s)
an `error` (status 400) terminates the turn — on WebSocket transports the
connection then closes. A user who finishes OAuth *after* the timeout
succeeds automatically on their **next message**, so offer a retry.
**Event reference:**
| Event `type` | Key fields | Client action |
|---|---|---|
| `oauth_required` | `server_name`, `server_id`, `auth_url`, `message` | show a prompt naming the server; open `auth_url` in a new tab; show a waiting indicator |
| `oauth_connection_resolved` | `server_name`, `server_id`, `message` | dismiss the prompt; the chat resumes automatically |
| `mcp_tools_retrieved` | `session_id`, `mentor_id` | informational: tool fetch succeeded on retry (3 attempts, backoff 1s/2s/4s) — log or ignore |
| `warning` | `message`, `developer_error`, `code: 503` | non-OAuth tool failure; the chat continues **without** MCP tools — surface `message`, log `developer_error`, never show it to end users |
| `error` | `error`, `status_code: 400` (**no `type` field** — detect by the top-level `error` key) | OAuth timeout / URL build failure / missing connected service; the turn terminates — offer retry |
Constants: max wait 300s (`MCP_OAUTH_MAX_WAIT_SECONDS`), poll interval 10s
(`MCP_OAUTH_POLL_INTERVAL_SECONDS`). Each poll checks for a connection with a
valid connected service for user + server (or a connected service matching
provider + user + org) — first match resolves.
## Example
Register a platform-token server, bind the shared key, and enable it on an
agent (settings read-merge-write elided):
```bash
BASE="https://api.iblai.app/dm/api/ai-mentor/orgs/$IBLAI_ORG/users/$IBLAI_USERNAME"
AUTH="Authorization: Api-Token $IBLAI_API_KEY"
curl -s -X POST "$BASE/mcp-servers/" -H "$AUTH" -H "Content-Type: application/json" \
-d '{"name":"Workflow MCP","url":"https://wf.example.com/mcp","transport":"streamable_http",
"auth_type":"token","auth_scope":"platform","is_enabled":true}'
# → capture "id": 9
curl -s -X POST "$BASE/mcp-server-connections/" -H "$AUTH" -H "Content-Type: application/json" \
-d "{\"server\":9,\"scope\":\"platform\",\"auth_type\":\"token\",
\"credentials\":\"$MCP_KEY\",\"authorization_scheme\":\"Bearer\"}"
```
## Notes
- **Troubleshooting quick map:**
- `400 OAuth2 connections require a connected service.` — complete the
connected-service flow first; pass the resulting id.
- `400` cross-org server on a platform connection — use a server owned by
this org, or mark the source server `is_featured=true`.
- `400 No credentials found` on OAuth start — install the
`auth_{provider}` credential (client_id, client_secret, redirect_uri).
- Agent never calls the server — `mcp-tool` missing from `tool_slugs` or
the server id missing from `mcp_servers`; these lists are replaced, not
merged, so a careless settings write may have stripped them.
- No OAuth prompt on a per-user server — needs **both**
`auth_scope="user"` and `auth_type="oauth2"`, plus a linked
`oauth_service`, plus an authenticated (non-anonymous) chat session.
- Prompt fires every message even after auth — the connected service
belongs to a different user or org than the chat user.
- Connection unexpectedly falls back to platform creds — check the user
connection's `is_active` and that the connected service's user matches
the chat user.
- `oauth-services` returns `[]` — no enabled OAuth service records; seed
the provider + service.
- Callback `Invalid state` — the start/callback round-trip spanned browser
contexts or exceeded the 1-hour window; redo the flow in one session.
- Callback `Could not exchange auth token` — the provider rejected the
code; verify the redirect URI matches the provider console and restart.
- Tool call fails silently — a `warning` (503) event was ignored; surface
it and verify the MCP server is reachable from the platform.
- **Scope enum is `platform | mentor | user`** on both `MCPServer.auth_scope`
and connection `scope` — `mentor` means agent-wide, `platform` means
org-wide. (There is no `agent` or `tenant` value on the wire.)
- The chat events above ride the runtime chat connection (see
`/iblai-api-agent-chat` for wiring live chat); everything else in this
skill is plain REST.
## Reference material
`references/` carries the additive material that doesn't fit the endpoint
sections above — the endpoint/method/body and event facts stay above:
- [`references/concepts.md`](references/concepts.md) — backend model and the
design "why": the five Django models behind the wire objects, the module map,
runtime-resolution internals, the OAuth state/token design, and the
validation, access-control, and extensibility rules.
- [`references/integration.md`](references/integration.md) — client / front-end
notes for the REST setup screens: driving the connection form off `auth_type`,
scope-aware fields, sourcing the OAuth2 connected-service picker, rendering
inline per-field validation errors, and the browser OAuth start/callback
strategy.
- [`references/in-chat-events.md`](references/in-chat-events.md) — the fuller
per-frame event reference: field types, the three `error` variants with their
exact messages, and the client-handling gotchas.
- [`references/mcp-servers-catalog.md`](references/mcp-servers-catalog.md) — the
open-source `iblai-mcp` repo of ready-made MCP servers. This skill wires
*external* MCP servers onto agents; that repo is a source of servers to wire.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!