# ποΈ Architecture Documentation
## Overview
JobNimbus MCP Remote Server is a **stateless, secure** proxy that enables multiple Claude Desktop clients to access JobNimbus APIs without storing API keys server-side.
## Core Principles
### 1. π Zero Storage Security
**API keys are NEVER stored anywhere on the server:**
- β
Client sends API key in each request header
- β
Server extracts and validates temporarily
- β
Server uses key only for that request
- β
Server clears key from memory immediately
- β No database storage
- β No file storage
- β No environment variables
### 2. π Stateless Design
Every request is independent:
- No sessions
- No authentication tokens
- No cached credentials
- Each request carries its own API key
### 3. π¦ Per-Client Rate Limiting
Rate limiting is based on **client ID** (IP + user agent hash), not API key:
- Prevents rate limit info from exposing API keys
- Fair resource allocation
- Configurable limits per window
---
## Architecture Diagram
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CLAUDE DESKTOP CLIENTS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Client A (Stamford) Client B (Guilford) Client C β
β βββββββββββββββ βββββββββββββββ ββββββββββββ β
β β API Key A β β API Key B β β Both Keysβ β
β ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββ¬ββββββ β
β β β β β
βββββββββββΌββββββββββββββββββββββββΌββββββββββββββββββββΌββββββββ
β β β
β HTTPS + Headers β β
β X-JobNimbus-Api-Key β β
βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RENDER.COM SERVER β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Express Application β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β β
β β 1. Security Middleware (Helmet) β β
β β 2. API Key Extractor β β
β β 3. Rate Limiter (per client) β β
β β 4. Tool Router β β
β β 5. Error Handler β β
β β β β
β βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ β
β β Tool Execution Engine β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β β
β β β’ get_jobs β’ get_contacts β β
β β β’ search_jobs β’ get_estimates β β
β β β’ analyze_* β’ and more... β β
β β β β
β βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β β β
ββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β
β API Key from request context
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JOBNIMBUS API β
β https://app.jobnimbus.com/api1 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Request Flow
### 1. Client Request
```typescript
POST https://your-server.onrender.com/mcp/tools/call
Headers:
X-JobNimbus-Api-Key: sk_live_xxxxx
X-JobNimbus-Instance: stamford
Content-Type: application/json
Body:
{
"name": "get_jobs",
"arguments": { "size": 10 }
}
```
### 2. API Key Extraction
```typescript
// middleware/apiKeyExtractor.ts
const apiKey = req.headers['x-jobnimbus-api-key'];
const instance = req.headers['x-jobnimbus-instance'];
// Validate format
if (!isValidFormat(apiKey)) {
throw new ValidationError();
}
// Attach temporarily to request
req.apiKey = apiKey;
req.instance = instance;
// Auto-cleanup on response
res.on('finish', () => {
delete req.apiKey;
});
```
### 3. Rate Limiting
```typescript
// middleware/rateLimiter.ts
const clientId = hash(req.ip + req.userAgent);
const limit = rateLimitStore.get(clientId);
if (limit.count > MAX_REQUESTS) {
throw new RateLimitError();
}
limit.count++;
```
### 4. Tool Execution
```typescript
// server/index.ts
const tool = toolRegistry.getTool(name);
const context = {
apiKey: req.apiKey, // From request context
instance: req.instance,
clientId: req.clientId,
};
const result = await tool.execute(args, context);
```
### 5. JobNimbus API Call
```typescript
// services/jobNimbusClient.ts
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${apiKey}`, // Used here
},
});
// Clear from memory immediately
apiKey = '';
```
### 6. Response
```json
{
"success": true,
"data": { /* JobNimbus response */ }
}
```
---
## Security Layers
### Layer 1: Transport Security
- **HTTPS only** (TLS 1.3)
- **Helmet.js** security headers
- **CORS** configured
### Layer 2: Input Validation
- API key format validation
- Request body validation
- Parameter sanitization
### Layer 3: Rate Limiting
- Per-client limits (60 req/min default)
- Configurable windows
- Automatic cleanup
### Layer 4: Error Handling
- Sanitized error messages
- No stack traces in production
- No sensitive data in logs
### Layer 5: Memory Management
- API keys cleared after each request
- No persistent connections
- Automatic garbage collection
---
## Tool System
### Base Tool Class
```typescript
abstract class BaseTool<TInput, TOutput> {
abstract get definition(): MCPToolDefinition;
abstract execute(input: TInput, context: ToolContext): Promise<TOutput>;
}
```
### Example Tool
```typescript
class GetJobsTool extends BaseTool {
get definition() {
return {
name: 'get_jobs',
description: 'Get jobs from JobNimbus',
inputSchema: { /* ... */ },
};
}
async execute(input, context) {
return this.client.get(
context.apiKey, // API key from context
'jobs',
input
);
}
}
```
### Tool Registry
```typescript
class ToolRegistry {
private tools = new Map<string, BaseTool>();
registerTool(tool: BaseTool) {
this.tools.set(tool.definition.name, tool);
}
getTool(name: string): BaseTool | undefined {
return this.tools.get(name);
}
}
```
---
## Deployment Pipeline
### Local Development
```bash
npm run dev # TypeScript watch mode
```
### GitHub Push
```bash
git push origin main
```
### GitHub Actions
1. **CI Workflow** (on every push/PR):
- Install dependencies
- Type check
- Lint
- Build
- Security audit
2. **Deploy Workflow** (on main push):
- Trigger Render deployment
- Wait for deploy
- Health check
### Render.com
1. Reads `render.yaml`
2. Runs `npm ci && npm run build`
3. Runs `npm run start:prod`
4. Monitors `/health` endpoint
5. Auto-scales as needed
---
## Monitoring
### Health Check
```bash
GET /health
Response:
{
"status": "healthy",
"version": "1.0.0",
"uptime": 123456,
"timestamp": "2025-..."
}
```
### Rate Limit Headers
```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1234567890
```
### Logs
- **Info**: Request received, tool executed
- **Warn**: Rate limit exceeded
- **Error**: API failures, validation errors
- **Debug**: Detailed flow (dev only)
All logs are **sanitized** - no API keys logged.
---
## Scalability
### Horizontal Scaling
- Stateless design enables infinite scaling
- Render auto-scales based on load
- No shared state between instances
### Performance
- In-memory rate limiting (fast)
- No database queries (fast)
- Direct pass-through to JobNimbus (minimal latency)
### Costs
- **Starter**: $7/month, 512 MB RAM
- **Standard**: $25/month, 2 GB RAM
- Auto-scale as needed
---
## Adding New Tools
1. Create tool file: `src/tools/category/myTool.ts`
2. Extend `BaseTool`
3. Define `definition` and `execute`
4. Register in `src/tools/index.ts`
**That's it!** Auto-discovered and available.
See `docs/ADDING_TOOLS.md` for detailed guide.
---
## Security Checklist
- [x] API keys never stored
- [x] HTTPS only
- [x] Rate limiting per client
- [x] Input validation
- [x] Error sanitization
- [x] Helmet.js security headers
- [x] No secrets in code/repo
- [x] Logs sanitized
- [x] Memory cleanup
- [x] Security audit in CI
---
## Technology Stack
- **Runtime**: Node.js 18+
- **Language**: TypeScript 5.3+
- **Framework**: Express 4.18
- **Security**: Helmet, CORS
- **Logging**: Winston
- **Validation**: Zod (future)
- **Deployment**: Render.com
- **CI/CD**: GitHub Actions
---
## Future Enhancements
- [ ] Add caching (optional, without API keys)
- [ ] Metrics dashboard
- [ ] Custom domains
- [ ] More tools (~50 total)
- [ ] WebSocket support for real-time
- [ ] Redis for distributed rate limiting