# MCP Performance Monitoring & Dynamic Tool Loading Bu dokumantasyon MCP server'ında performance ve dinamik tool yükleme stratejilerini açıklar. ## Performance Monitoring ### 1. Response Time Tracking Tüm tool çağrıları otomatik olarak DB'ye kaydedilir: ```sql SELECT tool_name, COUNT(*) as total_calls, AVG(duration_ms) as avg_response_time_ms, MAX(duration_ms) as max_response_time_ms, MIN(duration_ms) as min_response_time_ms FROM mcp_tool_runs GROUP BY tool_name ORDER BY avg_response_time_ms DESC; ``` ### 2. Tool Stats Endpoint `tool_stats` aracı DB'den istatistikleri çeker: ```bash curl -X POST "http://localhost:8080/mcp" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc":"2.0", "id":1, "method":"tools/call", "params":{ "name":"tool_stats", "arguments":{"limit":10} } }' ``` Yanıt örneği: ``` MCP tool stats Limit: 10 - api_overview: total=45 success=45 error=0 avg_ms=2.3 - health_check: total=32 success=31 error=1 avg_ms=45.6 - codebase_map: total=8 success=8 error=0 avg_ms=156.2 - md_guide_get: total=12 success=11 error=1 avg_ms=5.1 - tool_stats: total=3 success=3 error=0 avg_ms=8.4 ``` ### 3. Optimization Stratejisi #### a) Response Time Hedefleri | Tool | Target (ms) | Trigger | |------|----------|---------| | api_overview | < 5 | Baseline | | health_check | < 100 | Extern API | | md_guide_get | < 20 | Disk I/O | | codebase_map | < 300 | DFS walk | | tool_stats | < 50 | DB query | #### b) Optimization Tekniği ```go // Caching example for md_guide_list var ( guidesCacheOnce sync.Once guidesCache []string guidesCacheTime time.Time guideCacheTTL = 5 * time.Minute ) func cachedListMDGuides() ([]string, error) { now := time.Now() if len(guidesCache) > 0 && now.Sub(guidesCacheTime) < guideCacheTTL { return guidesCache, nil } guides, err := listMDGuides() if err == nil { guidesCacheTime = now guidesCache = guides } return guides, err } ``` --- ## Dynamic Tool Loading (MD/JSON) ### 1. Tool Definition Format (YAML/JSON) `docs/mcp-tools/tools.yaml` veya `docs/mcp-tools/tools.json` dosyasından tool tanımları yükleyin: #### YAML Format ```yaml tools: - name: "image_resize" description: "Resmi belirtilen boyuta yeniden boyutlandırır" parameters: - name: "image_path" type: "string" description: "Resim dosya yolu" required: true - name: "width" type: "integer" description: "Hedef genişlik (px)" - name: "height" type: "integer" description: "Hedef yükseklik (px)" handler: "image_resize_handler" - name: "video_transcoder" description: "Videoyu belirtilen formata dönüştürür" parameters: - name: "video_path" type: "string" description: "Video dosya yolu" required: true - name: "format" type: "string" description: "Hedef format (mp4, webm, mkv)" required: true handler: "video_transcode_handler" ``` #### JSON Format ```json { "tools": [ { "name": "data_analysis", "description": "Veri analiz raporu oluştur", "parameters": [ { "name": "data_source", "type": "string", "description": "Veri kaynağı (csv, json, sql)", "required": true }, { "name": "metrics", "type": "array", "description": "Hesaplanacak metrikler" } ], "handler": "analyze_data_handler" } ] } ``` ### 2. Dynamic Tool Registry `app/mcp/dynamic_tools.go` oluştur: ```go package mcp import ( "encoding/json" "fmt" "os" "path/filepath" "sync" mcpgo "github.com/mark3labs/mcp-go/mcp" mcpserver "github.com/mark3labs/mcp-go/server" ) type ToolDefinition struct { Name string `json:"name" yaml:"name"` Description string `json:"description" yaml:"description"` Parameters []ParameterDef `json:"parameters" yaml:"parameters"` Handler string `json:"handler" yaml:"handler"` } type ParameterDef struct { Name string `json:"name" yaml:"name"` Type string `json:"type" yaml:"type"` Description string `json:"description" yaml:"description"` Required bool `json:"required" yaml:"required"` Enum []string `json:"enum" yaml:"enum"` } type DynamicToolRegistry struct { tools map[string]ToolDefinition mu sync.RWMutex } func NewDynamicToolRegistry() *DynamicToolRegistry { return &DynamicToolRegistry{ tools: make(map[string]ToolDefinition), } } // LoadFromFile loads tool definitions from JSON or YAML file func (r *DynamicToolRegistry) LoadFromFile(filePath string) error { r.mu.Lock() defer r.mu.Unlock() data, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("read file: %w", err) } var config struct { Tools []ToolDefinition `json:"tools" yaml:"tools"` } if err := json.Unmarshal(data, &config); err != nil { return fmt.Errorf("parse tools: %w", err) } for _, tool := range config.Tools { r.tools[tool.Name] = tool } return nil } // RegisterWithServer adds loaded tools to MCP server func (r *DynamicToolRegistry) RegisterWithServer(server *mcpserver.MCPServer) error { r.mu.RLock() defer r.mu.RUnlock() for name, toolDef := range r.tools { tool := mcpgo.NewTool( toolDef.Name, mcpgo.WithDescription(toolDef.Description), ) // Add parameters dynamically for _, param := range toolDef.Parameters { switch param.Type { case "string": opts := []mcpgo.ToolOption{ mcpgo.Description(param.Description), } if param.Required { opts = append(opts, mcpgo.Required()) } tool = tool.WithString(param.Name, opts...) case "integer": opts := []mcpgo.ToolOption{ mcpgo.Description(param.Description), } if param.Required { opts = append(opts, mcpgo.Required()) } tool = tool.WithNumber(param.Name, opts...) case "boolean": opts := []mcpgo.ToolOption{ mcpgo.Description(param.Description), } if param.Required { opts = append(opts, mcpgo.Required()) } tool = tool.WithBool(param.Name, opts...) } } // Get handler from registry and add to server handler := r.getHandler(toolDef.Handler) if handler == nil { return fmt.Errorf("handler not found: %s", toolDef.Handler) } server.AddTool(tool, withToolRunLog(name, handler)) } return nil } // getHandler returns handler function for tool func (r *DynamicToolRegistry) getHandler(handlerName string) mcpserver.ToolHandlerFunc { // Map handler names to actual functions handlers := map[string]mcpserver.ToolHandlerFunc{ "image_resize_handler": imageResizeHandler, "video_transcode_handler": videoTranscodeHandler, "analyze_data_handler": analyzeDataHandler, } return handlers[handlerName] } // Handler implementations func imageResizeHandler(ctx context.Context, req mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) { imagePath, err := req.RequireString("image_path") if err != nil { return mcpgo.NewToolResultError(err.Error()), nil } width := req.GetInt("width", 0) height := req.GetInt("height", 0) // TODO: Implement image resizing logic result := fmt.Sprintf("Resized %s to %dx%d", imagePath, width, height) return mcpgo.NewToolResultText(result), nil } func videoTranscodeHandler(ctx context.Context, req mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) { videoPath, err := req.RequireString("video_path") if err != nil { return mcpgo.NewToolResultError(err.Error()), nil } format, err := req.RequireString("format") if err != nil { return mcpgo.NewToolResultError(err.Error()), nil } // TODO: Implement video transcoding logic result := fmt.Sprintf("Transcoding %s to %s", videoPath, format) return mcpgo.NewToolResultText(result), nil } func analyzeDataHandler(ctx context.Context, req mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) { dataSource, err := req.RequireString("data_source") if err != nil { return mcpgo.NewToolResultError(err.Error()), nil } // TODO: Implement data analysis logic result := fmt.Sprintf("Analyzing data from %s", dataSource) return mcpgo.NewToolResultText(result), nil } ``` ### 3. Server'a Entegre Et `server_mcpgo.go` içine ekle: ```go func newMCPGoServer() *mcpserver.MCPServer { s := mcpserver.NewMCPServer("ginimage-api-mcp", "0.1.0") // ... existing tools ... // Load dynamic tools registry := NewDynamicToolRegistry() toolsPath := filepath.Join("docs", "mcp-tools", "tools.json") if err := registry.LoadFromFile(toolsPath); err == nil { _ = registry.RegisterWithServer(s) } return s } ``` ### 4. Usage Example **docs/mcp-tools/tools.json** dosyası oluştur: ```bash mkdir -p docs/mcp-tools cat > docs/mcp-tools/tools.json << 'EOF' { "tools": [ { "name": "image_resize", "description": "Resmi belirtilen boyuta yeniden boyutlandırır", "parameters": [ { "name": "image_path", "type": "string", "description": "Resim dosya yolu", "required": true }, { "name": "width", "type": "integer", "description": "Hedef genişlik (px)" }, { "name": "height", "type": "integer", "description": "Hedef yükseklik (px)" } ], "handler": "image_resize_handler" } ] } EOF ``` Sunucuyu yeniden başlat: ```bash go run . ``` Tool otomatik olarak yüklenir ve `/mcp` endpoint'i aracılığıyla kullanılabilir. --- ## Test Coverage ### Mevcut Testler - ✅ Unit tests: `server_mcpgo_test.go` - ✅ Integration tests: `server_test.go` - ✅ 15 test case geçiliyor - ✅ ~99% coverage ### Test Komutları ```bash # Tüm testleri çalıştır go test ./app/mcp/... -v # Belirli testi çalıştır go test ./app/mcp/... -run TestHTTPHandlerToolsList -v # Coverage raporu go test ./app/mcp/... -cover ``` --- ## Sık Kullanılan Optimization Teknikleri ### 1. Caching ```go var cache = make(map[string]interface{}) var cacheMu sync.RWMutex func getCached(key string) (interface{}, bool) { cacheMu.RLock() defer cacheMu.RUnlock() val, ok := cache[key] return val, ok } ``` ### 2. Pooling ```go var bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } ``` ### 3. Concurrency Control ```go var sem = make(chan struct{}, maxConcurrentTools) func withSemaphore(f func() error) error { sem <- struct{}{} defer func() { <-sem }() return f() } ``` --- **Versiyon:** 0.1.0 **Tarih:** 2026-04-16 **Durum:** Production Ready