Bläddra i källkod

feat: add missing MCP endpoints

- Added GET /api/mcp/stats endpoint for overall MCP statistics
- Added GET /api/mcp/shops/:shopId/logs endpoint to get shop-specific MCP logs
- Added DELETE /api/mcp/shops/:shopId/logs endpoint to clear shop MCP logs
- Implemented clearShopMcpLogs() database method
- All endpoints support dual ID lookup (internal and custom IDs)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fszontagh 8 månader sedan
förälder
incheckning
265189b1d3
2 ändrade filer med 252 tillägg och 0 borttagningar
  1. 242 0
      src/api/components/McpEndpoint.ts
  2. 10 0
      src/database/Database.ts

+ 242 - 0
src/api/components/McpEndpoint.ts

@@ -378,6 +378,163 @@ export class McpEndpoint extends BaseEndpointComponent {
             }
             }
           }
           }
         }
         }
+      ),
+
+      // MCP Stats
+      this.createEndpoint(
+        'GET',
+        '/api/mcp/stats',
+        this.getMcpStats.bind(this),
+        {
+          summary: 'Get MCP statistics',
+          description: 'Get overall MCP statistics including top tools and recent activity',
+          tags: ['MCP'],
+          requiresAuth: true,
+          responses: {
+            '200': {
+              description: 'MCP statistics',
+              schema: {
+                type: 'object',
+                properties: {
+                  total_calls: { type: 'number' },
+                  successful_calls: { type: 'number' },
+                  failed_calls: { type: 'number' },
+                  average_response_time: { type: 'number' },
+                  top_tools: {
+                    type: 'array',
+                    items: {
+                      type: 'object',
+                      properties: {
+                        tool_name: { type: 'string' },
+                        call_count: { type: 'number' }
+                      }
+                    }
+                  },
+                  recent_activity: {
+                    type: 'array',
+                    items: {
+                      type: 'object',
+                      properties: {
+                        id: { type: 'string' },
+                        shop_id: { type: 'string' },
+                        tool_name: { type: 'string' },
+                        query: { type: 'string' },
+                        success: { type: 'boolean' },
+                        response_time: { type: 'number' },
+                        error_message: { type: 'string', nullable: true },
+                        created_at: { type: 'string' }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      ),
+
+      // Shop MCP Logs
+      this.createEndpoint(
+        'GET',
+        '/api/mcp/shops/:shopId/logs',
+        this.getShopMcpLogs.bind(this),
+        {
+          summary: 'Get shop MCP logs',
+          description: 'Get MCP logs for a specific shop',
+          tags: ['MCP'],
+          requiresAuth: true,
+          parameters: [
+            {
+              name: 'shopId',
+              in: 'path',
+              type: 'string',
+              required: true,
+              description: 'Shop ID'
+            },
+            {
+              name: 'limit',
+              in: 'query',
+              type: 'number',
+              required: false,
+              description: 'Maximum number of logs to return'
+            },
+            {
+              name: 'success',
+              in: 'query',
+              type: 'boolean',
+              required: false,
+              description: 'Filter by success status'
+            },
+            {
+              name: 'tool_name',
+              in: 'query',
+              type: 'string',
+              required: false,
+              description: 'Filter by tool name'
+            }
+          ],
+          responses: {
+            '200': {
+              description: 'Shop MCP logs',
+              schema: {
+                type: 'object',
+                properties: {
+                  logs: {
+                    type: 'array',
+                    items: {
+                      type: 'object',
+                      properties: {
+                        id: { type: 'string' },
+                        shop_id: { type: 'string' },
+                        tool_name: { type: 'string' },
+                        query: { type: 'string' },
+                        success: { type: 'boolean' },
+                        response_time: { type: 'number' },
+                        error_message: { type: 'string', nullable: true },
+                        created_at: { type: 'string' }
+                      }
+                    }
+                  }
+                }
+              }
+            },
+            '404': { description: 'Shop not found' }
+          }
+        }
+      ),
+
+      // Clear Shop MCP Logs
+      this.createEndpoint(
+        'DELETE',
+        '/api/mcp/shops/:shopId/logs',
+        this.clearShopMcpLogs.bind(this),
+        {
+          summary: 'Clear shop MCP logs',
+          description: 'Delete all MCP logs for a specific shop',
+          tags: ['MCP'],
+          requiresAuth: true,
+          parameters: [
+            {
+              name: 'shopId',
+              in: 'path',
+              type: 'string',
+              required: true,
+              description: 'Shop ID'
+            }
+          ],
+          responses: {
+            '200': {
+              description: 'Logs cleared successfully',
+              schema: {
+                type: 'object',
+                properties: {
+                  message: { type: 'string' }
+                }
+              }
+            },
+            '404': { description: 'Shop not found' }
+          }
+        }
       )
       )
     ];
     ];
   }
   }
@@ -535,4 +692,89 @@ export class McpEndpoint extends BaseEndpointComponent {
       res.status(500).json({ error: 'Internal server error' });
       res.status(500).json({ error: 'Internal server error' });
     }
     }
   }
   }
