Skip to main content

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:

  • userusers
  • companycompanies
  • personpeople

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:

  • emailalice@example.com
  • phone_number+1-555-123-4567
  • createdAt2026-03-21T10:30:00.000Z
  • statusactive
  • user_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:

  1. Get current tenant:

    Tool: get_current_tenant
    Result: {"tenantId": "tenant_abc123", "slug": "acme-corp"}
  2. List instances:

    Tool: list_instances
    Input: {"status": "active"}
    Result: {"instances": [{"id": "inst_abc123", "name": "User API"}]}
  3. 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

VariableDescriptionRequired
SURESTAGE_API_URLAPI base URLYes
SURESTAGE_TOKENJWT authentication tokenYes
SURESTAGE_TENANT_SLUGDefault tenant slugNo
MCP_LOG_LEVELLog 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 input
  • NOT_FOUND - Resource not found
  • AUTH_ERROR - Authentication failed
  • API_ERROR - SureStage API returned error
  • INTERNAL_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.)