@dxheroes/local-mcp-backend 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
 
2
- > @dxheroes/local-mcp-backend@0.3.1 build /home/runner/work/local-mcp-gateway/local-mcp-gateway/apps/backend
2
+ > @dxheroes/local-mcp-backend@0.3.2 build /home/runner/work/local-mcp-gateway/local-mcp-gateway/apps/backend
3
3
  > nest build
4
4
 
5
5
  -  TSC  Initializing type checker...
6
6
  ✔  TSC  Initializing type checker...
7
7
  >  TSC  Found 0 issues.
8
8
  >  SWC  Running...
9
- Successfully compiled: 32 files with swc (73.37ms)
9
+ Successfully compiled: 32 files with swc (73.65ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.2](https://github.com/DXHeroes/local-mcp-gateway/compare/backend-v0.3.1...backend-v0.3.2) (2026-01-13)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * simplify tool inclusion logic in ProxyService ([8bb3be9](https://github.com/DXHeroes/local-mcp-gateway/commit/8bb3be93ef5504790edef84711cc3cff8c4dc6d9))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @dxheroes/local-mcp-core bumped to 0.3.2
16
+ * @dxheroes/local-mcp-database bumped to 0.3.2
17
+ * @dxheroes/mcp-gemini-deep-research bumped to 0.3.2
18
+ * devDependencies
19
+ * @dxheroes/local-mcp-config bumped to 0.3.2
20
+
3
21
  ## [0.3.1](https://github.com/DXHeroes/local-mcp-gateway/compare/backend-v0.3.0...backend-v0.3.1) (2026-01-13)
4
22
 
5
23
 
@@ -41,11 +41,7 @@ export class ProxyService {
41
41
  },
42
42
  include: {
43
43
  mcpServer: true,
44
- tools: {
45
- where: {
46
- isEnabled: true
47
- }
48
- }
44
+ tools: true
49
45
  },
50
46
  orderBy: {
51
47
  order: 'asc'
@@ -197,6 +193,10 @@ export class ProxyService {
197
193
  const tools = await instance.listTools();
198
194
  // Check if this server has the requested tool (by name or custom name)
199
195
  const customization = profileServer.tools.find((t)=>t.customName === params.name || t.toolName === params.name);
196
+ // Skip if tool is disabled
197
+ if (customization && !customization.isEnabled) {
198
+ continue;
199
+ }
200
200
  const toolName = customization?.toolName || params.name;
201
201
  const hasTool = tools.some((t)=>t.name === toolName);
202
202
  if (hasTool) {
@@ -373,11 +373,7 @@ export class ProxyService {
373
373
  },
374
374
  include: {
375
375
  mcpServer: true,
376
- tools: {
377
- where: {
378
- isEnabled: true
379
- }
380
- }
376
+ tools: true
381
377
  },
382
378
  orderBy: {
383
379
  order: 'asc'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/modules/proxy/proxy.service.ts"],"sourcesContent":["/**\n * Proxy Service\n *\n * Handles MCP protocol proxying for profiles.\n */\n\nimport type { ApiKeyConfig as CoreApiKeyConfig, McpServer } from '@dxheroes/local-mcp-core';\nimport { RemoteHttpMcpServer, RemoteSseMcpServer } from '@dxheroes/local-mcp-core';\nimport { Injectable, Logger, NotFoundException } from '@nestjs/common';\nimport { PrismaService } from '../database/prisma.service.js';\nimport { DebugService } from '../debug/debug.service.js';\nimport { McpRegistry } from '../mcp/mcp-registry.js';\n\ninterface McpToolCall {\n name: string;\n arguments?: Record<string, unknown>;\n}\n\nexport interface McpRequest {\n jsonrpc: '2.0';\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface McpResponse {\n jsonrpc: '2.0';\n id: string | number;\n result?: unknown;\n error?: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\ninterface ServerConfig {\n builtinId?: string;\n url?: string;\n}\n\ninterface StoredApiKeyConfig {\n apiKey: string;\n headerName?: string;\n headerValueTemplate?: string;\n}\n\n@Injectable()\nexport class ProxyService {\n private readonly logger = new Logger(ProxyService.name);\n private readonly serverInstances = new Map<string, McpServer>();\n\n constructor(\n private readonly prisma: PrismaService,\n private readonly registry: McpRegistry,\n private readonly debugService: DebugService\n ) {}\n\n /**\n * Handle MCP JSON-RPC request for a profile\n */\n async handleRequest(profileName: string, request: McpRequest): Promise<McpResponse> {\n const requestId = request.id;\n const startTime = Date.now();\n\n // Get profile with servers\n const profile = await this.prisma.profile.findUnique({\n where: { name: profileName },\n include: {\n mcpServers: {\n where: { isActive: true },\n include: {\n mcpServer: true,\n tools: {\n where: { isEnabled: true },\n },\n },\n orderBy: { order: 'asc' },\n },\n },\n });\n\n if (!profile) {\n throw new NotFoundException(`Profile \"${profileName}\" not found`);\n }\n\n // Skip logging for initialize (just handshaking)\n const shouldLog = request.method !== 'initialize';\n\n // Create debug log entry\n let logId: string | null = null;\n if (shouldLog) {\n try {\n const log = await this.debugService.createLog({\n profileId: profile.id,\n requestType: request.method,\n requestPayload: JSON.stringify(request),\n status: 'pending',\n });\n logId = log.id;\n } catch (logError) {\n this.logger.warn(`Failed to create debug log: ${logError}`);\n }\n }\n\n try {\n // Handle different MCP methods\n let response: McpResponse;\n\n switch (request.method) {\n case 'initialize':\n response = await this.handleInitialize(requestId, profile);\n break;\n\n case 'tools/list':\n response = await this.handleToolsList(requestId, profile);\n break;\n\n case 'tools/call':\n response = await this.handleToolsCall(\n requestId,\n profile,\n request.params as McpToolCall,\n logId\n );\n break;\n\n case 'resources/list':\n response = await this.handleResourcesList(requestId, profile);\n break;\n\n case 'resources/read':\n response = await this.handleResourcesRead(\n requestId,\n profile,\n request.params as { uri: string }\n );\n break;\n\n default:\n response = {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32601,\n message: `Method not found: ${request.method}`,\n },\n };\n }\n\n // Update debug log with success\n if (logId) {\n const durationMs = Date.now() - startTime;\n try {\n await this.debugService.updateLog(logId, {\n responsePayload: JSON.stringify(response),\n status: response.error ? 'error' : 'success',\n errorMessage: response.error?.message,\n durationMs,\n });\n } catch (logError) {\n this.logger.warn(`Failed to update debug log: ${logError}`);\n }\n }\n\n return response;\n } catch (error) {\n this.logger.error(`MCP request error: ${error}`);\n\n const errorMessage = error instanceof Error ? error.message : 'Internal error';\n\n // Update debug log with error\n if (logId) {\n const durationMs = Date.now() - startTime;\n try {\n await this.debugService.updateLog(logId, {\n status: 'error',\n errorMessage,\n durationMs,\n });\n } catch (logError) {\n this.logger.warn(`Failed to update debug log: ${logError}`);\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32603,\n message: errorMessage,\n },\n };\n }\n }\n\n private async handleInitialize(\n requestId: string | number,\n _profile: { name: string }\n ): Promise<McpResponse> {\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: {\n protocolVersion: '2024-11-05',\n capabilities: {\n tools: {},\n resources: {},\n },\n serverInfo: {\n name: 'Local MCP Gateway',\n version: '0.1.0',\n },\n },\n };\n }\n\n private async handleToolsList(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n tools: Array<{\n toolName: string;\n isEnabled: boolean;\n customName: string | null;\n customDescription: string | null;\n }>;\n }>;\n }\n ): Promise<McpResponse> {\n const allTools: Array<{\n name: string;\n description: string;\n inputSchema: unknown;\n }> = [];\n\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const tools = await instance.listTools();\n\n // Apply tool customizations\n for (const tool of tools) {\n const customization = profileServer.tools.find((t) => t.toolName === tool.name);\n\n if (!customization || customization.isEnabled) {\n allTools.push({\n name: customization?.customName || tool.name,\n description: customization?.customDescription || tool.description,\n inputSchema: tool.inputSchema,\n });\n }\n }\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: {\n tools: allTools,\n },\n };\n }\n\n private async handleToolsCall(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n tools: Array<{\n toolName: string;\n isEnabled: boolean;\n customName: string | null;\n }>;\n }>;\n },\n params: McpToolCall,\n logId: string | null\n ): Promise<McpResponse> {\n // Find which server has this tool\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const tools = await instance.listTools();\n\n // Check if this server has the requested tool (by name or custom name)\n const customization = profileServer.tools.find(\n (t) => t.customName === params.name || t.toolName === params.name\n );\n\n const toolName = customization?.toolName || params.name;\n const hasTool = tools.some((t) => t.name === toolName);\n\n if (hasTool) {\n // Update log with the server that handled this tool call\n if (logId) {\n try {\n await this.debugService.updateLog(logId, {\n mcpServerId: server.id,\n });\n } catch (logError) {\n this.logger.warn(`Failed to update debug log with server info: ${logError}`);\n }\n }\n\n const result = await instance.callTool(toolName, params.arguments || {});\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n result,\n };\n }\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32602,\n message: `Tool not found: ${params.name}`,\n },\n };\n }\n\n private async handleResourcesList(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n }>;\n }\n ): Promise<McpResponse> {\n const allResources: Array<{\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n }> = [];\n\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const resources = await instance.listResources();\n allResources.push(...resources);\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: {\n resources: allResources,\n },\n };\n }\n\n private async handleResourcesRead(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n }>;\n },\n params: { uri: string }\n ): Promise<McpResponse> {\n // Try each server until we find one that has this resource\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n try {\n const content = await instance.readResource(params.uri);\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: content,\n };\n } catch {\n // Resource not found on this server, try next\n }\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32602,\n message: `Resource not found: ${params.uri}`,\n },\n };\n }\n\n /**\n * Get tools for a specific server by ID\n */\n async getToolsForServer(serverId: string) {\n const server = await this.prisma.mcpServer.findUnique({\n where: { id: serverId },\n });\n\n if (!server) {\n throw new NotFoundException(`MCP server ${serverId} not found`);\n }\n\n const instance = await this.getServerInstance(server);\n if (!instance) {\n return [];\n }\n\n return instance.listTools();\n }\n\n /**\n * Get or create a server instance\n */\n private async getServerInstance(server: {\n id: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n }): Promise<McpServer | null> {\n // Check cache\n const cached = this.serverInstances.get(server.id);\n if (cached) {\n return cached;\n }\n\n // Parse config\n const config = this.parseJson<ServerConfig>(server.config);\n const builtinId = config?.builtinId;\n\n // Get API key config and convert to CoreApiKeyConfig format\n const storedConfig = this.parseJson<StoredApiKeyConfig>(server.apiKeyConfig);\n const apiKeyConfig = this.convertApiKeyConfig(storedConfig);\n\n // For builtin servers, get from registry\n if (builtinId && this.registry.has(builtinId)) {\n const pkg = this.registry.get(builtinId);\n if (pkg) {\n const instance = pkg.createServer(apiKeyConfig);\n await instance.initialize();\n this.serverInstances.set(server.id, instance);\n return instance;\n }\n }\n\n // For remote_http servers, create RemoteHttpMcpServer\n if (server.type === 'remote_http' && config?.url) {\n const remoteServer = new RemoteHttpMcpServer(\n { url: config.url, transport: 'http' },\n null,\n apiKeyConfig\n );\n await remoteServer.initialize();\n this.serverInstances.set(server.id, remoteServer);\n return remoteServer;\n }\n\n // For remote_sse servers, create RemoteSseMcpServer\n if (server.type === 'remote_sse' && config?.url) {\n const remoteServer = new RemoteSseMcpServer(\n { url: config.url, transport: 'sse' },\n null,\n apiKeyConfig\n );\n await remoteServer.initialize();\n this.serverInstances.set(server.id, remoteServer);\n return remoteServer;\n }\n\n return null;\n }\n\n /**\n * Convert stored API key config to CoreApiKeyConfig format\n */\n private convertApiKeyConfig(stored: StoredApiKeyConfig | null): CoreApiKeyConfig | null {\n if (!stored?.apiKey) return null;\n\n const headerName = stored.headerName || 'Authorization';\n const template = stored.headerValueTemplate || 'Bearer {apiKey}';\n const headerValue = template.replace('{apiKey}', stored.apiKey);\n\n return {\n apiKey: stored.apiKey,\n headerName,\n headerValue,\n };\n }\n\n private parseJson<T>(value: unknown): T | null {\n if (typeof value === 'string') {\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n return value as T | null;\n }\n\n /**\n * Get profile info with aggregated tools and server status\n */\n async getProfileInfo(profileName: string) {\n const profile = await this.prisma.profile.findUnique({\n where: { name: profileName },\n include: {\n mcpServers: {\n where: { isActive: true },\n include: {\n mcpServer: true,\n tools: { where: { isEnabled: true } },\n },\n orderBy: { order: 'asc' },\n },\n },\n });\n\n if (!profile) {\n throw new NotFoundException(`Profile \"${profileName}\" not found`);\n }\n\n // Aggregate tools from all servers\n const tools: Array<{ name: string; description: string }> = [];\n const serverStatus: Record<string, { connected: boolean; toolCount: number }> = {};\n\n for (const ps of profile.mcpServers) {\n const server = ps.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const serverTools = await instance.listTools();\n serverStatus[server.id] = { connected: true, toolCount: serverTools.length };\n\n for (const tool of serverTools) {\n const customization = ps.tools.find((t) => t.toolName === tool.name);\n if (!customization || customization.isEnabled) {\n tools.push({\n name: customization?.customName || tool.name,\n description: customization?.customDescription || tool.description,\n });\n }\n }\n } else {\n serverStatus[server.id] = { connected: false, toolCount: 0 };\n }\n }\n\n return {\n tools,\n serverStatus: {\n total: profile.mcpServers.length,\n connected: Object.values(serverStatus).filter((s) => s.connected).length,\n servers: serverStatus,\n },\n };\n }\n}\n"],"names":["RemoteHttpMcpServer","RemoteSseMcpServer","Injectable","Logger","NotFoundException","PrismaService","DebugService","McpRegistry","ProxyService","prisma","registry","debugService","logger","name","serverInstances","Map","handleRequest","profileName","request","requestId","id","startTime","Date","now","profile","findUnique","where","include","mcpServers","isActive","mcpServer","tools","isEnabled","orderBy","order","shouldLog","method","logId","log","createLog","profileId","requestType","requestPayload","JSON","stringify","status","logError","warn","response","handleInitialize","handleToolsList","handleToolsCall","params","handleResourcesList","handleResourcesRead","jsonrpc","error","code","message","durationMs","updateLog","responsePayload","errorMessage","Error","_profile","result","protocolVersion","capabilities","resources","serverInfo","version","allTools","profileServer","server","instance","getServerInstance","listTools","tool","customization","find","t","toolName","push","customName","description","customDescription","inputSchema","hasTool","some","mcpServerId","callTool","arguments","allResources","listResources","content","readResource","uri","getToolsForServer","serverId","cached","get","config","parseJson","builtinId","storedConfig","apiKeyConfig","convertApiKeyConfig","has","pkg","createServer","initialize","set","type","url","remoteServer","transport","stored","apiKey","headerName","template","headerValueTemplate","headerValue","replace","value","parse","getProfileInfo","serverStatus","ps","serverTools","connected","toolCount","length","total","Object","values","filter","s","servers"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/modules/proxy/proxy.service.ts"],"sourcesContent":["/**\n * Proxy Service\n *\n * Handles MCP protocol proxying for profiles.\n */\n\nimport type { ApiKeyConfig as CoreApiKeyConfig, McpServer } from '@dxheroes/local-mcp-core';\nimport { RemoteHttpMcpServer, RemoteSseMcpServer } from '@dxheroes/local-mcp-core';\nimport { Injectable, Logger, NotFoundException } from '@nestjs/common';\nimport { PrismaService } from '../database/prisma.service.js';\nimport { DebugService } from '../debug/debug.service.js';\nimport { McpRegistry } from '../mcp/mcp-registry.js';\n\ninterface McpToolCall {\n name: string;\n arguments?: Record<string, unknown>;\n}\n\nexport interface McpRequest {\n jsonrpc: '2.0';\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface McpResponse {\n jsonrpc: '2.0';\n id: string | number;\n result?: unknown;\n error?: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\ninterface ServerConfig {\n builtinId?: string;\n url?: string;\n}\n\ninterface StoredApiKeyConfig {\n apiKey: string;\n headerName?: string;\n headerValueTemplate?: string;\n}\n\n@Injectable()\nexport class ProxyService {\n private readonly logger = new Logger(ProxyService.name);\n private readonly serverInstances = new Map<string, McpServer>();\n\n constructor(\n private readonly prisma: PrismaService,\n private readonly registry: McpRegistry,\n private readonly debugService: DebugService\n ) {}\n\n /**\n * Handle MCP JSON-RPC request for a profile\n */\n async handleRequest(profileName: string, request: McpRequest): Promise<McpResponse> {\n const requestId = request.id;\n const startTime = Date.now();\n\n // Get profile with servers\n const profile = await this.prisma.profile.findUnique({\n where: { name: profileName },\n include: {\n mcpServers: {\n where: { isActive: true },\n include: {\n mcpServer: true,\n tools: true,\n },\n orderBy: { order: 'asc' },\n },\n },\n });\n\n if (!profile) {\n throw new NotFoundException(`Profile \"${profileName}\" not found`);\n }\n\n // Skip logging for initialize (just handshaking)\n const shouldLog = request.method !== 'initialize';\n\n // Create debug log entry\n let logId: string | null = null;\n if (shouldLog) {\n try {\n const log = await this.debugService.createLog({\n profileId: profile.id,\n requestType: request.method,\n requestPayload: JSON.stringify(request),\n status: 'pending',\n });\n logId = log.id;\n } catch (logError) {\n this.logger.warn(`Failed to create debug log: ${logError}`);\n }\n }\n\n try {\n // Handle different MCP methods\n let response: McpResponse;\n\n switch (request.method) {\n case 'initialize':\n response = await this.handleInitialize(requestId, profile);\n break;\n\n case 'tools/list':\n response = await this.handleToolsList(requestId, profile);\n break;\n\n case 'tools/call':\n response = await this.handleToolsCall(\n requestId,\n profile,\n request.params as McpToolCall,\n logId\n );\n break;\n\n case 'resources/list':\n response = await this.handleResourcesList(requestId, profile);\n break;\n\n case 'resources/read':\n response = await this.handleResourcesRead(\n requestId,\n profile,\n request.params as { uri: string }\n );\n break;\n\n default:\n response = {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32601,\n message: `Method not found: ${request.method}`,\n },\n };\n }\n\n // Update debug log with success\n if (logId) {\n const durationMs = Date.now() - startTime;\n try {\n await this.debugService.updateLog(logId, {\n responsePayload: JSON.stringify(response),\n status: response.error ? 'error' : 'success',\n errorMessage: response.error?.message,\n durationMs,\n });\n } catch (logError) {\n this.logger.warn(`Failed to update debug log: ${logError}`);\n }\n }\n\n return response;\n } catch (error) {\n this.logger.error(`MCP request error: ${error}`);\n\n const errorMessage = error instanceof Error ? error.message : 'Internal error';\n\n // Update debug log with error\n if (logId) {\n const durationMs = Date.now() - startTime;\n try {\n await this.debugService.updateLog(logId, {\n status: 'error',\n errorMessage,\n durationMs,\n });\n } catch (logError) {\n this.logger.warn(`Failed to update debug log: ${logError}`);\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32603,\n message: errorMessage,\n },\n };\n }\n }\n\n private async handleInitialize(\n requestId: string | number,\n _profile: { name: string }\n ): Promise<McpResponse> {\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: {\n protocolVersion: '2024-11-05',\n capabilities: {\n tools: {},\n resources: {},\n },\n serverInfo: {\n name: 'Local MCP Gateway',\n version: '0.1.0',\n },\n },\n };\n }\n\n private async handleToolsList(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n tools: Array<{\n toolName: string;\n isEnabled: boolean;\n customName: string | null;\n customDescription: string | null;\n }>;\n }>;\n }\n ): Promise<McpResponse> {\n const allTools: Array<{\n name: string;\n description: string;\n inputSchema: unknown;\n }> = [];\n\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const tools = await instance.listTools();\n\n // Apply tool customizations\n for (const tool of tools) {\n const customization = profileServer.tools.find((t) => t.toolName === tool.name);\n\n if (!customization || customization.isEnabled) {\n allTools.push({\n name: customization?.customName || tool.name,\n description: customization?.customDescription || tool.description,\n inputSchema: tool.inputSchema,\n });\n }\n }\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: {\n tools: allTools,\n },\n };\n }\n\n private async handleToolsCall(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n tools: Array<{\n toolName: string;\n isEnabled: boolean;\n customName: string | null;\n }>;\n }>;\n },\n params: McpToolCall,\n logId: string | null\n ): Promise<McpResponse> {\n // Find which server has this tool\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const tools = await instance.listTools();\n\n // Check if this server has the requested tool (by name or custom name)\n const customization = profileServer.tools.find(\n (t) => t.customName === params.name || t.toolName === params.name\n );\n\n // Skip if tool is disabled\n if (customization && !customization.isEnabled) {\n continue;\n }\n\n const toolName = customization?.toolName || params.name;\n const hasTool = tools.some((t) => t.name === toolName);\n\n if (hasTool) {\n // Update log with the server that handled this tool call\n if (logId) {\n try {\n await this.debugService.updateLog(logId, {\n mcpServerId: server.id,\n });\n } catch (logError) {\n this.logger.warn(`Failed to update debug log with server info: ${logError}`);\n }\n }\n\n const result = await instance.callTool(toolName, params.arguments || {});\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n result,\n };\n }\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32602,\n message: `Tool not found: ${params.name}`,\n },\n };\n }\n\n private async handleResourcesList(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n }>;\n }\n ): Promise<McpResponse> {\n const allResources: Array<{\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n }> = [];\n\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const resources = await instance.listResources();\n allResources.push(...resources);\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: {\n resources: allResources,\n },\n };\n }\n\n private async handleResourcesRead(\n requestId: string | number,\n profile: {\n mcpServers: Array<{\n mcpServer: {\n id: string;\n name: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n };\n }>;\n },\n params: { uri: string }\n ): Promise<McpResponse> {\n // Try each server until we find one that has this resource\n for (const profileServer of profile.mcpServers) {\n const server = profileServer.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n try {\n const content = await instance.readResource(params.uri);\n return {\n jsonrpc: '2.0',\n id: requestId,\n result: content,\n };\n } catch {\n // Resource not found on this server, try next\n }\n }\n }\n\n return {\n jsonrpc: '2.0',\n id: requestId,\n error: {\n code: -32602,\n message: `Resource not found: ${params.uri}`,\n },\n };\n }\n\n /**\n * Get tools for a specific server by ID\n */\n async getToolsForServer(serverId: string) {\n const server = await this.prisma.mcpServer.findUnique({\n where: { id: serverId },\n });\n\n if (!server) {\n throw new NotFoundException(`MCP server ${serverId} not found`);\n }\n\n const instance = await this.getServerInstance(server);\n if (!instance) {\n return [];\n }\n\n return instance.listTools();\n }\n\n /**\n * Get or create a server instance\n */\n private async getServerInstance(server: {\n id: string;\n type: string;\n config: unknown;\n apiKeyConfig: unknown;\n }): Promise<McpServer | null> {\n // Check cache\n const cached = this.serverInstances.get(server.id);\n if (cached) {\n return cached;\n }\n\n // Parse config\n const config = this.parseJson<ServerConfig>(server.config);\n const builtinId = config?.builtinId;\n\n // Get API key config and convert to CoreApiKeyConfig format\n const storedConfig = this.parseJson<StoredApiKeyConfig>(server.apiKeyConfig);\n const apiKeyConfig = this.convertApiKeyConfig(storedConfig);\n\n // For builtin servers, get from registry\n if (builtinId && this.registry.has(builtinId)) {\n const pkg = this.registry.get(builtinId);\n if (pkg) {\n const instance = pkg.createServer(apiKeyConfig);\n await instance.initialize();\n this.serverInstances.set(server.id, instance);\n return instance;\n }\n }\n\n // For remote_http servers, create RemoteHttpMcpServer\n if (server.type === 'remote_http' && config?.url) {\n const remoteServer = new RemoteHttpMcpServer(\n { url: config.url, transport: 'http' },\n null,\n apiKeyConfig\n );\n await remoteServer.initialize();\n this.serverInstances.set(server.id, remoteServer);\n return remoteServer;\n }\n\n // For remote_sse servers, create RemoteSseMcpServer\n if (server.type === 'remote_sse' && config?.url) {\n const remoteServer = new RemoteSseMcpServer(\n { url: config.url, transport: 'sse' },\n null,\n apiKeyConfig\n );\n await remoteServer.initialize();\n this.serverInstances.set(server.id, remoteServer);\n return remoteServer;\n }\n\n return null;\n }\n\n /**\n * Convert stored API key config to CoreApiKeyConfig format\n */\n private convertApiKeyConfig(stored: StoredApiKeyConfig | null): CoreApiKeyConfig | null {\n if (!stored?.apiKey) return null;\n\n const headerName = stored.headerName || 'Authorization';\n const template = stored.headerValueTemplate || 'Bearer {apiKey}';\n const headerValue = template.replace('{apiKey}', stored.apiKey);\n\n return {\n apiKey: stored.apiKey,\n headerName,\n headerValue,\n };\n }\n\n private parseJson<T>(value: unknown): T | null {\n if (typeof value === 'string') {\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n return value as T | null;\n }\n\n /**\n * Get profile info with aggregated tools and server status\n */\n async getProfileInfo(profileName: string) {\n const profile = await this.prisma.profile.findUnique({\n where: { name: profileName },\n include: {\n mcpServers: {\n where: { isActive: true },\n include: {\n mcpServer: true,\n tools: true,\n },\n orderBy: { order: 'asc' },\n },\n },\n });\n\n if (!profile) {\n throw new NotFoundException(`Profile \"${profileName}\" not found`);\n }\n\n // Aggregate tools from all servers\n const tools: Array<{ name: string; description: string }> = [];\n const serverStatus: Record<string, { connected: boolean; toolCount: number }> = {};\n\n for (const ps of profile.mcpServers) {\n const server = ps.mcpServer;\n const instance = await this.getServerInstance(server);\n\n if (instance) {\n const serverTools = await instance.listTools();\n serverStatus[server.id] = { connected: true, toolCount: serverTools.length };\n\n for (const tool of serverTools) {\n const customization = ps.tools.find((t) => t.toolName === tool.name);\n if (!customization || customization.isEnabled) {\n tools.push({\n name: customization?.customName || tool.name,\n description: customization?.customDescription || tool.description,\n });\n }\n }\n } else {\n serverStatus[server.id] = { connected: false, toolCount: 0 };\n }\n }\n\n return {\n tools,\n serverStatus: {\n total: profile.mcpServers.length,\n connected: Object.values(serverStatus).filter((s) => s.connected).length,\n servers: serverStatus,\n },\n };\n }\n}\n"],"names":["RemoteHttpMcpServer","RemoteSseMcpServer","Injectable","Logger","NotFoundException","PrismaService","DebugService","McpRegistry","ProxyService","prisma","registry","debugService","logger","name","serverInstances","Map","handleRequest","profileName","request","requestId","id","startTime","Date","now","profile","findUnique","where","include","mcpServers","isActive","mcpServer","tools","orderBy","order","shouldLog","method","logId","log","createLog","profileId","requestType","requestPayload","JSON","stringify","status","logError","warn","response","handleInitialize","handleToolsList","handleToolsCall","params","handleResourcesList","handleResourcesRead","jsonrpc","error","code","message","durationMs","updateLog","responsePayload","errorMessage","Error","_profile","result","protocolVersion","capabilities","resources","serverInfo","version","allTools","profileServer","server","instance","getServerInstance","listTools","tool","customization","find","t","toolName","isEnabled","push","customName","description","customDescription","inputSchema","hasTool","some","mcpServerId","callTool","arguments","allResources","listResources","content","readResource","uri","getToolsForServer","serverId","cached","get","config","parseJson","builtinId","storedConfig","apiKeyConfig","convertApiKeyConfig","has","pkg","createServer","initialize","set","type","url","remoteServer","transport","stored","apiKey","headerName","template","headerValueTemplate","headerValue","replace","value","parse","getProfileInfo","serverStatus","ps","serverTools","connected","toolCount","length","total","Object","values","filter","s","servers"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxheroes/local-mcp-backend",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "NestJS API server providing MCP proxy, server aggregation, OAuth 2.1, and profile management",
5
5
  "license": "Elastic-2.0",
6
6
  "type": "module",
@@ -19,9 +19,9 @@
19
19
  "reflect-metadata": "^0.2.2",
20
20
  "rxjs": "^7.8.2",
21
21
  "zod": "^4.3.5",
22
- "@dxheroes/local-mcp-core": "0.3.1",
23
- "@dxheroes/local-mcp-database": "0.3.1",
24
- "@dxheroes/mcp-gemini-deep-research": "0.3.1"
22
+ "@dxheroes/local-mcp-database": "0.3.2",
23
+ "@dxheroes/mcp-gemini-deep-research": "0.3.2",
24
+ "@dxheroes/local-mcp-core": "0.3.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@nestjs/cli": "^11.0.14",
@@ -34,7 +34,7 @@
34
34
  "@types/node": "^25.0.6",
35
35
  "typescript": "^5.9.3",
36
36
  "vitest": "^4.0.17",
37
- "@dxheroes/local-mcp-config": "0.3.1"
37
+ "@dxheroes/local-mcp-config": "0.3.2"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "nest build",
@@ -71,9 +71,7 @@ export class ProxyService {
71
71
  where: { isActive: true },
72
72
  include: {
73
73
  mcpServer: true,
74
- tools: {
75
- where: { isEnabled: true },
76
- },
74
+ tools: true,
77
75
  },
78
76
  orderBy: { order: 'asc' },
79
77
  },
@@ -306,6 +304,11 @@ export class ProxyService {
306
304
  (t) => t.customName === params.name || t.toolName === params.name
307
305
  );
308
306
 
307
+ // Skip if tool is disabled
308
+ if (customization && !customization.isEnabled) {
309
+ continue;
310
+ }
311
+
309
312
  const toolName = customization?.toolName || params.name;
310
313
  const hasTool = tools.some((t) => t.name === toolName);
311
314
 
@@ -546,7 +549,7 @@ export class ProxyService {
546
549
  where: { isActive: true },
547
550
  include: {
548
551
  mcpServer: true,
549
- tools: { where: { isEnabled: true } },
552
+ tools: true,
550
553
  },
551
554
  orderBy: { order: 'asc' },
552
555
  },