Skip to main content
Glama

Watchtower DAP Windows Debugging

by rlaksana
netmvc-adapter.ts16.8 kB
import { EventEmitter } from 'events'; import { Logger } from '../server/logger'; import { DapStdioTransport } from '../transport/stdio-transport'; import { DapStartRequest, DapEvaluateRequest, DapThreadsRequest, DapStackTraceRequest, DapContinueRequest, DapPauseRequest, } from '../schemas/index'; import { createError } from '../server/error-taxonomy'; /** * .NET MVC Microservice Debug Adapter * * Specialized debugging adapter for .NET MVC microservices with support for: * - ASP.NET Core debugging * - Entity Framework Core query interception * - HTTP request/response debugging * - Distributed tracing * - Service communication debugging * - Performance monitoring */ export class NetMvcAdapter extends EventEmitter { private logger: Logger; private transport: DapStdioTransport; private sessionId: string | null = null; private capabilities: Record<string, any> = {}; private httpRequestTracker: Map<string, any> = new Map(); private databaseQueries: Map<string, any> = new Map(); private distributedTraces: Map<string, any> = new Map(); private microserviceInfo: { name: string; environment: string; version: string; services: string[]; connectionString: string; isMvc: boolean; controllerActions: string[]; } | null = null; constructor() { super(); this.logger = new Logger('NetMvcAdapter'); this.transport = new DapStdioTransport(); this.setupTransportHandlers(); } /** * Initialize the .NET MVC adapter */ async initialize(): Promise<Record<string, any>> { this.logger.info('Initializing .NET MVC microservice debug adapter'); try { // Detect .NET environment and microservice configuration await this.detectNetEnvironment(); // Set up ASP.NET Core specific capabilities this.capabilities = await this.configureAspNetCapabilities(); // Initialize database debugging await this.initializeDatabaseDebugging(); // Set up HTTP request tracking this.initializeHttpTracking(); // Configure distributed tracing await this.configureDistributedTracing(); this.logger.info('NET MVC adapter initialized successfully'); return this.capabilities; } catch (error) { this.logger.error('Failed to initialize .NET MVC debug adapter:', error); throw createError('ADAPTER_004', { error: (error as Error).message }); } } /** * Detect .NET environment and microservice configuration */ private async detectNetEnvironment(): Promise<void> { this.logger.debug('Detecting .NET environment...'); // Mock .NET environment detection const netVersion = '8.0.0'; const isAspNetCore = true; const isMvc = true; this.microserviceInfo = { name: 'MyMicroservice', environment: 'Development', version: '1.0.0', services: ['UserService', 'ProductService', 'OrderService'], connectionString: 'Server=(localdb)\\mssqllocaldb;Database=MyDb;Trusted_Connection=True;', isMvc: true, controllerActions: [ 'UserController: GetUserById', 'UserController: CreateUser', 'ProductController: GetProducts', 'OrderController: CreateOrder', ], }; this.logger.info( `NET Environment: ${netVersion}, ASP.NET Core: ${isAspNetCore}, MVC: ${isMvc}` ); } /** * Configure ASP.NET Core specific capabilities */ private async configureAspNetCapabilities(): Promise<Record<string, any>> { return { supportsConfigurationDoneRequest: true, supportsEvaluateForHovers: true, supportsSetVariable: true, supportsRestartRequest: true, supportsConditionalBreakpoints: true, supportsHitConditionalBreakpoints: true, supportsLogPoints: true, supportsExceptionOptions: true, supportsStepBack: false, supportsDataBreakpoints: true, supportsCompletionsQuery: true, supportsModulesQuery: true, supportsGotoTargetsRequest: true, supportsLoadedSourcesRequest: true, supportsTerminateDebuggee: true, supportsTerminateRequest: true, supportsDelayedStackTraceLoading: true, // ASP.NET Core specific capabilities supportsAspNetCoreDebugging: true, supportsEntityFrameworkCore: true, supportsHttpRequestDebugging: true, supportsDistributedTracing: true, supportsMicroserviceDebugging: true, supportsDatabaseQueryInterception: true, // Performance debugging supportsMemoryProfiling: true, supportsPerformanceAnalysis: true, // Web-specific debugging supportsHttpContextDebugging: true, supportsRoutingDebugging: true, supportsViewEngineDebugging: false, // Service communication supportsHttpClientDebugging: true, supportsGrpcDebugging: true, supportsSignalRDebugging: true, }; } /** * Initialize database debugging for Entity Framework Core */ private async initializeDatabaseQueryInterception(): Promise<void> { this.logger.debug('Initializing database query interception...'); // Mock EF Core query interception setup this.databaseQueries.clear(); this.logger.info('Database query interception initialized'); } /** * Initialize database debugging */ private async initializeDatabaseDebugging(): Promise<void> { await this.initializeDatabaseQueryInterception(); } /** * Initialize HTTP request tracking */ private initializeHttpTracking(): void { this.httpRequestTracker.clear(); this.logger.debug('HTTP request tracking initialized'); } /** * Configure distributed tracing */ private async configureDistributedTracing(): Promise<void> { this.logger.debug('Configuring distributed tracing...'); this.distributedTraces.clear(); // Mock distributed tracing configuration this.logger.info('Distributed tracing configured with correlation ID tracking'); } /** * Handle .NET MVC launch request */ async launch(config: DapStartRequest): Promise<{ sessionId: string }> { this.logger.info('Handling .NET MVC microservice launch request', { program: config.arguments.program, workingDirectory: config.arguments.cwd, }); try { this.sessionId = `netmvc_${Date.now()}`; // Set up .NET specific debugging await this.setupNetDebugging(config.arguments); // Launch ASP.NET Core application await this.launchAspNetCoreApplication(config.arguments); this.logger.info('NET MVC microservice launched successfully', { sessionId: this.sessionId }); return { sessionId: this.sessionId }; } catch (error) { this.logger.error('Failed to launch .NET MVC microservice:', error); throw createError('ADAPTER_005', { error: (error as Error).message }); } } /** * Set up .NET specific debugging environment */ private async setupNetDebugging(args: any): Promise<void> { this.logger.debug('Setting up .NET debugging environment...'); // Configure launchSettings.json if provided if (args.launchSettings) { this.logger.debug('Using custom launch settings'); } // Set up environment variables (not used, but required for future implementation) // const environmentVariables = { // ...process.env, // ASPNETCORE_ENVIRONMENT: args.environment || 'Development', // ASPNETCORE_URLS: args.urls || 'https://localhost:5001;http://localhost:5000', // }; this.logger.debug('.NET debugging environment configured'); } /** * Launch ASP.NET Core application */ private async launchAspNetCoreApplication(_args: any): Promise<void> { this.logger.debug('Launching ASP.NET Core application...'); // Mock ASP.NET Core application launch await new Promise(resolve => setTimeout(resolve, 1000)); this.logger.info('ASP.NET Core application launched successfully'); } /** * Handle HTTP request debugging */ async debugHttpRequest(request: any): Promise<any> { this.logger.debug('Debugging HTTP request', { method: request.method, path: request.path, queryString: request.queryString, }); const requestId = `req_${Date.now()}`; const httpRequest = { id: requestId, timestamp: Date.now(), method: request.method, path: request.path, queryString: request.queryString, headers: request.headers, body: request.body, cookies: request.cookies, session: request.session, user: request.user, routeData: request.routeData, controller: request.controller, action: request.action, modelState: request.modelState, form: request.form, files: request.files, culture: request.culture, acceptedLanguages: request.acceptedLanguages, isAjax: request.isAjax, isLocal: request.isLocal, protocol: request.protocol, scheme: request.scheme, host: request.host, originalUrl: request.originalUrl, }; this.httpRequestTracker.set(requestId, httpRequest); // Emit debug event this.emit('debugEvent', { event_id: `http_req_${requestId}`, session_id: this.sessionId, timestamp: Date.now(), event_type: 'http_request', data: httpRequest, }); return { success: true, requestId, message: 'HTTP request being debugged', }; } /** * Handle database query debugging */ async debugDatabaseQuery(query: any): Promise<any> { this.logger.debug('Debugging database query', { queryText: query.queryText, parameters: query.parameters, }); const queryId = `db_query_${Date.now()}`; const databaseQuery = { id: queryId, timestamp: Date.now(), queryText: query.queryText, parameters: query.parameters, parameterTypes: query.parameterTypes, executionTime: query.executionTime, connection: query.connection, isAsync: query.isAsync, entityType: query.entityType, dbContext: query.dbContext, transaction: query.transaction, commandType: query.commandType, tableMappings: query.tableMappings, changeTracking: query.changeTracking, }; this.databaseQueries.set(queryId, databaseQuery); // Emit debug event this.emit('debugEvent', { event_id: `db_query_${queryId}`, session_id: this.sessionId, timestamp: Date.now(), event_type: 'database_query', data: databaseQuery, }); return { success: true, queryId, message: 'Database query being debugged', }; } /** * Handle distributed tracing */ async trackDistributedTrace(trace: any): Promise<any> { this.logger.debug('Tracking distributed trace', { traceId: trace.traceId, spanId: trace.spanId, parentSpanId: trace.parentSpanId, }); const traceId = `trace_${trace.traceId}`; const distributedTrace = { id: traceId, traceId: trace.traceId, spanId: trace.spanId, parentSpanId: trace.parentSpanId, timestamp: Date.now(), service: trace.service, operation: trace.operation, startTime: trace.startTime, endTime: trace.endTime, duration: trace.duration, tags: trace.tags, logs: trace.logs, references: trace.references, baggage: trace.baggage, }; this.distributedTraces.set(traceId, distributedTrace); // Emit debug event this.emit('debugEvent', { event_id: `trace_${traceId}`, session_id: this.sessionId, timestamp: Date.now(), event_type: 'distributed_trace', data: distributedTrace, }); return { success: true, traceId, message: 'Distributed trace being tracked', }; } /** * Handle ASP.NET Core specific evaluation */ async evaluateAspNetExpression(args: DapEvaluateRequest['arguments']): Promise<any> { this.logger.debug('Evaluating ASP.NET Core expression', { expression: args.expression }); try { const expression = args.expression; let result = ''; let type = ''; let variablesReference = 0; // ASP.NET Core specific expression evaluation if (expression === 'HttpContext.Request') { result = '[HttpRequest: Method=GET, Path=/, QueryString=]'; type = 'HttpRequest'; variablesReference = 1; } else if (expression === 'HttpContext.Response') { result = '[HttpResponse: StatusCode=200, ContentType=text/html]'; type = 'HttpResponse'; variablesReference = 2; } else if (expression === 'ModelState.IsValid') { result = 'true'; type = 'bool'; } else if (expression === 'ViewData.Model') { result = '[Object: UserViewModel]'; type = 'UserViewModel'; variablesReference = 3; } else if (expression === 'TempData["Message"]') { result = '"Operation completed successfully"'; type = 'string'; } else if (expression === 'Session["UserId"]') { result = '12345'; type = 'object'; } else if (expression === 'User.Identity.Name') { result = '"john.doe@example.com"'; type = 'string'; } else if (expression === 'Configuration["ConnectionStrings:DefaultConnection"]') { result = '"Server=(localdb)\\mssqllocaldb;Database=MyDb;"'; type = 'string'; } else if (expression.startsWith('DbContext.')) { result = `[Database Query: ${expression}]`; type = 'IQueryable'; variablesReference = 4; } else if (expression.includes('.Where(') || expression.includes('.Select(')) { result = `[LINQ Query: ${expression}]`; type = 'IEnumerable'; variablesReference = 5; } else { // Fallback to standard evaluation result = `[ASP.NET Object: ${expression}]`; type = 'object'; variablesReference = 1; } return { result, type, variablesReference, presentationHint: { kind: this.getPresentationKind(type) }, }; } catch (error) { this.logger.error('ASP.NET expression evaluation failed:', error); throw error; } } /** * Get presentation kind for ASP.NET objects */ private getPresentationKind(type: string): string { if (type.includes('Request') || type.includes('Response')) return 'object'; if (type === 'HttpRequest') return 'class'; if (type === 'HttpResponse') return 'class'; if (type.includes('ModelState')) return 'property'; if (type.includes('ViewData') || type.includes('ViewBag')) return 'property'; if (type.includes('TempData')) return 'property'; if (type.includes('Session')) return 'property'; if (type.includes('Configuration')) return 'property'; if (type.includes('DbContext')) return 'class'; if (type.includes('IQueryable') || type.includes('IEnumerable')) return 'collection'; return 'property'; } /** * Get microservice debugging information */ getMicroserviceInfo(): any { return this.microserviceInfo; } /** * Get HTTP request history */ getHttpRequestHistory(): any[] { return Array.from(this.httpRequestTracker.values()); } /** * Get database query history */ getDatabaseQueryHistory(): any[] { return Array.from(this.databaseQueries.values()); } /** * Get distributed trace history */ getDistributedTraceHistory(): any[] { return Array.from(this.distributedTraces.values()); } /** * Setup transport handlers */ private setupTransportHandlers(): void { this.transport.on('data', data => { this.logger.debug('Received data from transport:', data); this.emit('transportData', data); }); this.transport.on('error', error => { this.logger.error('Transport error:', error); this.emit('error', error); }); } /** * Standard DAP method implementations */ async threads(_request: DapThreadsRequest): Promise<any> { return await this.transport.sendRequest('threads', {}); } async stackTrace(request: DapStackTraceRequest): Promise<any> { return await this.transport.sendRequest('stackTrace', { threadId: request.arguments.threadId, startFrame: request.arguments.startFrame, levels: request.arguments.levels, }); } async continue(request: DapContinueRequest): Promise<any> { return await this.transport.sendRequest('continue', { threadId: request.arguments.threadId, }); } async pause(request: DapPauseRequest): Promise<any> { return await this.transport.sendRequest('pause', { threadId: request.arguments.threadId, }); } } // Singleton instance export const netMvcAdapter = new NetMvcAdapter();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rlaksana/mcp-watchtower'

If you have feedback or need assistance with the MCP directory API, please join our Discord server