Authentication
Two authentication paths for buzzabout's MCP server — x-api-key for headless agents, OAuth/JWT for interactive clients.
The buzzabout MCP server accepts two authentication paths on the same URL. Pick the one that matches your client.
| Path | Best for | How |
|---|---|---|
x-api-key header | Headless agents, CI, custom scripts, server-side LLM pipelines | Send x-api-key: bz_live_... on every request |
| OAuth → JWT | Interactive MCP clients (Claude Desktop, IDE assistants, chat UIs) | Client redirects to buzzabout, exchanges code for a JWT, sends Authorization: Bearer <jwt> |
Both resolve to the same authenticated User in the server. Every
tool sees the same identity regardless of how the request authenticated.
Per-tool auth-path requirements are documented inline in the
tools reference; headless callers that need to
invoke the assistant should use the REST surface
(POST /v1/ask) instead.
Endpoint
https://mcp.buzzabout-staging.com/mcp/Streamable-HTTP transport. The trailing slash is required — the
unslashed /mcp returns a 307 redirect that strips the request body
in many MCP clients.
Path 1 — x-api-key
Mint a key in the web app under Settings → API keys → New key. Copy
the value (bz_live_...) once; the rest is hashed and not retrievable
again. Lifecycle and rotation are documented under
REST authentication.
Send the header on every MCP request:
POST /mcp/ HTTP/1.1
Host: mcp.buzzabout-staging.com
x-api-key: bz_live_...
Content-Type: application/json
Accept: application/json,text/event-streamimport asyncio
import os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main() -> None:
headers = {"x-api-key": os.environ["BUZZABOUT_KEY"]}
async with streamablehttp_client(
"https://mcp.buzzabout-staging.com/mcp/",
headers=headers,
) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
for tool in tools.tools:
print(tool.name)
result = await session.call_tool(
"buzzabout__list_datasets",
arguments={"limit": 5},
)
print(result.content)
asyncio.run(main())import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.buzzabout-staging.com/mcp/"),
{
requestInit: {
headers: { "x-api-key": process.env.BUZZABOUT_KEY! },
},
},
);
const client = new Client({ name: "my-agent", version: "0.1.0" });
await client.connect(transport);
const tools = await client.listTools();
for (const tool of tools.tools) {
console.log(tool.name);
}
const result = await client.callTool({
name: "buzzabout__list_datasets",
arguments: { limit: 5 },
});
console.log(result.content);curl -X POST https://mcp.buzzabout-staging.com/mcp/ \
-H "x-api-key: $BUZZABOUT_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'Path 2 — OAuth → JWT
Interactive MCP hosts that support OAuth (Claude Desktop, several IDE
assistants, chat UIs) discover the flow automatically via the
WWW-Authenticate: Bearer resource_metadata="..." header.
The user-visible flow:
- The host attempts a tool call without credentials.
- The server responds
401with aWWW-Authenticateheader pointing at the OAuth metadata document. - The host opens a browser tab to
mcp.buzzabout-staging.com/mcp/oauth/authorize. - The user signs in with their buzzabout account.
- The host exchanges the redirect code for a JWT and stores it locally.
- Subsequent requests carry
Authorization: Bearer <jwt>.
The JWT carries the same User claims as an x-api-key request. No
configuration is required on your side beyond pointing the host at the
URL above — see Use in your agent for the
host-side wiring.
Auth resolution internals
Each request hits a dual-auth ASGI middleware:
- Check
x-api-key. If valid → resolve user, proceed. - Otherwise fall back to
Authorization: Bearer <jwt>. If valid → resolve user, proceed. - If neither resolves → respond
401withWWW-Authenticate: Bearer resource_metadata="...".
A single integration can mix transports cleanly: some hosts attach an api-key, others go through OAuth, and the server treats them the same.
Tool errors
Tool errors come back inside the MCP tool/call result as a structured
JSON payload — same error_code taxonomy as the REST API:
{
"error": {
"code": "dataset_not_found",
"message": "Dataset not found",
"status": 404
}
}Match on code (stable); show message to humans. status mirrors
the HTTP status the equivalent REST call would return.
Transport errors (401, 429) come back as MCP transport errors,
not tool-level errors. Handle them at the SDK transport layer.
Next steps
- Use in your agent — host-side wiring.
- Tools reference — every tool, with required parameters.
- REST authentication — same
bz_live_...key works forhttps://api.buzzabout-staging.com/v1/*.