MCP Server Endpoints
Register and manage Model Context Protocol servers, verify agent access to tools, and expose OAuth 2.0 Protected Resource Metadata.
Register MCP Server
POST /api/v1/mcp/servers
Register an MCP server with Bulwark so agents can be authorized to call its tools.
Headers
Authorization: Bearer <apiKey>X-Bulwark-Tenant: <tenant-id>
Body
{
"name": "file-ops-server",
"slug": "file-ops-server",
"description": "File system operations for the workspace agent",
"resource_uri": "https://mcp.example.com",
"tools": [
{
"name": "read_file",
"description": "Read a file by path",
"scopes_required": ["files:read"]
},
{
"name": "write_file",
"description": "Write or overwrite a file",
"scopes_required": ["files:write"]
}
],
"allowed_agents": ["agent_01j..."]
}
| Field | Required | Description |
|-------|----------|-------------|
| name | Yes | Unique server name within the tenant |
| resource_uri | Yes | Base URI of the MCP server (used as the OAuth 2.0 resource identifier) |
| tools | No | Tool definitions with required scopes for authorization |
| slug | No | URL-safe slug; auto-generated from name if omitted |
| description | No | Human-readable description |
| allowed_agents | No | Agent IDs that may access this server; empty means all agents |
Response 201
{
"id": "01j...",
"tenant_id": "01j...",
"name": "file-ops-server",
"slug": "file-ops-server",
"description": "File system operations for the workspace agent",
"resource_uri": "https://mcp.example.com",
"tools": [
{ "name": "read_file", "scopes_required": ["files:read"] },
{ "name": "write_file", "scopes_required": ["files:write"] }
],
"status": "active",
"allowed_agents": ["agent_01j..."],
"config": {},
"created_at": "2026-03-30T00:00:00Z",
"updated_at": "2026-03-30T00:00:00Z"
}
List MCP Servers
GET /api/v1/mcp/servers
Headers
Authorization: Bearer <apiKey>X-Bulwark-Tenant: <tenant-id>
Response 200
{
"servers": [
{
"id": "01j...",
"tenant_id": "01j...",
"name": "file-ops-server",
"slug": "file-ops-server",
"resource_uri": "https://mcp.example.com",
"tools": [
{ "name": "read_file", "scopes_required": ["files:read"] },
{ "name": "write_file", "scopes_required": ["files:write"] }
],
"status": "active",
"allowed_agents": [],
"config": {},
"created_at": "2026-03-30T00:00:00Z",
"updated_at": "2026-03-30T00:00:00Z"
}
],
"count": 1
}
Get MCP Server
GET /api/v1/mcp/servers/{id}
Response 200
{
"id": "01j...",
"tenant_id": "01j...",
"name": "file-ops-server",
"slug": "file-ops-server",
"description": "File system operations for the workspace agent",
"resource_uri": "https://mcp.example.com",
"tools": [
{ "name": "read_file", "scopes_required": ["files:read"] },
{ "name": "write_file", "scopes_required": ["files:write"] }
],
"status": "active",
"allowed_agents": [],
"config": {},
"created_at": "2026-03-30T00:00:00Z",
"updated_at": "2026-03-30T00:00:00Z"
}
Update MCP Server
PUT /api/v1/mcp/servers/{id}
Replace the mutable fields of a registered MCP server. All body fields are applied as a full replacement of the current values — omit tools or allowed_agents to clear them.
Headers
Authorization: Bearer <apiKey>X-Bulwark-Tenant: <tenant-id>
Body
{
"name": "file-ops-server",
"description": "Updated description",
"resource_uri": "https://mcp.example.com",
"tools": [
{
"name": "read_file",
"description": "Read a file by path",
"scopes_required": ["files:read"]
},
{
"name": "write_file",
"description": "Write or overwrite a file",
"scopes_required": ["files:write"]
}
],
"allowed_agents": ["agent_01j..."]
}
| Field | Required | Description |
|-------|----------|-------------|
| name | Yes | Server name |
| resource_uri | Yes | Base URI of the MCP server |
| description | No | Human-readable description |
| tools | No | Full replacement of tool definitions; omit to clear |
| allowed_agents | No | Agent IDs allowed to access this server; omit to allow all |
slug is not updatable — it is derived from name at registration time and used as a stable identifier in Biscuit scope strings.
Response 200
{
"status": "updated"
}
Response 500
{ "error": "failed to update MCP server" }
Delete MCP Server
DELETE /api/v1/mcp/servers/{id}
Removes the server registration. Agents with existing Biscuit tokens scoped to this server's tools will be denied on next introspection.
Response 200
{
"status": "deleted"
}
Introspect Agent Token
POST /api/v1/mcp/introspect
Public endpoint — no admin credentials required. Called by MCP servers to verify that an incoming agent Biscuit token is valid and grants access to a specific tool.
Bulwark resolves the registered server by server_id, builds the canonical resource string mcp:<server-slug>:<tool> from the stored slug, and validates the agent session against the database — it must belong to the claiming agent, be active, and be unexpired. A cryptographically valid token is denied once its session has been revoked, completed, or expired. Tokens must be minted with rights in the canonical mcp:<server-slug>:<tool> form or introspection denies them.
Body
{
"token": "<biscuit-token>",
"tool": "write_file",
"server_id": "mcp_01j...",
"session_id": "sess_01j...",
"agent_id": "agent_01j..."
}
| Field | Required | Description |
|-------|----------|-------------|
| token | Yes | Biscuit token (base64url) |
| tool | Yes | MCP tool name being invoked |
| server_id | Yes | Registered MCP server ID |
| session_id | Yes | Agent session ID (from token) |
| agent_id | Yes | Agent ID (from token) |
Response 200 — Authorized
{
"active": true,
"agent_id": "agent_01j...",
"tenant_id": "00000000-0000-7000-8000-...",
"session_id": "sess_01j...",
"tool": "write_file",
"expires_at": 1765432100
}
expires_at is the Unix timestamp at which the agent session expires.
Response 200 — Denied
{
"active": false,
"tool": "write_file",
"reason": "token verification failed"
}
Denial reasons: unknown server (server ID not registered or inactive), agent not allowed for this server (the server restricts allowed_agents), session invalid (session missing, owned by another agent, revoked, completed, or expired), token verification failed, or a Biscuit policy reason. Requests missing any required field return 400.
Protected Resource Metadata
GET /api/v1/mcp/servers/{id}/metadata
Returns OAuth 2.0 Protected Resource Metadata per RFC 9728. MCP clients use this discovery document to determine which authorization server issues tokens for this resource.
Response 200
{
"resource": "https://mcp.example.com",
"authorization_servers": [
"https://api.bulwarkauth.com"
],
"bearer_methods_supported": ["header"],
"scopes_supported": ["files:read", "files:write"],
"introspection_endpoint": "https://api.bulwarkauth.com/api/v1/mcp/introspect"
}
This endpoint is unauthenticated and intended for public discovery.