Connecting via MCP
Service accounts, client credentials or API keys, Azure AI Foundry, and your first tool call.
Machine integrations authenticate as a service account using the OAuth client_credentials grant, or — for clients that can only send a fixed header — a static API key. No browser, no user session, no interactive consent.
1. Create a service account
An administrator does this from Admin → Service Accounts:
- Enter a display name that identifies the integration — "Jira Sync", not "api". It shows up in audit history.
- Press create. The client secret and the API key are shown exactly once; copy them immediately. If they are lost, use Reissue secret — neither can be recovered.
The service account's id is the client_id, and it is safe to store in config. The secret is not — treat it like a password.
2. Grant it access to a brain
A new service account can authenticate but can see nothing. Access is granted per brain by an administrator, from Admin → Brains → the brain → its service-account access section. Note this is the admin brain page: the user-facing brain page manages user and group access, not service accounts.
Pick the service account and choose a level:
| Level | Can do |
|---|---|
read | Search and read. The right default for a retrieval agent. |
read_write | Also create, update and delete neurons and synapses. |
owner | Also manage access and brain settings. |
Grant the least you need. An agent that only answers questions should be read — that way a prompt-injection attempt in retrieved content cannot lead to writes.
3. Get an access token
POST /brain/{brain_id}/token. Form-encoded or JSON both work. The access token is a bearer JWT valid for 1 hour; the refresh token lasts 30 days.
curl -X POST https://hive.example.com/brain/$BRAIN_ID/token \
-d grant_type=client_credentials \
-d client_id=$HIVE_CLIENT_ID \
-d client_secret=$HIVE_CLIENT_SECRET
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOi..."
}
To rotate without re-sending the secret, post grant_type=refresh_token with the refresh_token. Refresh tokens rotate on use — store the new one.
Refresh tokens are tied to the account's current secret. After the secret is reissued, or once it expires, a refresh returns invalid_grant — request a new token with grant_type=client_credentials using the current secret. Keep the client secret available to your integration; a refresh token alone is not enough to recover.
4. Call a tool
The MCP endpoint is POST /brain/{brain_id}/mcp, JSON-RPC 2.0 over the streamable-HTTP transport. Send the access token as a bearer header and accept both content types:
curl -X POST https://hive.example.com/brain/$BRAIN_ID/mcp \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "aiqbee_search",
"arguments": {"query": "what did we decide about rate limiting?"}
}
}'
tools/list returns the full tool set with schemas — useful for building your tool definitions programmatically rather than hard-coding them.
API keys
Some agent platforms cannot run a token exchange and can only attach a fixed header to every request. For those, a service account also carries an API key, issued alongside the client secret and sharing its expiry. Prefer client credentials (above) wherever your client can run them.
Send the key on X-Api-Key against any MCP endpoint, https://<hive>/brain/<brain-id>/mcp. No token request is needed:
X-Api-Key: <api-key>
curl -X POST https://<hive>/brain/<brain-id>/mcp \
-H "X-Api-Key: $HIVE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
- The key grants exactly what the service account grants — the same brain access, the same permission layers, the same
allow_mcp_editingrequirement. - The key must be sent on
X-Api-Key. Sending it as anAuthorization: Bearertoken is refused, with a message saying so. - A
401means the key is wrong, revoked or expired — replace it. A503withRetry-Aftermeans the Hive could not verify the key just then — retry; do not reissue anything. - The key shares the client secret's expiry and rotates whenever the secret is reissued.
- Service accounts created before API keys existed get a key at their next Reissue secret.
Optionally, exchange the key for an access token: POST /brain/<brain-id>/token with grant_type=api_key and api_key=<api-key> returns a 1-hour access token and no refresh token.
A token obtained with grant_type=api_key stays valid until it expires (up to 1 hour) even after the key is reissued, whereas a key sent directly in X-Api-Key stops working on the next request.
VS Code (SSH remote / headless)
Where the OAuth sign-in cannot open a browser, put the key in mcp.json as a header and keep it out of the file with a masked prompt:
{
"inputs": [
{ "id": "hive-api-key", "type": "promptString",
"description": "Hive API key", "password": true }
],
"servers": {
"aiqbee-hive": {
"type": "http",
"url": "https://<hive>/brain/<brain-id>/mcp",
"headers": { "X-Api-Key": "${input:hive-api-key}" }
}
}
}
Connecting from Azure AI Foundry
Foundry offers several authentication methods for MCP tools. Only one suits a service account.
| Foundry option | Works with Hive? |
|---|---|
| Key-based | Yes — use this. Send the service account API key. |
| OAuth identity passthrough | Authenticates the signed-in Foundry user, not a service account. Works if you want each user to act as themselves and they each have a Hive account. |
| Microsoft Entra ID (agent or project identity) | Not supported. |
| Unauthenticated | Not supported. Every MCP endpoint requires a credential. |
- In Hive, create a service account under Admin → Service Accounts and copy the API key shown at creation. Use one service account per integration so it can be revoked without affecting anything else.
- Grant it access to the brain you want the agent to use (step 2 above). Service accounts get no access implicitly.
- In the Foundry portal, open your project and go to Build → Tools, then Add tool → Custom → Model Context Protocol.
- Fill in the connection: endpoint
https://<hive>/brain/<brain-id>/mcp, Authentication Key-based, Credential nameX-Api-Key, Credential value your API key. Then select Connect.
Worth knowing
- Anyone with access to the Foundry project can read a credential stored in a project connection. Use one least-privilege service account per integration.
- The key stops working on the secret's expiry date. Set a reminder before then — Foundry stores the value statically, so someone has to paste a new one in.
- Require approval defaults to
always. A blanketneverauto-approves writes and deletes too, so name the read-only tools instead and leave anything that writes onalways. - Use the brain-specific endpoint, so the brain_id parameter disappears from the tool schemas and the model has less to get wrong.
A minimal Python client
Most MCP SDKs will handle this for you. If you are wiring it by hand, the whole surface is this small:
import os, httpx
BASE = os.environ["HIVE_BASE_URL"]
BRAIN_ID = os.environ["HIVE_BRAIN_ID"]
class Hive:
"""Thin Hive MCP client. Fetches a token lazily and retries once on 401."""
def __init__(self) -> None:
self._client = httpx.Client(timeout=30)
self._token: str | None = None
def _get_token(self) -> str:
r = self._client.post(
f"{BASE}/brain/{BRAIN_ID}/token",
data={
"grant_type": "client_credentials",
"client_id": os.environ["HIVE_CLIENT_ID"],
"client_secret": os.environ["HIVE_CLIENT_SECRET"],
},
)
r.raise_for_status()
return r.json()["access_token"]
def call(self, tool: str, **arguments: object) -> dict:
if self._token is None:
self._token = self._get_token()
def _post() -> httpx.Response:
return self._client.post(
f"{BASE}/brain/{BRAIN_ID}/mcp",
headers={
"Authorization": f"Bearer {self._token}",
"Accept": "application/json, text/event-stream",
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {"name": tool, "arguments": arguments},
},
)
response = _post()
if response.status_code == 401: # token expired mid-session
self._token = self._get_token()
response = _post()
response.raise_for_status()
return response.json()
hive = Hive()
print(hive.call("aiqbee_search", query="rate limiting decision"))
Troubleshooting
| Symptom | Cause |
|---|---|
invalid_client (401) at the token endpoint |
Wrong client_id/secret, the account is deactivated, or its secret has expired. Reissue from the admin page. |
invalid_grant (400) when refreshing a token |
The secret was reissued or has expired, the account is deactivated or deleted, or the refresh token itself expired. Request a new token with grant_type=client_credentials and the current secret (reactivate the account first if it was deactivated). |
401 on the MCP endpoint with X-Api-Key |
The key is wrong, revoked or expired. Reissue the secret from the admin page and use the new key. |
503 with Retry-After |
The Hive could not verify the key just then. Retry after the delay; do not reissue. |
| Token works, every tool reports no access | Step 2 was skipped — the service account has no grant on that brain, or you are calling a different brain's id. |
| Reads fine, writes refused | Access level is read, or the brain has allow_mcp_editing switched off. |
| Audit history tools refuse | Audit is off for that brain. Enabling it needs a paid licence (Licensing & tiers), but reading existing history does not — a brain that already has audit on keeps serving its history. |
| Audit history refuses for a service account | Audit history is owner-only. A service account with read or read_write cannot read it even though its own writes are recorded there. |
| 406 Not Acceptable | Missing Accept: application/json, text/event-stream. The streamable-HTTP transport requires both. |