Protocols
Pierre implements three protocols on a single http port (8081).
MCP (Model Context Protocol)
Json-rpc 2.0 protocol for ai assistant integration.
Endpoints
POST /mcp- main mcp endpointGET /mcp/sse/{session_id}- sse transport for streaming (session-scoped)
Transport
Pierre supports both http and sse transports:
- http: traditional request-response
- sse: server-sent events for streaming responses
Sdk handles transport negotiation automatically.
Authentication
Mcp requests require jwt bearer token in authorization header:
Authorization: Bearer <jwt_token>
Obtained via oauth2 flow (sdk handles automatically).
Request Format
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "get_activities",
"arguments": {
"limit": 5
}
}
}
Response Format
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"content": [
{
"type": "text",
"text": "[activity data...]"
}
]
}
}
Output Format Parameter
Most data-returning tools support an optional format parameter for output serialization:
| Format | Description | Use Case |
|---|---|---|
json | Standard JSON (default) | Universal compatibility |
toon | Token-Oriented Object Notation | ~40% fewer LLM tokens |
Example with TOON format:
{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "get_activities",
"arguments": {
"provider": "strava",
"limit": 100,
"format": "toon"
}
}
}
TOON format responses include format: "toon" and content_type: "application/vnd.toon" in the result. Use TOON for large datasets (year summaries, batch analysis) to reduce LLM context usage.
See TOON specification for format details.
MCP Methods
initialize- start sessiontools/list- list available toolstools/call- execute toolresources/list- list resourcesprompts/list- list prompts
Implementation: src/mcp/protocol.rs, src/protocols/universal/
OAuth2 Authorization Server
Rfc 7591 (dynamic client registration) + rfc 7636 (pkce) compliant oauth2 server for mcp client authentication.
Endpoints
GET /.well-known/oauth-authorization-server- server metadata (rfc 8414)POST /oauth2/register- dynamic client registrationGET /oauth2/authorize- authorization endpointPOST /oauth2/token- token endpointGET /oauth2/jwks- json web key setGET /.well-known/jwks.json- jwks at standard oidc locationPOST /oauth2/validate-and-refresh- validate and refresh jwt tokensPOST /oauth2/token-validate- validate jwt token
Registration Flow
- client registration (rfc 7591):
# local development (http allowed for localhost)
curl -X POST http://localhost:8081/oauth2/register \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": ["http://localhost:35535/oauth/callback"],
"client_name": "My MCP Client (Dev)",
"grant_types": ["authorization_code"]
}'
# production (https required)
curl -X POST https://api.example.com/oauth2/register \
-H "Content-Type: application/json" \
-d '{
"redirect_uris": ["https://client.example.com/oauth/callback"],
"client_name": "My MCP Client",
"grant_types": ["authorization_code"]
}'
Response:
{
"client_id": "generated_client_id",
"client_secret": "generated_secret",
"redirect_uris": ["http://localhost:35535/oauth/callback"],
"grant_types": ["authorization_code"]
}
callback url security: redirect_uris using http only permitted for localhost/127.0.0.1 in development. Production clients must use https to protect authorization codes from interception.
- authorization request:
GET /oauth2/authorize?
client_id=<client_id>&
redirect_uri=<redirect_uri>&
response_type=code&
code_challenge=<pkce_challenge>&
code_challenge_method=S256
User authenticates in browser, redirected to:
<redirect_uri>?code=<authorization_code>
- token exchange:
curl -X POST http://localhost:8081/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&\
code=<authorization_code>&\
client_id=<client_id>&\
client_secret=<client_secret>&\
redirect_uri=<redirect_uri>&\
code_verifier=<pkce_verifier>"
Response:
{
"access_token": "jwt_token",
"token_type": "Bearer",
"expires_in": 86400
}
Jwt access token used for all mcp requests.
PKCE Requirement
Pierre enforces pkce (rfc 7636) for all authorization code flows. Clients must:
- generate code verifier (43-128 characters)
- create code challenge:
base64url(sha256(verifier)) - include challenge in authorization request
- include verifier in token request
Server Discovery (RFC 8414)
Pierre provides oauth2 server metadata for automatic configuration:
curl http://localhost:8081/.well-known/oauth-authorization-server
Response includes:
{
"issuer": "http://localhost:8081",
"authorization_endpoint": "http://localhost:8081/oauth2/authorize",
"token_endpoint": "http://localhost:8081/oauth2/token",
"jwks_uri": "http://localhost:8081/oauth2/jwks",
"registration_endpoint": "http://localhost:8081/oauth2/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code"],
"code_challenge_methods_supported": ["S256"]
}
Issuer url configurable via OAUTH2_ISSUER_URL environment variable.
JWKS Endpoint
Public keys for jwt token verification available at /oauth2/jwks:
curl http://localhost:8081/oauth2/jwks
Response (rfc 7517 compliant):
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "key_2024_01_01",
"n": "modulus_base64url",
"e": "exponent_base64url"
}
]
}
cache-control headers: jwks endpoint returns Cache-Control: public, max-age=3600 allowing browsers to cache public keys for 1 hour.
Key Rotation
Pierre supports rs256 key rotation with grace period:
- new keys generated with timestamp-based kid (e.g.,
key_2024_01_01_123456) - old keys retained during grace period for existing token validation
- tokens issued with old keys remain valid until expiration
- new tokens signed with current key
Clients should:
- Fetch jwks on startup
- Cache public keys for 1 hour (respects cache-control header)
- Refresh jwks if unknown kid encountered
- Verify token signature using matching kid
Rate Limiting
Oauth2 endpoints protected by per-ip token bucket rate limiting:
| endpoint | requests per minute |
|---|---|
/oauth2/authorize | 60 (1/second) |
/oauth2/token | 30 (1/2 seconds) |
/oauth2/register | 10 (1/6 seconds) |
Rate limit headers included in all responses:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1704067200
429 response when limit exceeded:
{
"error": "rate_limit_exceeded",
"error_description": "Rate limit exceeded. Retry after 42 seconds."
}
Headers:
Retry-After: 42
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Implementation: src/oauth2_server/, src/oauth2_server/rate_limiting.rs
A2A (Agent-to-Agent Protocol)
Protocol for autonomous ai systems to communicate.
Endpoints
GET /a2a/status- protocol statusGET /a2a/tools- available toolsPOST /a2a/execute- execute toolGET /a2a/monitoring- monitoring info
Authentication
A2A endpoints use JWT Bearer authentication (same as MCP):
Authorization: Bearer <jwt_token>
Create jwt token via admin endpoint:
curl -X POST http://localhost:8081/api/keys \
-H "Authorization: Bearer <admin_jwt>" \
-H "Content-Type: application/json" \
-d '{"name": "My A2A System", "tier": "professional"}'
Agent Cards
Agents advertise capabilities via agent cards:
{
"agent_id": "fitness-analyzer",
"name": "Fitness Analyzer Agent",
"version": "1.0.0",
"capabilities": [
"activity_analysis",
"performance_prediction",
"goal_tracking"
],
"endpoints": [
{
"path": "/a2a/execute",
"method": "POST",
"description": "Execute fitness analysis"
}
]
}
Request Format
{
"tool": "analyze_activity",
"parameters": {
"activity_id": "12345",
"analysis_type": "comprehensive"
}
}
Response Format
{
"success": true,
"result": {
"analysis": {...},
"recommendations": [...]
}
}
Implementation: src/a2a/, src/protocols/universal/
REST API
Traditional rest endpoints for web applications.
Authentication Endpoints
POST /api/auth/register- user registration (admin-provisioned)POST /api/auth/login- user loginPOST /api/auth/logout- logoutPOST /api/auth/refresh- refresh jwt token
Provider OAuth Endpoints
GET /api/oauth/auth/{provider}/{user_id}- initiate oauth (strava, garmin, fitbit, whoop)GET /api/oauth/callback/{provider}- oauth callbackGET /api/oauth/status- connection status
Admin Endpoints
POST /admin/setup- create admin userPOST /admin/users- manage usersGET /admin/analytics- usage analytics
Configuration Endpoints
GET /api/configuration/catalog- config catalogGET /api/configuration/profiles- available profilesGET /api/configuration/user- user configPUT /api/configuration/user- update config
Implementation: src/routes.rs, src/admin_routes.rs, src/configuration_routes.rs
SSE (Server-Sent Events)
Real-time notifications for oauth completions and system events.
Endpoint
GET /notifications/sse/:user_id
Event Types
oauth_complete- oauth flow completedoauth_error- oauth flow failedsystem_status- system status update
Example
const eventSource = new EventSource('/notifications/sse/user-123');
eventSource.onmessage = function(event) {
const notification = JSON.parse(event.data);
if (notification.type === 'oauth_complete') {
console.log('OAuth completed for provider:', notification.provider);
}
};
Implementation: src/sse/routes.rs, src/sse/
Protocol Comparison
| feature | mcp | oauth2 | a2a | rest |
|---|---|---|---|---|
| primary use | ai assistants | client auth | agent comms | web apps |
| auth method | jwt bearer | - | api key | jwt bearer |
| transport | http + sse | http | http | http |
| format | json-rpc 2.0 | oauth2 | json | json |
| implementation | src/mcp/ | src/oauth2_server/ | src/a2a/ | src/routes/ |
Choosing a Protocol
- ai assistant integration: use mcp (claude, chatgpt)
- web application: use rest api
- autonomous agents: use a2a
- client authentication: use oauth2 (for mcp clients)
All protocols share the same business logic via src/protocols/universal/.