# MCP Transport & Authentication Architecture
**Status:** Analysis Complete
**Date:** 2026-01-22
**Purpose:** Document the correct architecture for remote MCP server authentication
---
## Executive Summary
After researching MCP specifications, client support, and authentication patterns, this document outlines the **actual** state of MCP remote server architecture and recommends a path forward for Jana Earth Data's hosted MCP server.
---
## Current MCP Landscape (January 2026)
### Transport Evolution
| Transport | Status | Specification |
|-----------|--------|---------------|
| **stdio** | Active | Local process communication |
| **HTTP+SSE** | **Deprecated** (March 2025) | Dual endpoint (`/sse` + `/messages`) |
| **Streamable HTTP** | **Current Standard** | Single endpoint (`/mcp`) |
MCP deprecated SSE in favor of Streamable HTTP because:
- SSE required two endpoints, complicating implementation
- Persistent connections made middleware/security inspection difficult
- Authentication headers were hard to pass during SSE handshake
### Client Support Matrix
| Client | stdio | HTTP+SSE | Streamable HTTP | Auth Headers | Query Param Token |
|--------|-------|----------|-----------------|--------------|-------------------|
| **Cursor** | ✅ | ✅ | ✅ (v1.0+, buggy) | ❌ Feature request | ✅ Works |
| **Claude Desktop** | ✅ | ✅ | ❓ Unknown | ❌ | ✅ Works |
| **Claude Code** | ✅ | ✅ | ❓ Unknown | ❌ | ✅ Works |
| **mcp-remote proxy** | N/A | ✅ | ❌ | ✅ | N/A |
**Key Finding:** No major MCP client currently supports passing custom `Authorization` headers to SSE endpoints. The only working options are:
1. **Token in URL query parameter**: `/sse?token=xxx`
2. **mcp-remote proxy**: Local process that adds headers before forwarding
---
## Authentication Options Analysis
### Option 1: Token in Query Parameter (Pragmatic)
```
https://mcp.janaearth.com/sse?token=abc123def456
```
**Pros:**
- Works with all current clients (Cursor, Claude Desktop, Claude Code)
- Simple to implement
- No user-side dependencies
**Cons:**
- Token visible in URLs, server logs, browser history
- Less secure than header-based auth
- Not recommended by security best practices
**Mitigation:**
- Always use HTTPS (tokens encrypted in transit)
- Short-lived tokens with rotation
- Server-side log sanitization
- Rate limiting per token
### Option 2: mcp-remote Proxy
Users run a local proxy that adds headers:
```json
{
"mcpServers": {
"jana": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.janaearth.com/sse",
"--header",
"Authorization:${JANA_TOKEN}"
],
"env": {
"JANA_TOKEN": "Token your_token_here"
}
}
}
}
```
**Pros:**
- Tokens not exposed in URLs
- Works today
**Cons:**
- Requires Node.js installed
- Extra complexity for users
- Described as "experimental" by maintainers
- Additional latency (local proxy hop)
### Option 3: Streamable HTTP with OAuth 2.1 (Future Standard)
The MCP specification recommends OAuth 2.1 for Streamable HTTP:
1. Client connects to `/mcp`
2. Server returns `401 Unauthorized` with `WWW-Authenticate` header
3. Client performs OAuth flow (dynamic client registration)
4. Client retries with Bearer token
**Pros:**
- Standards-compliant (RFC 9728, RFC 7591, RFC 8707)
- Proper token binding prevents reuse attacks
- Token refresh built-in
**Cons:**
- Complex implementation
- Client support still buggy/incomplete
- Requires OAuth authorization server
---
## Recommended Architecture
### Phase 1: Pragmatic (Now)
Support **both** authentication methods on SSE transport:
```
┌─────────────────────────────────────────────────────────────────┐
│ User's Machine │
│ │
│ ┌──────────────┐ OR ┌──────────────────────┐ │
│ │ Cursor/Claude│ │ Cursor/Claude │ │
│ │ Direct SSE │ │ + mcp-remote proxy │ │
│ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │
│ │ /sse?token=xxx │ /sse │
│ │ (query param) + Authorization header │
└─────────┼───────────────────────────────────────┼───────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Jana MCP Server │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Authentication Layer │ │
│ │ │ │
│ │ 1. Check Authorization header (Token/Bearer) │ │
│ │ 2. If not found, check ?token= query parameter │ │
│ │ 3. Create per-session client with user's token │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Implementation:**
- Extract token from `Authorization` header (current implementation)
- **Add fallback** to extract from `?token=` query parameter
- Per-session client creation (already implemented)
### Phase 2: Add Streamable HTTP (Future)
When client support matures:
1. Add `/mcp` endpoint using `fastmcp` or `FastApiMCP`
2. Support OAuth 2.1 authorization flow
3. Keep SSE for backward compatibility with deprecation notice
4. Set sunset date for SSE endpoint
---
## Implementation Plan
### Immediate (Phase 1)
1. **Add query parameter token extraction** to `_extract_auth_token()`
```python
def _extract_auth_token(scope: Scope) -> str | None:
# First try Authorization header
headers = dict(scope.get("headers", []))
auth_header = headers.get(b"authorization", b"").decode()
if auth_header.startswith("Token "):
return auth_header[6:]
if auth_header.startswith("Bearer "):
return auth_header[7:]
# Fallback: check query parameters
query_string = scope.get("query_string", b"").decode()
params = dict(parse_qsl(query_string))
return params.get("token")
```
2. **Update User Manual** with both connection methods:
- Simple: Direct URL with query param token
- Advanced: mcp-remote with header-based auth
3. **Add security logging** to track which auth method was used
### Future (Phase 2)
1. Add Streamable HTTP transport via `FastApiMCP`
2. Implement OAuth 2.1 authorization server discovery
3. Set deprecation timeline for SSE
---
## Security Considerations
### Query Parameter Tokens
| Risk | Mitigation |
|------|------------|
| Token in server logs | Log sanitization, don't log query params |
| Token in browser history | MCP clients are apps, not browsers |
| Token in referrer headers | Not applicable for API calls |
| Token visible in network tools | HTTPS encrypts URL in transit |
### Recommendations
1. **Short-lived tokens** - 24-hour expiry with refresh mechanism
2. **Token rotation** - Users can regenerate tokens
3. **Per-user rate limits** - Prevent abuse
4. **Audit logging** - Track all authenticated requests
5. **HTTPS only** - Reject HTTP connections in production
---
## User Experience Comparison
### Method 1: Query Parameter (Simplest)
**User steps:**
1. Get token from Jana portal
2. Add to MCP config:
```json
{
"mcpServers": {
"jana": {
"url": "https://mcp.janaearth.com/sse?token=YOUR_TOKEN"
}
}
}
```
3. Restart Cursor/Claude
**Complexity:** ⭐ (1/5)
### Method 2: mcp-remote (Most Secure for SSE)
**User steps:**
1. Install Node.js
2. Get token from Jana portal
3. Add to MCP config:
```json
{
"mcpServers": {
"jana": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.janaearth.com/sse",
"--header",
"Authorization:${JANA_TOKEN}"
],
"env": {
"JANA_TOKEN": "Token YOUR_TOKEN"
}
}
}
}
```
4. Restart Cursor/Claude
**Complexity:** ⭐⭐⭐ (3/5)
---
## Decision
**Recommended approach:** Implement Phase 1 now (query param fallback), plan for Phase 2 (Streamable HTTP) when clients mature.
**Rationale:**
- Query param works with all clients today
- Security risks are acceptable with mitigations (HTTPS, short tokens, rate limits)
- Users get simple setup experience
- We can migrate to OAuth 2.1 later without breaking existing users
---
## References
- [MCP Specification - Authorization](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
- [MCP Specification - Security Best Practices](https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices)
- [Why MCP Deprecated SSE](https://blog.fka.dev/blog/2025-06-06-why-mcp-deprecated-sse-and-go-with-streamable-http/)
- [mcp-remote npm package](https://www.npmjs.com/package/mcp-remote)
- [Cursor Forum - SSE Header Support Request](https://forum.cursor.com/t/api-key-for-sse-mcp-servers/63300)
- [Cursor Forum - Streamable HTTP Support](https://forum.cursor.com/t/please-implement-streamable-http-on-cursor-mcp/82984)