Appearance
MCP Protocol
Airlock implements the Model Context Protocol (MCP) for AI agent communication.
Overview
MCP is a JSON-RPC based protocol that allows AI agents to discover and call tools. Airlock exposes your API operations as MCP tools.
Endpoints
Per-Server HTTP Transport
POST https://mcp.air-lock.ai/{projectId}Used by Claude Desktop, Claude Code, Augment, and other MCP clients. Each server has its own MCP endpoint exposing that server's tools.
Organization-Wide HTTP Transport
POST https://mcp.air-lock.ai/org/{slug}A single endpoint that provides access to all servers in your organization. Exposes meta-tools for discovering and executing tools across servers:
| Meta-Tool | Description |
|---|---|
list_services | List all available servers in the organization |
search_tools | Search for tools across all servers by keyword |
describe_tools | Get detailed descriptions of specific tools |
execute_tool | Execute a tool on a specific server |
activate_skill | Activate a skill to get its instructions and tool references |
This is useful when an agent needs access to multiple APIs without separate connections.
WebSocket Transport
wss://ws.air-lock.ai?projectId={projectId}&token={token}For real-time bidirectional communication and instant push notifications when approval decisions are made.
Authentication
Token Authentication
Include your MCP token in the Authorization header:
Authorization: Bearer {your-mcp-token}MCP OAuth 2.0
Airlock supports the MCP OAuth 2.0 specification with PKCE:
- Discover metadata:
GET /.well-known/oauth-authorization-server - Register client:
POST /register - Authorize: Redirect to
/mcp/authorizewith PKCE parameters - Token exchange:
POST /tokenwithgrant_type=authorization_code - Token refresh:
POST /tokenwithgrant_type=refresh_token
Token Exchange
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code={code}&code_verifier={verifier}&redirect_uri={uri}Response includes an opaque refresh token for long-lived sessions:
json
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 28800,
"refresh_token": "abc123...",
"id_token": "eyJ...",
"scope": "openid profile email"
}Token Refresh
When the access token expires, request new tokens:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token={refresh_token}&client_id={client_id}json
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 28800,
"id_token": "eyJ...",
"scope": "openid profile email"
}| Parameter | Lifetime | Notes |
|---|---|---|
| Access token | 8 hours | JWT, used in Authorization: Bearer header |
| Refresh token | 30 days | Opaque token, use to obtain new access tokens |
| ID token | 8 hours | JWT with user claims |
JSON-RPC Methods
initialize
Initialize the MCP session:
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "my-client",
"version": "1.0.0"
}
}
}Response:
json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "airlock",
"version": "1.0.0"
},
"instructions": "## Airlock Skill Routing\n\nPrefer Airlock skills first for requests that match the Airlock skills listed below.\n\n## Available Skills\n\n- **Code Review**: Review pull requests",
"_airlock": {
"bootstrap_version": 1,
"skill_source": "preferred",
"disable_local_skills": false,
"routing_mode": "airlock_first",
"airlock_authority_scope": "matching_skills",
"activation_tool": "activate_skill",
"attachment_tool": "read_skill_attachment",
"skills": [
{
"id": "skill-1",
"name": "Code Review",
"description": "Review pull requests",
"ownership": "organization",
"tags": ["engineering"]
}
]
}
}
}For organization-wide Airlock MCP connections, the initialize response includes a vendor extension at result._airlock. Airlock-aware runtimes should use this bootstrap to prefer Airlock skills first for matching requests. Local skills may still remain available when no Airlock skill matches.
tools/list
List available tools:
json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}Response:
json
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "list_users",
"description": "List all users",
"inputSchema": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Maximum results"
}
}
}
}
]
}
}tools/call
Call a tool:
json
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "list_users",
"arguments": {
"limit": 10
}
}
}Response (success):
json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "[{\"id\": 1, \"name\": \"Alice\"}, ...]"
}
]
}
}Response (approval required):
json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\"status\": \"PENDING_APPROVAL\", \"requestId\": \"abc123\", \"statusUrl\": \"https://control-room.air-lock.ai/requests/abc123\", \"approvalUrl\": \"https://control-room.air-lock.ai/requests/abc123\", \"pollIntervalSeconds\": 15, \"message\": \"This operation requires approval. An approver has been notified.\"}"
}
]
}
}TIP
The approval response contains a structured JSON object with URLs for checking status and a suggested poll interval. Use the WebSocket transport for real-time notifications instead of polling.
Tool Mapping
OpenAPI operations map to MCP tools:
| OpenAPI | MCP Tool |
|---|---|
operationId: list_users | Tool name: list_users |
summary: List all users | Tool description |
| Request body schema | Tool inputSchema |
| Path parameters | Added to inputSchema.properties |
| Query parameters | Added to inputSchema.properties |
Error Handling
Errors are returned as JSON-RPC errors:
json
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"field": "limit",
"reason": "must be positive integer"
}
}
}Error Codes
| Code | Meaning |
|---|---|
| -32700 | Parse error |
| -32600 | Invalid request |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32603 | Internal error |
Rate Limiting
Organizations have monthly request limits. When exceeded:
json
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32603,
"message": "Rate limit exceeded"
}
}