# MCP Authentication Architecture
## Overview
This document describes the authentication architecture for the MCP (Model Context Protocol) server implementation in mrapids, following the **Semantic + Guidance** pattern with clean separation of concerns.
---
## Core Principle: Separation of Concerns
```
┌─────────────────────────────────────────────────────────────────────┐
│ MCP SERVER (Orchestrator) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ What MCP knows/provides: │
│ ├── operation_id (from spec parsing) │
│ ├── parameters (from user/AI conversation) │
│ └── environment (from user: "dev", "staging", "prod") │
│ │
│ What MCP does NOT do: │
│ ├── Load config files with credentials │
│ ├── Resolve ${VAR} placeholders │
│ ├── Cache or store credentials in memory │
│ └── Validate credential values │
│ │
└─────────────────────────────────────────────────────────────────────┘
│
│ subprocess (process isolation)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ mrapids run (Executor) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Receives: mrapids run <operation> --env <environment> [--params] │
│ │
│ Automatically handles: │
│ ├── config/{env}.yaml → base_url, auth schemes, headers │
│ ├── env/.env.{env} → actual secret values │
│ ├── ${VAR} resolution → replaces placeholders with real values │
│ └── Auth application → adds headers/tokens to HTTP request │
│ │
│ Process exits after request (credentials not persisted) │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
---
## Authentication Sources (Three Layers)
### Layer 1: OpenAPI Spec (What auth the API SUPPORTS)
**Location**: `specs/api.yaml`
**Purpose**: Declares which authentication methods each endpoint requires.
```yaml
# OpenAPI 3.x
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
# Swagger 2.0
securityDefinitions:
api_key:
type: apiKey
name: api_key
in: header
petstore_auth:
type: oauth2
flow: implicit
```
### Layer 2: Project Config (What auth you HAVE configured)
**Location**: `config/{environment}.yaml`
**Purpose**: Maps your credentials to auth schemes per environment.
```yaml
# config/development.yaml
base_url: http://localhost:3000
auth:
preferred: bearer
schemes:
bearer:
type: bearer
token: ${DEV_API_TOKEN}
apikey:
type: apiKey
in: header
name: X-API-Key
value: ${DEV_API_KEY}
# config/production.yaml
base_url: https://api.production.com
auth:
preferred: bearer
schemes:
bearer:
type: bearer
token: ${PROD_API_TOKEN}
```
### Layer 3: Environment Secrets (Actual credential VALUES)
**Location**: `env/.env.{environment}`
**Purpose**: Stores actual secret values (NEVER committed to git).
```bash
# env/.env.development
DEV_API_TOKEN=dev-token-12345
DEV_API_KEY=dev-key-abcdef
# env/.env.production
PROD_API_TOKEN=prod-live-token-xyz789
```
---
## Semantic + Guidance Pattern for Auth
### What is Semantic?
**Semantic** = Understanding the complete meaning of an operation, including authentication requirements.
```
Without auth info (incomplete semantic):
┌─────────────────────────────────────┐
│ Operation: getPetById │
│ Method: GET │
│ Path: /pet/{petId} │
│ Parameters: petId (required) │
│ Auth: ??? (unknown) │
└─────────────────────────────────────┘
With auth info (complete semantic):
┌─────────────────────────────────────┐
│ Operation: getPetById │
│ Method: GET │
│ Path: /pet/{petId} │
│ Parameters: petId (required) │
│ Auth: api_key (header: api_key) │ ← Complete understanding
└─────────────────────────────────────┘
```
### What is Guidance?
**Guidance** = Telling the AI what actions are possible and what's needed.
```
Without guidance (reactive):
api_show → "auth: required"
api_preview → creates token
api_run → 401 Unauthorized
AI: "Failed. Now what?"
With guidance (proactive):
api_show → "auth: api_key required"
→ "Status: NOT CONFIGURED for this environment"
→ "Next: mrapids auth connect api_key --env production"
AI: "I should tell user to configure auth first"
```
---
## Auth Detection vs Auth Validation
| **What it does** | Shows what auth is required | Checks if credentials work |
| **Data source** | OpenAPI spec only | Makes actual API call |
| **Credentials needed** | No | Yes |
| **Safe for MCP** | Yes | No |
| **When to use** | Always (semantic info) | Only at execution time |
### MCP Should Do: Auth Detection
```json
{
"auth": {
"required": true,
"schemes": [
{
"name": "api_key",
"type": "apiKey",
"location": "header",
"header_name": "api_key"
}
]
}
}
```
### MCP Should NOT Do: Auth Validation
```
❌ Load ConfigLoader (credentials in memory)
❌ Check if ${VAR} resolves correctly
❌ Make test API calls to validate tokens
❌ Cache credential values
```
---
## MCP Tool Responsibilities
| `api_help` | - | - |
| `api_find` | query | Spec parsing |
| `api_show` | operation_id, environment | Auth requirements from spec |
| `api_query` | operation_id | Parameter details |
| `api_preview` | operation_id, params, environment | Preview token generation |
| `api_run` | preview_id, **environment** | Config loading, auth, HTTP |
| `api_auth` | environment | Config file existence check |
---
## Environment Flow
The **only** credential-related input from the Agent is the environment name:
```
┌─────────────────────────┐
│ Agent Decision: │
│ "User wants production"│
│ │ │
│ ▼ │
│ environment = "prod" │ ← This is ALL MCP passes
└─────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ mrapids run getPetById --param petId=123 --env prod │
│ │
│ Internally (subprocess): │
│ 1. ConfigLoader::load("prod") │
│ 2. Reads config/prod.yaml │
│ 3. Reads env/.env.prod │
│ 4. Resolves ${PROD_API_KEY} → "actual-secret" │
│ 5. Applies auth to request │
│ 6. Sends HTTP request │
│ 7. Returns response │
│ 8. Process exits (credentials gone from memory) │
└─────────────────────────────────────────────────────┘
```
---
## api_show Response with Auth (Recommended)
```json
{
"data": {
"operation_id": "getPetById",
"method": "GET",
"path": "/pet/{petId}",
"summary": "Find pet by ID",
"parameters": [
{
"name": "petId",
"location": "path",
"type": "integer",
"required": true
}
],
"auth": {
"required": true,
"schemes": [
{
"name": "api_key",
"type": "apiKey",
"location": "header",
"header_name": "api_key",
"description": "API key for authentication"
}
]
}
},
"guidance": {
"ready": true,
"blockers": [],
"next_action": {
"tool": "api_query",
"params": {"operation_id": "getPetById"},
"reason_code": "get_parameter_details"
},
"auth_hint": "Ensure config/{env}.yaml has 'api_key' scheme configured"
}
}
```
---
## api_auth Response (Recommended)
```json
{
"data": {
"environment": "production",
"config_exists": true,
"available_schemes": ["bearer", "apikey"],
"preferred_scheme": "bearer"
},
"guidance": {
"ready": true,
"message": "Auth is configured for 'production' environment",
"next_action": {
"tool": "api_find",
"params": {"query": "your search term"},
"reason_code": "start_discovery"
}
}
}
```
**Note**: `api_auth` checks file existence and YAML structure, NOT credential values.
---
## Security Architecture
### Why Subprocess Isolation?
```
┌─────────────────────────────────────────────────────────────────────┐
│ MCP Server Process │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Spec Parser │ │ Preview │ │ Guidance Engine │ │
│ │ (read-only) │ │ Tokens │ │ (flow control) │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ❌ NO ConfigLoader │
│ ❌ NO credential resolution │
│ ❌ NO secrets in memory │
│ ✅ Spec parsing only │
│ ✅ File existence checks only │
└─────────────────────────────────────────────────────────────────────┘
│
│ subprocess (isolated process)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ mrapids run (Short-lived) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ConfigLoader │→ │ Auth Apply │→ │ HTTP Request │ │
│ │(loads creds)│ │ (real vals) │ │ (sends request) │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ✅ Loads credentials (short-lived in memory) │
│ ✅ Process exits after request │
│ ✅ Credentials not persisted │
└─────────────────────────────────────────────────────────────────────┘
```
### Benefits
| **Memory safety** | Credentials only in subprocess memory |
| **Process isolation** | MCP compromise doesn't expose secrets |
| **Short-lived** | Subprocess exits, credentials gone |
| **No caching** | Fresh credential load each time |
| **Rotation friendly** | Credential changes picked up immediately |
---
## Existing Commands for Auth
| `mrapids auth detect` | Analyze spec auth requirements | Semantic info |
| `mrapids auth detect --operations` | Per-operation requirements | Detailed semantic |
| `mrapids auth detect --format json` | JSON output | MCP integration |
| `mrapids auth connect` | Configure credentials | User setup |
| `mrapids auth validate` | Test credentials | User verification |
| `mrapids auth list` | Show configured profiles | User info |
---
## Implementation Notes
### Current State
| `src/core/spec.rs` | ✅ Works | Used by `auth detect` |
| `src/core/parser.rs` | ❌ Incomplete | TODO for Swagger 2.0 security |
| `mcp.rs api_show` | ⚠️ Partial | Shows "See api_query" placeholder |
| `mcp.rs api_auth` | ⚠️ Subprocess | Could check file structure directly |
### Recommended Fix
MCP `api_show` should either:
1. **Option A**: Call `mrapids auth detect --format json` subprocess
2. **Option B**: Use `AuthDetector` from `src/core/auth/detection.rs` directly
Both options provide auth info without loading credentials.
---
## Summary
| Environment selection | User → Agent → MCP |
| Auth requirements (semantic) | MCP (from spec) |
| Config file structure check | MCP (file existence only) |
| Config file location | mrapids (knows `.mrapids/` structure) |
| Secret resolution | mrapids (ConfigLoader in subprocess) |
| Auth application | mrapids (run command) |
| API execution | mrapids |
**Key Principle**: MCP provides `--env {environment}`. mrapids handles everything else.