Architecture
Detailed overview of Wellpipe's architecture and design decisions.
System Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ Clients │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ Claude Desktop│ │ Claude Web │ │ ChatGPT │ │
│ │ (MCP) │ │ (REST API) │ │ (REST/MCP) │ │
│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
└──────────┼──────────────────┼──────────────────┼────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Wellpipe │
│ │
│ Self-Hosted (CLI) Cloud (wellpipe.io) │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ @wellpipe/cli │ │ wellpipe-cloud │ │
│ │ │ │ │ │
│ │ ┌───────────┐ │ │ ┌─────────────────┐ │ │
│ │ │MCP Server │ │ │ │ API Routes │ │ │
│ │ └───────────┘ │ │ │ /api/mcp │ │ │
│ │ ┌───────────┐ │ │ │ /api/v1/health │ │ │
│ │ │OAuth Flow │ │ │ └─────────────────┘ │ │
│ │ └───────────┘ │ │ ┌─────────────────┐ │ │
│ │ ┌───────────┐ │ │ │ OAuth Manager │ │ │
│ │ │ .env file │ │ │ └─────────────────┘ │ │
│ │ └───────────┘ │ │ ┌─────────────────┐ │ │
│ └─────────────────┘ │ │ Token Storage │ │ │
│ │ │ (encrypted) │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Shared Packages │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ @wellpipe/core │ ◀────── │ @wellpipe/whoop │ │ │
│ │ │ - Interfaces │ │ - WhoopClient │ │ │
│ │ │ - Types │ │ - MCP Tools │ │ │
│ │ │ - TokenProvider│ │ - API Coverage │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ Health Provider APIs │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ WHOOP API │ │ Oura API │ │ Fitbit API │ │
│ │ │ │ (planned) │ │ (planned) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Package Architecture
Dependency Graph
@wellpipe/core
↑
│ (depends on)
│
@wellpipe/whoop ──────────────→ @wellpipe/cli
│
│ (uses)
↓
wellpipe-cloud
@wellpipe/core
The foundation package with no external dependencies except Zod.
// Exports
export { TokenProvider } from './interfaces/token-provider';
export { HealthProvider } from './interfaces/health-provider';
export type { SleepData, RecoveryData, WorkoutData } from './types/health';
Responsibilities:
- Define interfaces for token management
- Define interfaces for health data access
- Provide common health data types
- Zero runtime dependencies (except zod)
@wellpipe/whoop
WHOOP API integration implementing the core interfaces.
// Exports
export { WhoopClient } from './client';
export { allTools, sleepTools, recoveryTools } from './tools';
export { whoopOAuthConfig } from './oauth';
Responsibilities:
- Implement WHOOP API client
- Define MCP tools for WHOOP data
- Handle WHOOP-specific data transformations
- Provide OAuth configuration (not flow)
@wellpipe/cli
Command-line interface for self-hosting.
Responsibilities:
- Interactive setup wizard
- Local OAuth flow handling
- MCP server for Claude Desktop
- Token storage in .env file
wellpipe-cloud
SaaS backend (private repository).
Responsibilities:
- User authentication (NextAuth)
- Provider OAuth flows
- Encrypted token storage
- MCP and REST API endpoints
- Rate limiting and usage tracking
Data Flow
MCP Request (Claude Desktop)
1. User asks: "How did I sleep?"
2. Claude Desktop calls get-recent-sleep tool
3. MCP request → @wellpipe/cli
4. CLI loads tokens from .env
5. CLI uses @wellpipe/whoop to call WHOOP API
6. WHOOP returns data
7. CLI returns MCP response
8. Claude analyzes and responds
REST API Request (Cloud)
1. User asks: "How did I sleep?"
2. AI makes HTTP request to wellpipe.io
3. Cloud validates API key
4. Cloud decrypts tokens from database
5. Cloud uses @wellpipe/whoop to call WHOOP API
6. WHOOP returns data
7. Cloud returns JSON response
8. AI analyzes and responds
Token Management
Interface
interface TokenProvider {
getAccessToken(): Promise<string>;
getRefreshToken(): Promise<string | undefined>;
updateTokens(access: string, refresh?: string, expiresAt?: Date): Promise<void>;
isExpired(): Promise<boolean>;
}
Implementations
| Implementation | Storage | Encryption | Used By |
|---|---|---|---|
| EnvTokenProvider | .env file | None | CLI |
| DatabaseTokenProvider | PostgreSQL | AES-256-GCM | Cloud |
MCP Integration
Tool Definition
const getSleepTool = {
name: 'get-recent-sleep',
description: 'Get sleep data from the last N days',
inputSchema: {
type: 'object',
properties: {
days: { type: 'number', default: 7 }
}
},
handler: async (args, client) => {
const sleep = await client.sleep.getRecent(args.days);
return { content: [{ type: 'text', text: JSON.stringify(sleep) }] };
}
};
Server Setup
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { allTools } from '@wellpipe/whoop';
const server = new Server({
name: 'wellpipe',
version: '1.0.0',
});
// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: allTools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }))
}));
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const tool = allTools.find(t => t.name === request.params.name);
return tool.handler(request.params.arguments, whoopClient);
});
Security Architecture
Cloud Security
┌─────────────────────────────────────────────────┐
│ Cloud Service │
│ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ API Key Hash │ │ Encrypted Tokens │ │
│ │ (bcrypt) │ │ (AES-256-GCM) │ │
│ └──────────────┘ └──────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Validate │ │ Decrypt on │ │
│ │ Request │ │ API Request │ │
│ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────┘
Token Encryption
// Encryption
const cipher = createCipheriv('aes-256-gcm', key, iv);
const encrypted = Buffer.concat([cipher.update(token), cipher.final()]);
const authTag = cipher.getAuthTag();
// Store: iv + authTag + encrypted
// Decryption
const decipher = createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
Rate Limiting
Implementation
// Check rate limit
const usage = await getMonthlyUsage(userId);
const limit = user.subscription === 'PRO' ? Infinity : 1000;
if (usage >= limit) {
throw new RateLimitError('Monthly limit exceeded');
}
// Increment usage
await incrementUsage(userId, 'mcp_request');
Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 2025-01-01T00:00:00.000Z