Files
mcp-quantum/src/http-server.ts
2025-10-08 05:13:41 +02:00

995 líneas
28 KiB
TypeScript

/**
* HTTP Server for CUDA Quantum MCP
* Provides REST API endpoints and Server-Sent Events for quantum computing operations
*/
import express, { Request, Response } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUiExpress from 'swagger-ui-express';
import { WebSocketServer, WebSocket } from 'ws';
import { createServer } from 'http';
import { initializePythonBridge, getPythonBridge } from './bridge/python-bridge.js';
import { Logger, LogLevel } from './utils/logger.js';
/**
* HTTP Server configuration
*/
interface HttpServerConfig {
port: number;
host: string;
corsOrigins: string[];
logLevel: LogLevel;
pythonPath?: string;
}
/**
* SSE Client interface
*/
interface SSEClient {
id: string;
response: Response;
subscriptions: Set<string>;
}
/**
* WebSocket Client interface
*/
interface WSClient {
id: string;
ws: WebSocket;
subscriptions: Set<string>;
}
/**
* Swagger API Documentation Configuration
*/
const swaggerOptions = {
definition: {
openapi: '3.0.0',
info: {
title: 'CUDA Quantum MCP API',
version: '1.0.0',
description: 'RESTful API for CUDA Quantum operations with GPU acceleration',
contact: {
name: 'MCP Quantum Team',
email: 'quantum@mcp.dev'
},
license: {
name: 'MIT',
url: 'https://opensource.org/licenses/MIT'
}
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
],
paths: {
'/health': {
get: {
summary: 'Health check endpoint',
tags: ['System'],
responses: {
'200': {
description: 'Server is healthy',
content: {
'application/json': {
schema: { $ref: '#/components/schemas/ApiResponse' }
}
}
}
}
}
},
'/api/kernels': {
post: {
summary: 'Create a new quantum kernel',
tags: ['Quantum Kernels'],
requestBody: {
required: true,
content: {
'application/json': {
schema: { $ref: '#/components/schemas/QuantumKernel' }
}
}
},
responses: {
'200': { description: 'Kernel created successfully' },
'400': { description: 'Invalid request parameters' }
}
},
get: {
summary: 'List all quantum kernels',
tags: ['Quantum Kernels'],
responses: {
'200': { description: 'List of kernels retrieved successfully' }
}
}
},
'/api/gates': {
post: {
summary: 'Apply a quantum gate to a kernel',
tags: ['Quantum Gates'],
requestBody: {
required: true,
content: {
'application/json': {
schema: { $ref: '#/components/schemas/QuantumGate' }
}
}
},
responses: {
'200': { description: 'Gate applied successfully' }
}
}
},
'/api/sample': {
post: {
summary: 'Sample quantum circuit measurements',
tags: ['Quantum Execution'],
requestBody: {
required: true,
content: {
'application/json': {
schema: { $ref: '#/components/schemas/SampleRequest' }
}
}
},
responses: {
'200': { description: 'Sampling completed successfully' }
}
}
},
'/api/observe': {
post: {
summary: 'Calculate Hamiltonian expectation value',
tags: ['Quantum Execution'],
requestBody: {
required: true,
content: {
'application/json': {
schema: { $ref: '#/components/schemas/ObserveRequest' }
}
}
},
responses: {
'200': { description: 'Expectation value calculated successfully' }
}
}
},
'/api/state/{kernelName}': {
get: {
summary: 'Get quantum state vector',
tags: ['Quantum Execution'],
parameters: [
{
in: 'path',
name: 'kernelName',
required: true,
schema: { type: 'string' }
}
],
responses: {
'200': { description: 'State vector retrieved successfully' }
}
}
},
'/api/targets': {
get: {
summary: 'List available quantum targets',
tags: ['Quantum Backends'],
responses: {
'200': { description: 'Available targets retrieved successfully' }
}
},
post: {
summary: 'Set quantum computing target',
tags: ['Quantum Backends'],
requestBody: {
required: true,
content: {
'application/json': {
schema: { $ref: '#/components/schemas/TargetRequest' }
}
}
},
responses: {
'200': { description: 'Target set successfully' }
}
}
},
'/api/platform': {
get: {
summary: 'Get platform information',
tags: ['System'],
responses: {
'200': { description: 'Platform info retrieved successfully' }
}
}
},
'/api/events': {
get: {
summary: 'Server-Sent Events stream',
tags: ['Real-time'],
parameters: [
{
in: 'query',
name: 'topics',
schema: { type: 'string' },
description: 'Comma-separated list of topics to subscribe to'
}
],
responses: {
'200': {
description: 'SSE stream established',
content: {
'text/event-stream': {
schema: { type: 'string' }
}
}
}
}
}
}
},
components: {
schemas: {
QuantumKernel: {
type: 'object',
required: ['name', 'num_qubits'],
properties: {
name: {
type: 'string',
description: 'Unique kernel identifier'
},
num_qubits: {
type: 'integer',
minimum: 1,
maximum: 64,
description: 'Number of qubits in the circuit'
},
parameters: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
type: { type: 'string', enum: ['float', 'int', 'complex', 'angle'] },
description: { type: 'string' }
}
}
}
}
},
QuantumGate: {
type: 'object',
required: ['kernel_name', 'gate_name', 'target_qubits'],
properties: {
kernel_name: { type: 'string' },
gate_name: { type: 'string', enum: ['h', 'x', 'y', 'z', 'cnot', 'rx', 'ry', 'rz', 's', 't'] },
target_qubits: {
type: 'array',
items: { type: 'integer', minimum: 0 }
},
control_qubits: {
type: 'array',
items: { type: 'integer', minimum: 0 }
},
parameters: {
type: 'array',
items: { type: 'number' }
},
adjoint: { type: 'boolean', default: false }
}
},
SampleRequest: {
type: 'object',
required: ['kernel_name'],
properties: {
kernel_name: { type: 'string' },
shots: { type: 'integer', minimum: 1, default: 1000 },
parameters: { type: 'object' }
}
},
ObserveRequest: {
type: 'object',
required: ['kernel_name', 'hamiltonian_terms'],
properties: {
kernel_name: { type: 'string' },
hamiltonian_terms: {
type: 'array',
items: {
type: 'object',
properties: {
coefficient: { type: 'number' },
pauli_string: { type: 'string' }
}
}
},
shots: { type: 'integer', minimum: 1, default: 1000 },
parameters: { type: 'object' }
}
},
TargetRequest: {
type: 'object',
required: ['target'],
properties: {
target: { type: 'string' },
configuration: { type: 'object' }
}
},
ApiResponse: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {},
error: { type: 'string' },
timestamp: { type: 'string', format: 'date-time' }
}
}
}
}
},
apis: [] // We define paths directly in the definition above
};
export class CudaQuantumHttpServer {
private app: express.Application;
private server!: any;
private wsServer!: WebSocketServer;
private logger: Logger;
private sseClients: Map<string, SSEClient>;
private wsClients: Map<string, WSClient>;
private config: HttpServerConfig;
constructor(config: HttpServerConfig) {
this.config = config;
this.app = express();
this.logger = new Logger('HttpServer', { level: config.logLevel });
this.sseClients = new Map();
this.wsClients = new Map();
this.setupMiddleware();
this.setupRoutes();
this.setupSwagger();
}
/**
* Setup Express middleware
*/
private setupMiddleware(): void {
// Security middleware
this.app.use(helmet({
contentSecurityPolicy: false // Allow Swagger UI
}));
// CORS configuration
this.app.use(cors({
origin: this.config.corsOrigins,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
// Body parsing
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true }));
// Request logging
this.app.use((req, res, next) => {
this.logger.debug(`${req.method} ${req.path}`, {
ip: req.ip,
userAgent: req.get('User-Agent')
});
next();
});
}
/**
* Setup API routes with Swagger documentation
*/
private setupRoutes(): void {
// Health check
this.app.get('/health', (req, res) => {
res.json({
success: true,
data: {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
},
timestamp: new Date().toISOString()
});
});
// Quantum Kernels
this.app.post('/api/kernels', this.handleCreateKernel.bind(this));
this.app.get('/api/kernels', this.handleListKernels.bind(this));
// Quantum Gates
this.app.post('/api/gates', this.handleApplyGate.bind(this));
// Quantum Execution
this.app.post('/api/sample', this.handleSample.bind(this));
this.app.post('/api/observe', this.handleObserve.bind(this));
this.app.get('/api/state/:kernelName', this.handleGetState.bind(this));
// Quantum Backends
this.app.get('/api/targets', this.handleGetTargets.bind(this));
this.app.post('/api/targets', this.handleSetTarget.bind(this));
this.app.get('/api/platform', this.handleGetPlatform.bind(this));
// Server-Sent Events
this.app.get('/api/events', this.handleSSE.bind(this));
}
/**
* Setup Swagger documentation
*/
private setupSwagger(): void {
const specs = swaggerJsdoc(swaggerOptions);
this.app.use('/api-docs', swaggerUiExpress.serve, swaggerUiExpress.setup(specs, {
explorer: true,
customSiteTitle: 'CUDA Quantum MCP API Documentation',
customfavIcon: '/favicon.ico'
}));
// Serve raw OpenAPI spec
this.app.get('/api-docs.json', (req, res) => {
res.json(specs);
});
}
/**
* Handle kernel creation
*/
private async handleCreateKernel(req: Request, res: Response): Promise<void> {
try {
const { name, num_qubits, parameters } = req.body;
if (!name || !num_qubits) {
res.status(400).json({
success: false,
error: 'Missing required parameters: name, num_qubits',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.createKernel(name, num_qubits, parameters);
// Broadcast to SSE clients
this.broadcastSSE('kernel_created', { name, num_qubits, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Error creating kernel:', { error: errorMessage, stack: error instanceof Error ? error.stack : undefined });
res.status(500).json({
success: false,
error: errorMessage,
timestamp: new Date().toISOString()
});
}
}
/**
* Handle kernel listing
*/
private async handleListKernels(req: Request, res: Response): Promise<void> {
try {
const bridge = getPythonBridge();
const result = await bridge.listKernels();
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Error listing kernels:', { error: errorMessage, stack: error instanceof Error ? error.stack : undefined });
res.status(500).json({
success: false,
error: errorMessage,
timestamp: new Date().toISOString()
});
}
}
/**
* Handle gate application
*/
private async handleApplyGate(req: Request, res: Response): Promise<void> {
try {
const { kernel_name, gate_name, target_qubits, control_qubits, parameters, adjoint } = req.body;
if (!kernel_name || !gate_name || !target_qubits) {
res.status(400).json({
success: false,
error: 'Missing required parameters: kernel_name, gate_name, target_qubits',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.applyGate(
kernel_name, gate_name, target_qubits, control_qubits, parameters, adjoint
);
// Broadcast to SSE clients
this.broadcastSSE('gate_applied', { kernel_name, gate_name, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error applying gate:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle quantum sampling
*/
private async handleSample(req: Request, res: Response): Promise<void> {
try {
const { kernel_name, shots = 1000, parameters } = req.body;
if (!kernel_name) {
res.status(400).json({
success: false,
error: 'Missing required parameter: kernel_name',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.sample(kernel_name, shots, parameters);
// Broadcast to SSE clients
this.broadcastSSE('sampling_completed', { kernel_name, shots, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error sampling:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle Hamiltonian observation
*/
private async handleObserve(req: Request, res: Response): Promise<void> {
try {
const { kernel_name, hamiltonian_terms, shots = 1000, parameters } = req.body;
if (!kernel_name || !hamiltonian_terms) {
res.status(400).json({
success: false,
error: 'Missing required parameters: kernel_name, hamiltonian_terms',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.observe(kernel_name, hamiltonian_terms, shots, parameters);
// Broadcast to SSE clients
this.broadcastSSE('observation_completed', { kernel_name, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error observing:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle state vector retrieval
*/
private async handleGetState(req: Request, res: Response): Promise<void> {
try {
const { kernelName } = req.params;
const { parameters } = req.query;
if (!kernelName) {
res.status(400).json({
success: false,
error: 'Missing required parameter: kernelName',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.getState(
kernelName,
parameters ? JSON.parse(parameters as string) : undefined
);
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error getting state:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle target listing
*/
private async handleGetTargets(req: Request, res: Response): Promise<void> {
try {
const bridge = getPythonBridge();
const result = await bridge.getAvailableTargets();
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Error getting targets:', { error: errorMessage, stack: error instanceof Error ? error.stack : undefined });
res.status(500).json({
success: false,
error: errorMessage,
timestamp: new Date().toISOString()
});
}
}
/**
* Handle target setting
*/
private async handleSetTarget(req: Request, res: Response): Promise<void> {
try {
const { target, configuration } = req.body;
if (!target) {
res.status(400).json({
success: false,
error: 'Missing required parameter: target',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.setTarget(target, configuration);
// Broadcast to SSE clients
this.broadcastSSE('target_changed', { target, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error setting target:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle platform info
*/
private async handleGetPlatform(req: Request, res: Response): Promise<void> {
try {
const bridge = getPythonBridge();
const result = await bridge.getPlatformInfo();
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Error getting platform info:', { error: errorMessage, stack: error instanceof Error ? error.stack : undefined });
res.status(500).json({
success: false,
error: errorMessage,
timestamp: new Date().toISOString()
});
}
}
/**
* Handle Server-Sent Events
*/
private handleSSE(req: Request, res: Response): void {
const clientId = Math.random().toString(36).substring(2, 15);
const topics = req.query.topics ? (req.query.topics as string).split(',') : ['all'];
// Setup SSE headers
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
});
// Store client
const client: SSEClient = {
id: clientId,
response: res,
subscriptions: new Set(topics)
};
this.sseClients.set(clientId, client);
// Send initial connection event
res.write(`data: ${JSON.stringify({
type: 'connection',
clientId: clientId,
timestamp: new Date().toISOString()
})}\\n\\n`);
// Handle client disconnect
req.on('close', () => {
this.sseClients.delete(clientId);
this.logger.debug(`SSE client ${clientId} disconnected`);
});
this.logger.debug(`SSE client ${clientId} connected with topics: ${topics.join(', ')}`);
}
/**
* Broadcast event to SSE clients
*/
private broadcastSSE(eventType: string, data: any): void {
const message = {
type: eventType,
data: data,
timestamp: new Date().toISOString()
};
for (const [clientId, client] of this.sseClients) {
if (client.subscriptions.has('all') || client.subscriptions.has(eventType)) {
try {
client.response.write(`data: ${JSON.stringify(message)}\\n\\n`);
} catch (error) {
this.logger.warn(`Failed to send SSE to client ${clientId}:`, error);
this.sseClients.delete(clientId);
}
}
}
}
/**
* Setup WebSocket server
*/
private setupWebSocket(): void {
this.wsServer.on('connection', (ws: WebSocket, req: any) => {
const clientId = Math.random().toString(36).substring(2, 15);
const client: WSClient = {
id: clientId,
ws: ws,
subscriptions: new Set(['all'])
};
this.wsClients.set(clientId, client);
this.logger.debug(`WebSocket client ${clientId} connected`);
// Send welcome message
ws.send(JSON.stringify({
type: 'connection',
clientId: clientId,
timestamp: new Date().toISOString()
}));
// Handle messages
ws.on('message', (message: Buffer) => {
try {
const data = JSON.parse(message.toString());
this.handleWebSocketMessage(clientId, data);
} catch (error) {
this.logger.warn(`Invalid WebSocket message from ${clientId}:`, error);
}
});
// Handle disconnect
ws.on('close', () => {
this.wsClients.delete(clientId);
this.logger.debug(`WebSocket client ${clientId} disconnected`);
});
ws.on('error', (error: any) => {
this.logger.error(`WebSocket error for client ${clientId}:`, error);
this.wsClients.delete(clientId);
});
});
}
/**
* Handle WebSocket messages
*/
private handleWebSocketMessage(clientId: string, message: any): void {
const client = this.wsClients.get(clientId);
if (!client) return;
switch (message.type) {
case 'subscribe':
if (message.topics && Array.isArray(message.topics)) {
message.topics.forEach((topic: string) => {
client.subscriptions.add(topic);
});
}
break;
case 'unsubscribe':
if (message.topics && Array.isArray(message.topics)) {
message.topics.forEach((topic: string) => {
client.subscriptions.delete(topic);
});
}
break;
}
}
/**
* Start the HTTP server
*/
async start(): Promise<void> {
try {
// Initialize Python bridge
this.logger.info('Initializing Python bridge...');
await initializePythonBridge({ pythonPath: this.config.pythonPath });
this.logger.info('Python bridge initialized successfully');
// Create HTTP server
this.server = createServer(this.app);
// Setup WebSocket server
this.wsServer = new WebSocketServer({ server: this.server });
this.setupWebSocket();
// Start listening
await new Promise<void>((resolve, reject) => {
this.server.listen(this.config.port, this.config.host, () => {
resolve();
}).on('error', reject);
});
this.logger.info(`CUDA Quantum HTTP Server started on ${this.config.host}:${this.config.port}`);
this.logger.info(`API Documentation: http://${this.config.host}:${this.config.port}/api-docs`);
this.logger.info(`Health Check: http://${this.config.host}:${this.config.port}/health`);
this.logger.info(`SSE Endpoint: http://${this.config.host}:${this.config.port}/api/events`);
} catch (error) {
this.logger.error('Failed to start HTTP server:', error);
throw error;
}
}
/**
* Stop the HTTP server
*/
async stop(): Promise<void> {
this.logger.info('Shutting down HTTP server...');
// Close WebSocket connections
for (const [clientId, client] of this.wsClients) {
client.ws.close();
}
this.wsClients.clear();
// Close SSE connections
for (const [clientId, client] of this.sseClients) {
client.response.end();
}
this.sseClients.clear();
// Close WebSocket server
if (this.wsServer) {
this.wsServer.close();
}
// Close HTTP server
if (this.server) {
await new Promise<void>((resolve) => {
this.server.close(() => resolve());
});
}
// Close Python bridge
const bridge = getPythonBridge();
if (bridge) {
await bridge.close();
}
this.logger.info('HTTP server shutdown complete');
}
}
/**
* Main entry point for HTTP server
*/
async function main(): Promise<void> {
const config: HttpServerConfig = {
port: parseInt(process.env.HTTP_PORT || '3000'),
host: process.env.HTTP_HOST || 'localhost',
corsOrigins: process.env.CORS_ORIGINS ? process.env.CORS_ORIGINS.split(',') : ['*'],
pythonPath: process.env.CUDAQ_PYTHON_PATH,
logLevel: process.env.LOG_LEVEL === 'debug' ? LogLevel.DEBUG :
process.env.LOG_LEVEL === 'warn' ? LogLevel.WARN :
process.env.LOG_LEVEL === 'error' ? LogLevel.ERROR :
LogLevel.INFO
};
const server = new CudaQuantumHttpServer(config);
// Handle graceful shutdown
process.on('SIGTERM', async () => {
await server.stop();
process.exit(0);
});
process.on('SIGINT', async () => {
await server.stop();
process.exit(0);
});
try {
await server.start();
} catch (error) {
console.error('Failed to start CUDA Quantum HTTP Server:', error);
process.exit(1);
}
}
// Start the server if this file is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
}