2025-06-27 20:03:53 -07:00
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
2025-06-27 21:35:37 -07:00
"strings"
2025-06-27 20:03:53 -07:00
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
type RunResult struct {
Stdout string ` json:"stdout" `
Stderr string ` json:"stderr" `
ExitCode int ` json:"exit_code" `
Error string ` json:"error,omitempty" `
}
func main ( ) {
2025-06-27 20:29:53 -07:00
mcpServer := server . NewMCPServer (
"gocp" ,
2025-06-27 20:03:53 -07:00
"1.0.0" ,
2025-06-27 20:29:53 -07:00
server . WithToolCapabilities ( false ) ,
2025-06-27 20:03:53 -07:00
)
// Define the build_and_run_go tool
2025-06-27 20:29:53 -07:00
buildAndRunTool := mcp . NewTool ( "build_and_run_go" ,
2025-06-27 20:03:53 -07:00
mcp . WithDescription ( "Build and execute Go code" ) ,
2025-06-27 20:29:53 -07:00
mcp . WithString ( "code" ,
mcp . Required ( ) ,
mcp . Description ( "The Go source code to build and run" ) ,
) ,
mcp . WithNumber ( "timeout" ,
mcp . Description ( "Timeout in seconds (default: 30)" ) ,
) ,
2025-06-27 20:03:53 -07:00
)
2025-06-27 20:29:53 -07:00
mcpServer . AddTool ( buildAndRunTool , buildAndRunHandler )
// Define the find_symbols tool
findSymbolsTool := mcp . NewTool ( "find_symbols" ,
mcp . WithDescription ( "Find all functions, types, interfaces, constants, and variables by name/pattern" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "pattern" ,
mcp . Description ( "Symbol name pattern to search for (case-insensitive substring match)" ) ,
) ,
)
mcpServer . AddTool ( findSymbolsTool , findSymbolsHandler )
// Define the get_type_info tool
getTypeInfoTool := mcp . NewTool ( "get_type_info" ,
mcp . WithDescription ( "Get detailed information about a type including fields, methods, and embedded types" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "type" ,
mcp . Required ( ) ,
mcp . Description ( "Type name to get information for" ) ,
) ,
)
mcpServer . AddTool ( getTypeInfoTool , getTypeInfoHandler )
// Define the find_references tool
findReferencesTool := mcp . NewTool ( "find_references" ,
mcp . WithDescription ( "Find all references to a symbol (function calls, type usage, etc.)" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "symbol" ,
mcp . Required ( ) ,
mcp . Description ( "Symbol name to find references for" ) ,
) ,
)
mcpServer . AddTool ( findReferencesTool , findReferencesHandler )
// Define the list_packages tool
listPackagesTool := mcp . NewTool ( "list_packages" ,
mcp . WithDescription ( "List all Go packages in directory tree" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithBoolean ( "include_tests" ,
mcp . Description ( "Include test files in package listings (default: false)" ) ,
) ,
)
mcpServer . AddTool ( listPackagesTool , listPackagesHandler )
2025-06-27 20:03:53 -07:00
2025-06-27 20:55:52 -07:00
// Define the find_imports tool
findImportsTool := mcp . NewTool ( "find_imports" ,
mcp . WithDescription ( "Analyze import usage and find unused imports" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findImportsTool , findImportsHandler )
// Define the find_function_calls tool
findFunctionCallsTool := mcp . NewTool ( "find_function_calls" ,
mcp . WithDescription ( "Find all calls to a specific function" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "function" ,
mcp . Required ( ) ,
mcp . Description ( "Function name to find calls for" ) ,
) ,
)
mcpServer . AddTool ( findFunctionCallsTool , findFunctionCallsHandler )
// Define the find_struct_usage tool
findStructUsageTool := mcp . NewTool ( "find_struct_usage" ,
mcp . WithDescription ( "Find struct instantiations and field access patterns" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "struct" ,
mcp . Required ( ) ,
mcp . Description ( "Struct name to analyze usage for" ) ,
) ,
)
mcpServer . AddTool ( findStructUsageTool , findStructUsageHandler )
// Define the extract_interfaces tool
extractInterfacesTool := mcp . NewTool ( "extract_interfaces" ,
mcp . WithDescription ( "Find types implementing an interface or suggest interfaces" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "interface" ,
mcp . Description ( "Interface name to find implementations for (if empty, lists all interfaces)" ) ,
) ,
)
mcpServer . AddTool ( extractInterfacesTool , extractInterfacesHandler )
// Define the find_errors tool
findErrorsTool := mcp . NewTool ( "find_errors" ,
mcp . WithDescription ( "Find error handling patterns and unhandled errors" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findErrorsTool , findErrorsHandler )
// Define the analyze_tests tool
analyzeTestsTool := mcp . NewTool ( "analyze_tests" ,
mcp . WithDescription ( "Analyze test coverage and find untested exported functions" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeTestsTool , analyzeTestsHandler )
// Define the find_comments tool
findCommentsTool := mcp . NewTool ( "find_comments" ,
2025-06-27 21:53:04 -07:00
mcp . WithDescription ( "Find all comments in Go files, with optional filtering for TODOs or undocumented exports" ) ,
2025-06-27 20:55:52 -07:00
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "type" ,
2025-06-27 21:53:04 -07:00
mcp . Description ( "Comment type: 'todo' (filters by TODO keywords), 'undocumented' (finds undocumented exports), or 'all' (returns all comments, default)" ) ,
) ,
mcp . WithString ( "filter" ,
mcp . Description ( "Optional regex to filter comments (applies to 'todo' and 'all' types)" ) ,
2025-06-27 20:55:52 -07:00
) ,
2025-06-27 22:20:01 -07:00
mcp . WithBool ( "include_context" ,
mcp . Description ( "Include surrounding lines of code as context (default: false)" ) ,
) ,
2025-06-27 20:55:52 -07:00
)
mcpServer . AddTool ( findCommentsTool , findCommentsHandler )
// Define the analyze_dependencies tool
analyzeDependenciesTool := mcp . NewTool ( "analyze_dependencies" ,
mcp . WithDescription ( "Analyze package dependencies and find cycles" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeDependenciesTool , analyzeDependenciesHandler )
// Define the find_generics tool
findGenericsTool := mcp . NewTool ( "find_generics" ,
mcp . WithDescription ( "Find generic types, functions and their instantiations" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findGenericsTool , findGenericsHandler )
2025-06-27 21:04:39 -07:00
// Define the find_dead_code tool
findDeadCodeTool := mcp . NewTool ( "find_dead_code" ,
mcp . WithDescription ( "Find unreachable code, unused variables, and dead branches" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findDeadCodeTool , findDeadCodeHandler )
// Define the find_duplicates tool
findDuplicatesTool := mcp . NewTool ( "find_duplicates" ,
mcp . WithDescription ( "Detect code duplication and similar function patterns" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithNumber ( "threshold" ,
mcp . Description ( "Similarity threshold (0.0-1.0, default: 0.8)" ) ,
) ,
)
mcpServer . AddTool ( findDuplicatesTool , findDuplicatesHandler )
// Define the find_inefficiencies tool
findInefficienciesTool := mcp . NewTool ( "find_inefficiencies" ,
mcp . WithDescription ( "Find performance inefficiencies like string concatenation in loops" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findInefficienciesTool , findInefficienciesHandler )
// Define the extract_api tool
extractApiTool := mcp . NewTool ( "extract_api" ,
mcp . WithDescription ( "Extract public API surface and detect breaking changes" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( extractApiTool , extractApiHandler )
// Define the generate_docs tool
generateDocsTool := mcp . NewTool ( "generate_docs" ,
mcp . WithDescription ( "Auto-generate documentation from code structure" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
mcp . WithString ( "format" ,
mcp . Description ( "Output format: 'markdown' or 'json' (default: 'markdown')" ) ,
) ,
)
mcpServer . AddTool ( generateDocsTool , generateDocsHandler )
// Define the find_deprecated tool
findDeprecatedTool := mcp . NewTool ( "find_deprecated" ,
mcp . WithDescription ( "Find deprecated usage and suggest alternatives" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findDeprecatedTool , findDeprecatedHandler )
// Define the analyze_coupling tool
analyzeCouplingTool := mcp . NewTool ( "analyze_coupling" ,
mcp . WithDescription ( "Analyze package/module coupling and suggest refactoring" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeCouplingTool , analyzeCouplingHandler )
// Define the find_patterns tool
findPatternsTool := mcp . NewTool ( "find_patterns" ,
mcp . WithDescription ( "Find design patterns usage (singleton, factory, etc.)" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findPatternsTool , findPatternsHandler )
// Define the analyze_architecture tool
analyzeArchitectureTool := mcp . NewTool ( "analyze_architecture" ,
mcp . WithDescription ( "Analyze layer violations and dependency direction" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeArchitectureTool , analyzeArchitectureHandler )
// Define the analyze_go_idioms tool
analyzeGoIdiomsTool := mcp . NewTool ( "analyze_go_idioms" ,
mcp . WithDescription ( "Check for idiomatic Go patterns and suggest improvements" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeGoIdiomsTool , analyzeGoIdiomsHandler )
// Define the find_context_usage tool
findContextUsageTool := mcp . NewTool ( "find_context_usage" ,
mcp . WithDescription ( "Analyze Context.Context usage patterns and find missing contexts" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findContextUsageTool , findContextUsageHandler )
// Define the analyze_embedding tool
analyzeEmbeddingTool := mcp . NewTool ( "analyze_embedding" ,
mcp . WithDescription ( "Analyze interface and struct embedding patterns" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeEmbeddingTool , analyzeEmbeddingHandler )
// Define the analyze_test_quality tool
analyzeTestQualityTool := mcp . NewTool ( "analyze_test_quality" ,
mcp . WithDescription ( "Analyze test patterns, assertion quality, and mock usage" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( analyzeTestQualityTool , analyzeTestQualityHandler )
// Define the find_missing_tests tool
findMissingTestsTool := mcp . NewTool ( "find_missing_tests" ,
mcp . WithDescription ( "Find missing tests based on complexity and criticality" ) ,
mcp . WithString ( "dir" ,
mcp . Description ( "Directory to search (default: current directory)" ) ,
) ,
)
mcpServer . AddTool ( findMissingTestsTool , findMissingTestsHandler )
2025-06-27 21:35:37 -07:00
// Define the read_range tool
readRangeTool := mcp . NewTool ( "read_range" ,
mcp . WithDescription ( "Read file content by line/column or byte range" ) ,
mcp . WithString ( "file" ,
mcp . Required ( ) ,
mcp . Description ( "File path to read from" ) ,
) ,
mcp . WithNumber ( "start_line" ,
mcp . Description ( "Start line (1-based, use with end_line)" ) ,
) ,
mcp . WithNumber ( "end_line" ,
mcp . Description ( "End line (1-based, inclusive)" ) ,
) ,
mcp . WithNumber ( "start_col" ,
mcp . Description ( "Start column (1-based, optional)" ) ,
) ,
mcp . WithNumber ( "end_col" ,
mcp . Description ( "End column (1-based, optional)" ) ,
) ,
mcp . WithNumber ( "start_byte" ,
mcp . Description ( "Start byte offset (0-based, use with end_byte)" ) ,
) ,
mcp . WithNumber ( "end_byte" ,
mcp . Description ( "End byte offset (0-based, exclusive)" ) ,
) ,
)
mcpServer . AddTool ( readRangeTool , readRangeHandler )
// Define the write_range tool
writeRangeTool := mcp . NewTool ( "write_range" ,
mcp . WithDescription ( "Write content to file at specific line/column or byte range" ) ,
mcp . WithString ( "file" ,
mcp . Required ( ) ,
mcp . Description ( "File path to write to" ) ,
) ,
mcp . WithString ( "content" ,
mcp . Required ( ) ,
mcp . Description ( "Content to write" ) ,
) ,
mcp . WithNumber ( "start_line" ,
mcp . Description ( "Start line (1-based, use with end_line)" ) ,
) ,
mcp . WithNumber ( "end_line" ,
mcp . Description ( "End line (1-based, inclusive)" ) ,
) ,
mcp . WithNumber ( "start_col" ,
mcp . Description ( "Start column (1-based, optional)" ) ,
) ,
mcp . WithNumber ( "end_col" ,
mcp . Description ( "End column (1-based, optional)" ) ,
) ,
mcp . WithNumber ( "start_byte" ,
mcp . Description ( "Start byte offset (0-based, use with end_byte)" ) ,
) ,
mcp . WithNumber ( "end_byte" ,
mcp . Description ( "End byte offset (0-based, exclusive)" ) ,
) ,
mcp . WithString ( "confirm_old" ,
mcp . Description ( "Expected old content for confirmation before replacing" ) ,
) ,
)
mcpServer . AddTool ( writeRangeTool , writeRangeHandler )
// Define the search_replace tool
searchReplaceTool := mcp . NewTool ( "search_replace" ,
2025-06-27 22:08:12 -07:00
mcp . WithDescription ( "Search and optionally replace text in files. Supports context-aware replacements with before/after patterns." ) ,
2025-06-27 21:35:37 -07:00
mcp . WithString ( "paths" ,
mcp . Required ( ) ,
mcp . Description ( "File/directory path or comma-separated paths to search" ) ,
) ,
mcp . WithString ( "pattern" ,
mcp . Required ( ) ,
2025-06-27 21:38:51 -07:00
mcp . Description ( "Search pattern (string or regex). Used for simple search unless context_pattern is provided." ) ,
2025-06-27 21:35:37 -07:00
) ,
mcp . WithString ( "replacement" ,
mcp . Description ( "Replacement text (omit for search-only)" ) ,
) ,
mcp . WithBoolean ( "regex" ,
mcp . Description ( "Use regex pattern matching (default: false)" ) ,
) ,
mcp . WithBoolean ( "case_insensitive" ,
mcp . Description ( "Case-insensitive matching (default: false)" ) ,
) ,
mcp . WithBoolean ( "include_context" ,
mcp . Description ( "Include line context in search results (default: false)" ) ,
) ,
2025-06-27 22:08:12 -07:00
mcp . WithString ( "before_pattern" ,
mcp . Description ( "Pattern that must appear before the main pattern (for context-aware replacement)" ) ,
2025-06-27 21:38:51 -07:00
) ,
2025-06-27 22:08:12 -07:00
mcp . WithString ( "after_pattern" ,
mcp . Description ( "Pattern that must appear after the main pattern (for context-aware replacement)" ) ,
2025-06-27 21:38:51 -07:00
) ,
2025-06-27 21:35:37 -07:00
)
mcpServer . AddTool ( searchReplaceTool , searchReplaceHandler )
2025-06-27 20:03:53 -07:00
// Start the server
2025-06-27 20:29:53 -07:00
if err := server . ServeStdio ( mcpServer ) ; err != nil {
2025-06-27 20:03:53 -07:00
fmt . Fprintf ( os . Stderr , "Server error: %v\n" , err )
os . Exit ( 1 )
}
}
2025-06-27 20:29:53 -07:00
func buildAndRunHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
code , err := request . RequireString ( "code" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
2025-06-27 20:03:53 -07:00
}
2025-06-27 20:29:53 -07:00
timeout := request . GetFloat ( "timeout" , 30.0 )
2025-06-27 20:03:53 -07:00
2025-06-27 20:29:53 -07:00
stdout , stderr , exitCode , runErr := buildAndRunGo ( code , time . Duration ( timeout ) * time . Second )
2025-06-27 20:03:53 -07:00
result := RunResult {
Stdout : stdout ,
Stderr : stderr ,
ExitCode : exitCode ,
}
2025-06-27 20:29:53 -07:00
if runErr != nil {
result . Error = runErr . Error ( )
2025-06-27 20:03:53 -07:00
}
jsonData , err := json . Marshal ( result )
if err != nil {
2025-06-27 20:29:53 -07:00
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal result: %v" , err ) ) , nil
2025-06-27 20:03:53 -07:00
}
2025-06-27 20:29:53 -07:00
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
2025-06-27 20:03:53 -07:00
}
func buildAndRunGo ( code string , timeout time . Duration ) ( stdout , stderr string , exitCode int , err error ) {
tmpDir , err := os . MkdirTemp ( "" , "gocp-*" )
if err != nil {
return "" , "" , - 1 , fmt . Errorf ( "failed to create temp dir: %w" , err )
}
defer os . RemoveAll ( tmpDir )
tmpFile := filepath . Join ( tmpDir , "main.go" )
if err := os . WriteFile ( tmpFile , [ ] byte ( code ) , 0644 ) ; err != nil {
return "" , "" , - 1 , fmt . Errorf ( "failed to write code: %w" , err )
}
ctx , cancel := context . WithTimeout ( context . Background ( ) , timeout )
defer cancel ( )
modCmd := exec . CommandContext ( ctx , "go" , "mod" , "init" , "temp" )
modCmd . Dir = tmpDir
if err := modCmd . Run ( ) ; err != nil {
return "" , "" , - 1 , fmt . Errorf ( "failed to initialize go.mod: %w" , err )
}
runCmd := exec . CommandContext ( ctx , "go" , "run" , tmpFile )
runCmd . Dir = tmpDir
var stdoutBuf , stderrBuf bytes . Buffer
runCmd . Stdout = & stdoutBuf
runCmd . Stderr = & stderrBuf
err = runCmd . Run ( )
exitCode = 0
if err != nil {
if exitErr , ok := err . ( * exec . ExitError ) ; ok {
exitCode = exitErr . ExitCode ( )
2025-06-27 20:29:53 -07:00
err = nil
2025-06-27 20:03:53 -07:00
} else if ctx . Err ( ) == context . DeadlineExceeded {
return stdoutBuf . String ( ) , stderrBuf . String ( ) , - 1 , fmt . Errorf ( "execution timeout exceeded" )
} else {
return stdoutBuf . String ( ) , stderrBuf . String ( ) , - 1 , err
}
}
return stdoutBuf . String ( ) , stderrBuf . String ( ) , exitCode , nil
2025-06-27 20:29:53 -07:00
}
func findSymbolsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
pattern := request . GetString ( "pattern" , "" )
symbols , err := findSymbols ( dir , pattern )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find symbols: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( symbols )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal symbols: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func getTypeInfoHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
typeName , err := request . RequireString ( "type" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
info , err := getTypeInfo ( dir , typeName )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to get type info: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( info )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal type info: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findReferencesHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
symbol , err := request . RequireString ( "symbol" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
refs , err := findReferences ( dir , symbol )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find references: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( refs )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal references: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func listPackagesHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
includeTests := request . GetBool ( "include_tests" , false )
packages , err := listPackages ( dir , includeTests )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to list packages: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( packages )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal packages: %v" , err ) ) , nil
}
2025-06-27 20:55:52 -07:00
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findImportsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
imports , err := findImports ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze imports: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( imports )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal imports: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findFunctionCallsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
function , err := request . RequireString ( "function" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
calls , err := findFunctionCalls ( dir , function )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find function calls: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( calls )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal calls: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findStructUsageHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
structName , err := request . RequireString ( "struct" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
usage , err := findStructUsage ( dir , structName )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find struct usage: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( usage )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal usage: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func extractInterfacesHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
interfaceName := request . GetString ( "interface" , "" )
interfaces , err := extractInterfaces ( dir , interfaceName )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to extract interfaces: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( interfaces )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal interfaces: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findErrorsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
errors , err := findErrors ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find errors: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( errors )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal errors: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeTestsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
analysis , err := analyzeTests ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze tests: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( analysis )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal analysis: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findCommentsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
commentType := request . GetString ( "type" , "all" )
2025-06-27 21:53:04 -07:00
filter := request . GetString ( "filter" , "" )
2025-06-27 22:20:01 -07:00
includeContext := request . GetBool ( "include_context" , false )
2025-06-27 20:55:52 -07:00
2025-06-27 22:20:01 -07:00
comments , err := findComments ( dir , commentType , filter , includeContext )
2025-06-27 20:55:52 -07:00
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find comments: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( comments )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal comments: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeDependenciesHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
deps , err := analyzeDependencies ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze dependencies: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( deps )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal dependencies: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findGenericsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
generics , err := findGenerics ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find generics: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( generics )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal generics: %v" , err ) ) , nil
}
2025-06-27 21:04:39 -07:00
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findDeadCodeHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
deadCode , err := findDeadCode ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find dead code: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( deadCode )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal dead code: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findDuplicatesHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
threshold := request . GetFloat ( "threshold" , 0.8 )
duplicates , err := findDuplicates ( dir , threshold )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find duplicates: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( duplicates )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal duplicates: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findInefficienciesHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
inefficiencies , err := findInefficiencies ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find inefficiencies: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( inefficiencies )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal inefficiencies: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func extractApiHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
api , err := extractApi ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to extract API: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( api )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal API: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func generateDocsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
format := request . GetString ( "format" , "markdown" )
docs , err := generateDocs ( dir , format )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to generate docs: %v" , err ) ) , nil
}
if format == "markdown" {
return mcp . NewToolResultText ( docs . ( string ) ) , nil
}
jsonData , err := json . Marshal ( docs )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal docs: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findDeprecatedHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
deprecated , err := findDeprecated ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find deprecated: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( deprecated )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal deprecated: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeCouplingHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
coupling , err := analyzeCoupling ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze coupling: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( coupling )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal coupling: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findPatternsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
patterns , err := findPatterns ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find patterns: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( patterns )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal patterns: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeArchitectureHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
architecture , err := analyzeArchitecture ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze architecture: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( architecture )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal architecture: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeGoIdiomsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
idioms , err := analyzeGoIdioms ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze Go idioms: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( idioms )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal idioms: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findContextUsageHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
contextUsage , err := findContextUsage ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find context usage: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( contextUsage )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal context usage: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeEmbeddingHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
embedding , err := analyzeEmbedding ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze embedding: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( embedding )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal embedding: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func analyzeTestQualityHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
testQuality , err := analyzeTestQuality ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to analyze test quality: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( testQuality )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal test quality: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func findMissingTestsHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
dir := request . GetString ( "dir" , "./" )
missingTests , err := findMissingTests ( dir )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to find missing tests: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( missingTests )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal missing tests: %v" , err ) ) , nil
}
2025-06-27 21:35:37 -07:00
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func readRangeHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
file , err := request . RequireString ( "file" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
startLine := int ( request . GetFloat ( "start_line" , - 1 ) )
endLine := int ( request . GetFloat ( "end_line" , - 1 ) )
startCol := int ( request . GetFloat ( "start_col" , - 1 ) )
endCol := int ( request . GetFloat ( "end_col" , - 1 ) )
startByte := int ( request . GetFloat ( "start_byte" , - 1 ) )
endByte := int ( request . GetFloat ( "end_byte" , - 1 ) )
result , err := readRange ( file , startLine , endLine , startCol , endCol , startByte , endByte )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to read range: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( result )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal result: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func writeRangeHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
file , err := request . RequireString ( "file" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
content , err := request . RequireString ( "content" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
startLine := int ( request . GetFloat ( "start_line" , - 1 ) )
endLine := int ( request . GetFloat ( "end_line" , - 1 ) )
startCol := int ( request . GetFloat ( "start_col" , - 1 ) )
endCol := int ( request . GetFloat ( "end_col" , - 1 ) )
startByte := int ( request . GetFloat ( "start_byte" , - 1 ) )
endByte := int ( request . GetFloat ( "end_byte" , - 1 ) )
confirmOld := request . GetString ( "confirm_old" , "" )
result , err := writeRange ( file , content , startLine , endLine , startCol , endCol , startByte , endByte , confirmOld )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to write range: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( result )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal result: %v" , err ) ) , nil
}
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
}
func searchReplaceHandler ( ctx context . Context , request mcp . CallToolRequest ) ( * mcp . CallToolResult , error ) {
// For now, paths will be a single string
pathStr , err := request . RequireString ( "paths" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
// Split paths by comma if multiple
paths := strings . Split ( pathStr , "," )
for i := range paths {
paths [ i ] = strings . TrimSpace ( paths [ i ] )
}
pattern , err := request . RequireString ( "pattern" )
if err != nil {
return mcp . NewToolResultError ( err . Error ( ) ) , nil
}
var replacement * string
2025-06-27 22:13:21 -07:00
args := request . GetArguments ( )
if val , ok := args [ "replacement" ] ; ok {
if str , ok := val . ( string ) ; ok {
replacement = & str
}
2025-06-27 21:35:37 -07:00
}
useRegex := request . GetBool ( "regex" , false )
caseInsensitive := request . GetBool ( "case_insensitive" , false )
includeContext := request . GetBool ( "include_context" , false )
2025-06-27 22:08:12 -07:00
beforePattern := request . GetString ( "before_pattern" , "" )
afterPattern := request . GetString ( "after_pattern" , "" )
2025-06-27 21:35:37 -07:00
2025-06-27 22:08:12 -07:00
result , err := searchReplace ( paths , pattern , replacement , useRegex , caseInsensitive , includeContext , beforePattern , afterPattern )
2025-06-27 21:35:37 -07:00
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "search/replace failed: %v" , err ) ) , nil
}
jsonData , err := json . Marshal ( result )
if err != nil {
return mcp . NewToolResultError ( fmt . Sprintf ( "failed to marshal result: %v" , err ) ) , nil
}
2025-06-27 20:29:53 -07:00
return mcp . NewToolResultText ( string ( jsonData ) ) , nil
2025-06-27 20:03:53 -07:00
}