MCP Server Architecture
The MCP Server is a Model Context Protocol server that provides AI assistants with programmatic access to SureStage functionality. It enables AI tools like Claude Desktop to browse endpoints, manage tenant context, inspect Sandbox state, and build routes with validation.
Overview
The MCP Server implements the Model Context Protocol using @modelcontextprotocol/sdk to expose SureStage capabilities to AI assistants:
- Endpoint Explorer: Browse and search available API endpoints
- Tenant Context Manager: Switch between tenants and manage scope
- Instance Inspector: Inspect Sandbox instances, routes, and state
- Route Builder: Create routes with validation and sample value generation
Architecture
Protocol Implementation
StdioServerTransport
The MCP Server uses stdio for communication with AI assistants:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server(
{
name: 'surestage-mcp-server',
version: '1.0.0'
},
{
capabilities: {
tools: {}
}
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Communication:
- AI assistant launches MCP server as subprocess
- Sends JSON-RPC requests via stdin
- Receives JSON-RPC responses via stdout
- Stderr used for logging
Tool Registration
Tools are registered with the MCP SDK:
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'browse_endpoints',
description: 'Browse available API endpoints',
inputSchema: {
type: 'object',
properties: {
category: { type: 'string', enum: ['auth', 'instances', 'routes'] }
}
}
},
// ... more tools
]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
// Route tool invocation to handler
});
Tool Groups
Endpoint Explorer
Browse and search SureStage API endpoints.
browse_endpoints
List endpoints by category.
Input:
{
"category": "instances"
}
Output:
{
"endpoints": [
{
"method": "GET",
"path": "/api/instances",
"description": "List all Sandbox instances",
"parameters": ["status", "limit", "offset"]
},
{
"method": "POST",
"path": "/api/instances",
"description": "Create a new Sandbox instance",
"body": {
"name": "string",
"protocol": "http|graphql|grpc|websocket"
}
}
]
}
search_endpoints
Search endpoints by keyword.
Input:
{
"query": "create route"
}
Output:
{
"results": [
{
"method": "POST",
"path": "/api/routes",
"relevance": 0.95,
"description": "Create a new route"
}
]
}
Tenant Context
Manage tenant scope for API operations.
list_tenants
List available tenants.
Output:
{
"tenants": [
{
"id": "tenant_abc123",
"slug": "acme-corp",
"name": "Acme Corporation"
}
]
}
switch_tenant
Switch to a different tenant.
Input:
{
"tenantSlug": "acme-corp"
}
Output:
{
"success": true,
"currentTenant": "acme-corp"
}
get_current_tenant
Get current tenant context.
Output:
{
"tenantId": "tenant_abc123",
"slug": "acme-corp",
"name": "Acme Corporation"
}
Instance Inspector
Inspect Sandbox instances and their state.
list_instances
List all Sandbox instances.
Input:
{
"status": "active"
}
Output:
{
"instances": [
{
"id": "inst_abc123",
"name": "User API",
"protocol": "http",
"status": "active",
"url": "http://localhost:4001"
}
]
}
get_instance
Get detailed instance information.
Input:
{
"instanceId": "inst_abc123"
}
Output:
{
"id": "inst_abc123",
"name": "User API",
"protocol": "http",
"status": "active",
"url": "http://localhost:4001",
"routes": [
{
"method": "GET",
"path": "/users",
"responseId": "resp_xyz789"
}
],
"state": {
"user_count": 42,
"last_request": "2026-03-21T10:30:00Z"
}
}
inspect_instance_state
Inspect current mock state.
Input:
{
"instanceId": "inst_abc123"
}
Output:
{
"state": {
"user_count": 42,
"last_request": "2026-03-21T10:30:00Z",
"session_id": "sess_abc123"
},
"history": [
{
"timestamp": "2026-03-21T10:25:00Z",
"changes": {"user_count": 41}
}
]
}
Route Builder
Create routes with validation and sample value generation.
create_route
Create a route with validation.
Input:
{
"instanceId": "inst_abc123",
"method": "GET",
"path": "/users/:id",
"responseBody": {
"id": "{{userId}}",
"email": "{{email}}",
"createdAt": "{{timestamp}}"
},
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
}
}
Output:
{
"routeId": "route_xyz789",
"validation": {
"valid": true,
"warnings": ["Path parameter :id should be validated"]
},
"sampleResponse": {
"id": "usr_abc123",
"email": "alice@example.com",
"createdAt": "2026-03-21T10:30:00Z"
}
}
validate_route
Validate route configuration without creating it.
Input:
{
"method": "POST",
"path": "/users",
"responseBody": {
"id": "{{userId}}"
}
}
Output:
{
"valid": true,
"warnings": [
"POST route should include 201 status code for resource creation"
],
"suggestions": [
"Add validation for request body",
"Consider adding Location header with created resource URL"
]
}
generate_crud_routes
Generate full CRUD route set for a resource.
Input:
{
"instanceId": "inst_abc123",
"resourceName": "user",
"fields": {
"email": "email",
"name": "string",
"age": "integer"
}
}
Output:
{
"routes": [
{
"method": "GET",
"path": "/users",
"description": "List all users"
},
{
"method": "GET",
"path": "/users/:id",
"description": "Get a single user"
},
{
"method": "POST",
"path": "/users",
"description": "Create a new user"
},
{
"method": "PUT",
"path": "/users/:id",
"description": "Update a user"
},
{
"method": "DELETE",
"path": "/users/:id",
"description": "Delete a user"
}
]
}
Helper Functions
pluralize
Convert singular resource names to plural.
function pluralize(word: string): string {
const irregulars = {
'person': 'people',
'child': 'children',
'man': 'men',
'woman': 'women'
};
if (irregulars[word]) return irregulars[word];
if (word.endsWith('y')) return word.slice(0, -1) + 'ies';
if (word.endsWith('s')) return word + 'es';
return word + 's';
}
Examples:
user→userscompany→companiesperson→people
generateSampleValue
Generate realistic sample values based on field name and type.
function generateSampleValue(fieldName: string, fieldType: string): any {
// Email detection
if (fieldName.includes('email')) {
return 'alice@example.com';
}
// Phone detection
if (fieldName.includes('phone')) {
return '+1-555-123-4567';
}
// URL detection
if (fieldName.includes('url') || fieldName.includes('link')) {
return 'https://example.com';
}
// Date detection
if (fieldName.includes('date') || fieldName.includes('time') || fieldName.includes('at')) {
return new Date().toISOString();
}
// Status detection
if (fieldName.includes('status')) {
return 'active';
}
// Boolean detection
if (fieldType === 'boolean') {
return true;
}
// Integer detection
if (fieldType === 'integer' || fieldType === 'number') {
return fieldName.includes('count') ? 42 : 123;
}
// Default: string
return `sample_${fieldName}`;
}
Examples:
email→alice@example.comphone_number→+1-555-123-4567createdAt→2026-03-21T10:30:00.000Zstatus→activeuser_count(integer) →42
generateSampleObject
Generate a sample object from field definitions.
function generateSampleObject(fields: Record<string, string>): object {
const sample = {};
for (const [fieldName, fieldType] of Object.entries(fields)) {
sample[fieldName] = generateSampleValue(fieldName, fieldType);
}
return sample;
}
Input:
{
"email": "string",
"age": "integer",
"createdAt": "string"
}
Output:
{
"email": "alice@example.com",
"age": 123,
"createdAt": "2026-03-21T10:30:00.000Z"
}
validateRouteConfig
Validate route configuration and provide suggestions.
function validateRouteConfig(route: RouteConfig): ValidationResult {
const errors = [];
const warnings = [];
const suggestions = [];
// Check method
if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(route.method)) {
errors.push(`Invalid HTTP method: ${route.method}`);
}
// Check path format
if (!route.path.startsWith('/')) {
errors.push('Path must start with /');
}
// Check status code for method
if (route.method === 'POST' && route.statusCode !== 201) {
warnings.push('POST routes should return 201 for resource creation');
}
// Check response body
if (!route.responseBody || Object.keys(route.responseBody).length === 0) {
warnings.push('Response body is empty');
}
// CRUD validation
if (route.method === 'POST' && !route.path.match(/\/\w+$/)) {
suggestions.push('POST routes typically target collection endpoints (e.g., /users)');
}
if (['GET', 'PUT', 'PATCH', 'DELETE'].includes(route.method) && !route.path.includes(':')) {
suggestions.push('Consider adding path parameter for resource ID (e.g., /users/:id)');
}
return {
valid: errors.length === 0,
errors,
warnings,
suggestions
};
}
Validation Rules:
- Method must be valid HTTP verb
- Path must start with
/ - POST routes should return 201
- Response body should not be empty
- CRUD routes should follow REST conventions
Usage Example
AI Assistant Interaction
User asks Claude:
"Create a GET /users route that returns a list of users"
Claude uses MCP tools:
-
Get current tenant:
Tool: get_current_tenant
Result: {"tenantId": "tenant_abc123", "slug": "acme-corp"} -
List instances:
Tool: list_instances
Input: {"status": "active"}
Result: {"instances": [{"id": "inst_abc123", "name": "User API"}]} -
Create route:
Tool: create_route
Input: {
"instanceId": "inst_abc123",
"method": "GET",
"path": "/users",
"responseBody": {
"users": [
{
"id": "{{userId}}",
"email": "{{email}}",
"createdAt": "{{timestamp}}"
}
]
},
"statusCode": 200
}
Result: {
"routeId": "route_xyz789",
"validation": {"valid": true},
"sampleResponse": {
"users": [
{
"id": "usr_abc123",
"email": "alice@example.com",
"createdAt": "2026-03-21T10:30:00Z"
}
]
}
}
Claude responds:
"I've created a GET /users route in your User API Sandbox. It will return a list of users with id, email, and createdAt fields. Here's a sample response: ..."
Configuration
Claude Desktop Integration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"surestage": {
"command": "node",
"args": ["/path/to/mcp-server/dist/index.js"],
"env": {
"SURESTAGE_API_URL": "https://api.surestage.com",
"SURESTAGE_TOKEN": "your_jwt_token"
}
}
}
}
Environment Variables
| Variable | Description | Required |
|---|---|---|
SURESTAGE_API_URL | API base URL | Yes |
SURESTAGE_TOKEN | JWT authentication token | Yes |
SURESTAGE_TENANT_SLUG | Default tenant slug | No |
MCP_LOG_LEVEL | Log verbosity (debug, info, warn, error) | No |
Error Handling
MCP Server returns structured errors:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid route configuration",
"details": {
"field": "path",
"error": "Path must start with /"
}
}
}
Error Codes:
VALIDATION_ERROR- Invalid inputNOT_FOUND- Resource not foundAUTH_ERROR- Authentication failedAPI_ERROR- SureStage API returned errorINTERNAL_ERROR- Unexpected error
Security Considerations
Token Storage
JWT tokens are stored in:
- Environment variables (recommended)
- Config file (less secure)
Tokens should:
- Be scoped to minimum required permissions
- Have expiration times
- Be rotated regularly
Tenant Isolation
All MCP operations are tenant-scoped. Users can only access resources within their current tenant context.
Input Validation
All tool inputs are validated before making API requests:
- Type checking
- Required field validation
- Enum validation
- Format validation (paths, URLs, etc.)
Related
- Mock Engine Service - Route and instance management
- AI Features - AI capabilities in SureStage
- CLI Authentication - CLI token management