+
+  private async getMcpStats(req: Request, res: Response): Promise<void> {
+    try {
+      // Get overall analytics
+      const analytics = this.db.getMcpAnalytics({});
+
+      // Get top tools by usage
+      const toolUsage = this.db.getMcpToolUsage({});
+      const topTools = Object.entries(toolUsage)
+        .map(([tool_name, call_count]) => ({ tool_name, call_count }))
+        .sort((a, b) => b.call_count - a.call_count)
+        .slice(0, 10);
+
+      // Get recent activity (last 20 logs)
+      const recentLogs = this.db.getMcpLogs({ limit: 20, offset: 0 });
+
+      res.json({
+        total_calls: analytics.total_calls,
+        successful_calls: analytics.successful_calls,
+        failed_calls: analytics.failed_calls,
+        average_response_time: analytics.avg_response_time,
+        top_tools: topTools,
+        recent_activity: recentLogs.logs
+      });
+    } catch (error) {
+      logger.error('Failed to get MCP stats:', error);
+      res.status(500).json({ error: 'Internal server error' });
+    }
+  }
+
+  private async getShopMcpLogs(req: Request, res: Response): Promise<void> {
+    try {
+      const { shopId } = req.params;
+      const { limit, success, tool_name } = req.query;
+
+      // Get shop from database - try both internal ID and custom ID
+      let shop = this.db.getShopById(shopId);
+      if (!shop) {
+        shop = this.db.getShopByCustomId(shopId);
+      }
+      if (!shop) {
+        res.status(404).json({ error: 'Shop not found' });
+        return;
+      }
+
+      const options: any = {
+        shopId: shop.id,
+        limit: parseInt(limit as string) || 100,
+        offset: 0
+      };
+
+      if (success !== undefined) options.success = success === 'true';
+      if (tool_name) options.toolName = tool_name as string;
+
+      const result = this.db.getMcpLogs(options);
+
+      res.json({ logs: result.logs });
+    } catch (error) {
+      logger.error('Failed to get shop MCP logs:', error);
+      res.status(500).json({ error: 'Internal server error' });
+    }
+  }
+
+  private async clearShopMcpLogs(req: Request, res: Response): Promise<void> {
+    try {
+      const { shopId } = req.params;
+
+      // Get shop from database - try both internal ID and custom ID
+      let shop = this.db.getShopById(shopId);
+      if (!shop) {
+        shop = this.db.getShopByCustomId(shopId);
+      }
+      if (!shop) {
+        res.status(404).json({ error: 'Shop not found' });
+        return;
+      }
+
+      this.db.clearShopMcpLogs(shop.id);
+
+      res.json({ message: `Cleared all MCP logs for shop ${shop.id}` });
+    } catch (error) {
+      logger.error('Failed to clear shop MCP logs:', error);
+      res.status(500).json({ error: 'Internal server error' });
+    }
+  }
 }
 }

+ 10 - 0
src/database/Database.ts

@@ -1760,6 +1760,16 @@ export class ShopDatabase {
     return result.changes;
     return result.changes;
   }
   }
 
 
+  /**
+   * Clear all MCP logs for a specific shop
+   */
+  clearShopMcpLogs(shopId: string): void {
+    this.db.prepare(`
+      DELETE FROM mcp_logs
+      WHERE shop_id = ?
+    `).run(shopId);
+  }
+
   /**
   /**
    * Clean up old Qdrant error logs
    * Clean up old Qdrant error logs
    */
    */