Commit 14f1b12

mo khan <mo@mokhan.ca>
2025-06-21 20:26:47
feat: implement core MCP framework and Git server with TDD
- Add core MCP protocol types and JSON-RPC server implementation - Implement complete Git MCP server with 12 tools matching Python implementation - Add comprehensive test suite using TDD approach - Support all Git operations: status, diff, commit, add, reset, log, branches, etc. - Create project structure following design document specifications - Build statically-linked Go binary with no external dependencies 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 39c226b
cmd/git/main.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+	"context"
+	"log"
+
+	"github.com/xlgmokha/mcp/pkg/mcp"
+)
+
+func main() {
+	server := NewGitServer()
+	
+	// Set up basic initialization
+	server.SetInitializeHandler(func(req mcp.InitializeRequest) (mcp.InitializeResult, error) {
+		// Use default initialization
+		return mcp.InitializeResult{}, nil
+	})
+	
+	ctx := context.Background()
+	if err := server.Run(ctx); err != nil {
+		log.Fatalf("Server error: %v", err)
+	}
+}
\ No newline at end of file
cmd/git/server.go
@@ -0,0 +1,530 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"github.com/xlgmokha/mcp/pkg/mcp"
+)
+
+// GitServer implements the Git MCP server
+type GitServer struct {
+	*mcp.Server
+}
+
+// NewGitServer creates a new Git MCP server
+func NewGitServer() *GitServer {
+	server := mcp.NewServer("mcp-git", "1.0.0")
+	
+	gitServer := &GitServer{
+		Server: server,
+	}
+	
+	// Register all git tools
+	gitServer.registerTools()
+	
+	return gitServer
+}
+
+// registerTools registers all Git tools with the server
+func (gs *GitServer) registerTools() {
+	gs.RegisterTool("git_status", gs.HandleGitStatus)
+	gs.RegisterTool("git_diff_unstaged", gs.HandleGitDiffUnstaged)
+	gs.RegisterTool("git_diff_staged", gs.HandleGitDiffStaged)
+	gs.RegisterTool("git_diff", gs.HandleGitDiff)
+	gs.RegisterTool("git_commit", gs.HandleGitCommit)
+	gs.RegisterTool("git_add", gs.HandleGitAdd)
+	gs.RegisterTool("git_reset", gs.HandleGitReset)
+	gs.RegisterTool("git_log", gs.HandleGitLog)
+	gs.RegisterTool("git_create_branch", gs.HandleGitCreateBranch)
+	gs.RegisterTool("git_checkout", gs.HandleGitCheckout)
+	gs.RegisterTool("git_show", gs.HandleGitShow)
+	gs.RegisterTool("git_init", gs.HandleGitInit)
+}
+
+// ListTools returns all available Git tools
+func (gs *GitServer) ListTools() []mcp.Tool {
+	return []mcp.Tool{
+		{
+			Name:        "git_status",
+			Description: "Shows the working tree status",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+				},
+				"required": []string{"repo_path"},
+			},
+		},
+		{
+			Name:        "git_diff_unstaged",
+			Description: "Shows changes in the working directory that are not yet staged",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+				},
+				"required": []string{"repo_path"},
+			},
+		},
+		{
+			Name:        "git_diff_staged",
+			Description: "Shows changes that are staged for commit",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+				},
+				"required": []string{"repo_path"},
+			},
+		},
+		{
+			Name:        "git_diff",
+			Description: "Shows differences between branches or commits",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"target": map[string]interface{}{
+						"type":        "string",
+						"description": "Target branch or commit to diff against",
+					},
+				},
+				"required": []string{"repo_path", "target"},
+			},
+		},
+		{
+			Name:        "git_commit",
+			Description: "Records changes to the repository",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"message": map[string]interface{}{
+						"type":        "string",
+						"description": "Commit message",
+					},
+				},
+				"required": []string{"repo_path", "message"},
+			},
+		},
+		{
+			Name:        "git_add",
+			Description: "Adds file contents to the staging area",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"files": map[string]interface{}{
+						"type": "array",
+						"items": map[string]interface{}{
+							"type": "string",
+						},
+						"description": "List of files to add",
+					},
+				},
+				"required": []string{"repo_path", "files"},
+			},
+		},
+		{
+			Name:        "git_reset",
+			Description: "Unstages all staged changes",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+				},
+				"required": []string{"repo_path"},
+			},
+		},
+		{
+			Name:        "git_log",
+			Description: "Shows the commit logs",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"max_count": map[string]interface{}{
+						"type":        "integer",
+						"description": "Maximum number of commits to show",
+						"default":     10,
+					},
+				},
+				"required": []string{"repo_path"},
+			},
+		},
+		{
+			Name:        "git_create_branch",
+			Description: "Creates a new branch from an optional base branch",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"branch_name": map[string]interface{}{
+						"type":        "string",
+						"description": "Name of the new branch",
+					},
+					"base_branch": map[string]interface{}{
+						"type":        "string",
+						"description": "Base branch to create from (optional)",
+					},
+				},
+				"required": []string{"repo_path", "branch_name"},
+			},
+		},
+		{
+			Name:        "git_checkout",
+			Description: "Switches branches",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"branch_name": map[string]interface{}{
+						"type":        "string",
+						"description": "Name of the branch to checkout",
+					},
+				},
+				"required": []string{"repo_path", "branch_name"},
+			},
+		},
+		{
+			Name:        "git_show",
+			Description: "Shows the contents of a commit",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path to the Git repository",
+					},
+					"revision": map[string]interface{}{
+						"type":        "string",
+						"description": "Commit hash or reference to show",
+					},
+				},
+				"required": []string{"repo_path", "revision"},
+			},
+		},
+		{
+			Name:        "git_init",
+			Description: "Initialize a new Git repository",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"repo_path": map[string]interface{}{
+						"type":        "string",
+						"description": "Path where to initialize the Git repository",
+					},
+				},
+				"required": []string{"repo_path"},
+			},
+		},
+	}
+}
+
+// Tool handlers
+
+func (gs *GitServer) HandleGitStatus(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	output, err := gs.runGitCommand(repoPath, "status")
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git status failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Repository status:\n%s", output))), nil
+}
+
+func (gs *GitServer) HandleGitDiffUnstaged(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	output, err := gs.runGitCommand(repoPath, "diff")
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git diff failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Unstaged changes:\n%s", output))), nil
+}
+
+func (gs *GitServer) HandleGitDiffStaged(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	output, err := gs.runGitCommand(repoPath, "diff", "--cached")
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git diff --cached failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Staged changes:\n%s", output))), nil
+}
+
+func (gs *GitServer) HandleGitDiff(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	target, ok := req.Arguments["target"].(string)
+	if !ok {
+		return mcp.NewToolError("target is required"), nil
+	}
+
+	output, err := gs.runGitCommand(repoPath, "diff", target)
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git diff failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Diff with %s:\n%s", target, output))), nil
+}
+
+func (gs *GitServer) HandleGitCommit(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	message, ok := req.Arguments["message"].(string)
+	if !ok {
+		return mcp.NewToolError("message is required"), nil
+	}
+
+	output, err := gs.runGitCommand(repoPath, "commit", "-m", message)
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git commit failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Changes committed successfully:\n%s", output))), nil
+}
+
+func (gs *GitServer) HandleGitAdd(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	filesInterface, ok := req.Arguments["files"]
+	if !ok {
+		return mcp.NewToolError("files is required"), nil
+	}
+
+	files, err := gs.convertToStringSlice(filesInterface)
+	if err != nil {
+		return mcp.NewToolError("files must be an array of strings"), nil
+	}
+
+	args := append([]string{"add"}, files...)
+	_, err = gs.runGitCommand(repoPath, args...)
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git add failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent("Files staged successfully")), nil
+}
+
+func (gs *GitServer) HandleGitReset(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	_, err := gs.runGitCommand(repoPath, "reset")
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git reset failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent("All staged changes reset")), nil
+}
+
+func (gs *GitServer) HandleGitLog(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	maxCount := 10
+	if mc, exists := req.Arguments["max_count"]; exists {
+		if count, ok := mc.(float64); ok {
+			maxCount = int(count)
+		}
+	}
+
+	output, err := gs.runGitCommand(repoPath, "log", "--oneline", "-n", strconv.Itoa(maxCount))
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git log failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Commit history:\n%s", output))), nil
+}
+
+func (gs *GitServer) HandleGitCreateBranch(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	branchName, ok := req.Arguments["branch_name"].(string)
+	if !ok {
+		return mcp.NewToolError("branch_name is required"), nil
+	}
+
+	baseBranch, _ := req.Arguments["base_branch"].(string)
+
+	var args []string
+	if baseBranch != "" {
+		args = []string{"checkout", "-b", branchName, baseBranch}
+	} else {
+		args = []string{"checkout", "-b", branchName}
+	}
+
+	_, err := gs.runGitCommand(repoPath, args...)
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git create branch failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Created branch '%s'", branchName))), nil
+}
+
+func (gs *GitServer) HandleGitCheckout(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	branchName, ok := req.Arguments["branch_name"].(string)
+	if !ok {
+		return mcp.NewToolError("branch_name is required"), nil
+	}
+
+	_, err := gs.runGitCommand(repoPath, "checkout", branchName)
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git checkout failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Switched to branch '%s'", branchName))), nil
+}
+
+func (gs *GitServer) HandleGitShow(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	revision, ok := req.Arguments["revision"].(string)
+	if !ok {
+		return mcp.NewToolError("revision is required"), nil
+	}
+
+	output, err := gs.runGitCommand(repoPath, "show", revision)
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git show failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(output)), nil
+}
+
+func (gs *GitServer) HandleGitInit(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	repoPath, ok := req.Arguments["repo_path"].(string)
+	if !ok {
+		return mcp.NewToolError("repo_path is required"), nil
+	}
+
+	// Ensure directory exists
+	if err := os.MkdirAll(repoPath, 0755); err != nil {
+		return mcp.NewToolError(fmt.Sprintf("failed to create directory: %v", err)), nil
+	}
+
+	_, err := gs.runGitCommand(repoPath, "init")
+	if err != nil {
+		return mcp.NewToolError(fmt.Sprintf("git init failed: %v", err)), nil
+	}
+
+	return mcp.NewToolResult(mcp.NewTextContent(fmt.Sprintf("Initialized empty Git repository in %s", repoPath))), nil
+}
+
+// Helper methods
+
+func (gs *GitServer) runGitCommand(repoPath string, args ...string) (string, error) {
+	// Check if path exists
+	if _, err := os.Stat(repoPath); os.IsNotExist(err) {
+		return "", fmt.Errorf("repository path does not exist: %s", repoPath)
+	}
+
+	// Check if it's a git repository (except for init command)
+	if len(args) > 0 && args[0] != "init" {
+		gitDir := filepath.Join(repoPath, ".git")
+		if _, err := os.Stat(gitDir); os.IsNotExist(err) {
+			return "", fmt.Errorf("not a git repository: %s", repoPath)
+		}
+	}
+
+	cmd := exec.Command("git", args...)
+	cmd.Dir = repoPath
+	
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		return "", fmt.Errorf("%v: %s", err, string(output))
+	}
+
+	return strings.TrimSpace(string(output)), nil
+}
+
+func (gs *GitServer) convertToStringSlice(input interface{}) ([]string, error) {
+	switch v := input.(type) {
+	case []interface{}:
+		result := make([]string, len(v))
+		for i, item := range v {
+			str, ok := item.(string)
+			if !ok {
+				return nil, fmt.Errorf("item at index %d is not a string", i)
+			}
+			result[i] = str
+		}
+		return result, nil
+	case []string:
+		return v, nil
+	default:
+		return nil, fmt.Errorf("input is not a slice")
+	}
+}
\ No newline at end of file
cmd/git/server_test.go
@@ -0,0 +1,215 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/xlgmokha/mcp/pkg/mcp"
+)
+
+func TestGitServer_GitStatus(t *testing.T) {
+	// Setup test repo
+	tempDir, err := os.MkdirTemp("", "git-test-*")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tempDir)
+
+	// Initialize git repo
+	if err := initGitRepo(tempDir); err != nil {
+		t.Fatal(err)
+	}
+
+	server := NewGitServer()
+	
+	// Test git status
+	req := mcp.CallToolRequest{
+		Name: "git_status",
+		Arguments: map[string]interface{}{
+			"repo_path": tempDir,
+		},
+	}
+
+	result, err := server.HandleGitStatus(req)
+	if err != nil {
+		t.Fatalf("Expected no error, got %v", err)
+	}
+
+	if len(result.Content) == 0 {
+		t.Fatal("Expected content in result")
+	}
+
+	textContent, ok := result.Content[0].(mcp.TextContent)
+	if !ok {
+		t.Fatal("Expected TextContent")
+	}
+
+	if textContent.Text == "" {
+		t.Fatal("Expected non-empty status text")
+	}
+
+	// Should contain status information
+	if len(textContent.Text) < 10 {
+		t.Fatal("Expected meaningful status output")
+	}
+}
+
+func TestGitServer_GitInit(t *testing.T) {
+	// Setup temp directory
+	tempDir, err := os.MkdirTemp("", "git-init-test-*")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tempDir)
+
+	server := NewGitServer()
+	
+	// Test git init
+	req := mcp.CallToolRequest{
+		Name: "git_init",
+		Arguments: map[string]interface{}{
+			"repo_path": tempDir,
+		},
+	}
+
+	result, err := server.HandleGitInit(req)
+	if err != nil {
+		t.Fatalf("Expected no error, got %v", err)
+	}
+
+	if len(result.Content) == 0 {
+		t.Fatal("Expected content in result")
+	}
+
+	textContent, ok := result.Content[0].(mcp.TextContent)
+	if !ok {
+		t.Fatal("Expected TextContent")
+	}
+
+	// Check that .git directory was created
+	gitDir := filepath.Join(tempDir, ".git")
+	if _, err := os.Stat(gitDir); os.IsNotExist(err) {
+		t.Fatal("Expected .git directory to be created")
+	}
+
+	// Check response message
+	if !contains(textContent.Text, "Initialized") {
+		t.Fatalf("Expected init success message, got: %s", textContent.Text)
+	}
+}
+
+func TestGitServer_GitAdd(t *testing.T) {
+	// Setup test repo
+	tempDir, err := os.MkdirTemp("", "git-test-*")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tempDir)
+
+	// Initialize git repo
+	if err := initGitRepo(tempDir); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a test file
+	testFile := filepath.Join(tempDir, "test.txt")
+	if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil {
+		t.Fatal(err)
+	}
+
+	server := NewGitServer()
+	
+	// Test git add
+	req := mcp.CallToolRequest{
+		Name: "git_add",
+		Arguments: map[string]interface{}{
+			"repo_path": tempDir,
+			"files":     []interface{}{"test.txt"},
+		},
+	}
+
+	result, err := server.HandleGitAdd(req)
+	if err != nil {
+		t.Fatalf("Expected no error, got %v", err)
+	}
+
+	if len(result.Content) == 0 {
+		t.Fatal("Expected content in result")
+	}
+
+	textContent, ok := result.Content[0].(mcp.TextContent)
+	if !ok {
+		t.Fatal("Expected TextContent")
+	}
+
+	// Check response message
+	if !contains(textContent.Text, "staged") {
+		t.Fatalf("Expected staged success message, got: %s", textContent.Text)
+	}
+}
+
+func TestGitServer_ListTools(t *testing.T) {
+	server := NewGitServer()
+	tools := server.ListTools()
+
+	expectedTools := []string{
+		"git_status", "git_diff_unstaged", "git_diff_staged", "git_diff",
+		"git_commit", "git_add", "git_reset", "git_log", "git_create_branch",
+		"git_checkout", "git_show", "git_init",
+	}
+
+	if len(tools) != len(expectedTools) {
+		t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
+	}
+
+	toolNames := make(map[string]bool)
+	for _, tool := range tools {
+		toolNames[tool.Name] = true
+	}
+
+	for _, expected := range expectedTools {
+		if !toolNames[expected] {
+			t.Fatalf("Expected tool %s not found", expected)
+		}
+	}
+}
+
+// Helper functions
+func initGitRepo(dir string) error {
+	// Use actual git init command for testing
+	server := NewGitServer()
+	req := mcp.CallToolRequest{
+		Name: "git_init",
+		Arguments: map[string]interface{}{
+			"repo_path": dir,
+		},
+	}
+	
+	result, err := server.HandleGitInit(req)
+	if err != nil {
+		return err
+	}
+	
+	if result.IsError {
+		return fmt.Errorf("git init failed")
+	}
+	
+	return nil
+}
+
+func contains(s, substr string) bool {
+	return len(s) > 0 && len(substr) > 0 && 
+		   (s == substr || (len(s) >= len(substr) && 
+		   findSubstring(s, substr)))
+}
+
+func findSubstring(s, substr string) bool {
+	for i := 0; i <= len(s)-len(substr); i++ {
+		if s[i:i+len(substr)] == substr {
+			return true
+		}
+	}
+	return false
+}
\ No newline at end of file
docs/mcp-server-specifications.md
@@ -0,0 +1,831 @@
+# MCP Server Feature Specifications for Go Implementation
+
+This document provides detailed specifications for implementing MCP servers in Go based on analysis of the Python reference implementations. Each server must implement the Model Context Protocol using JSON-RPC 2.0 over stdio.
+
+## Core MCP Protocol Requirements
+
+### Base Protocol
+- **Transport**: JSON-RPC 2.0 over stdio (stdin/stdout)
+- **Connection**: Stateful client-server connection
+- **Initialization**: Capability negotiation during handshake
+- **Message Format**: All messages must follow JSON-RPC 2.0 specification
+
+### Required MCP Methods
+All servers must implement these base protocol methods:
+- `initialize` - Capability negotiation and server info
+- `initialized` - Notification that initialization is complete
+- `shutdown` - Graceful shutdown request
+- `exit` - Immediate termination notification
+
+### Server Capabilities
+Servers can implement any combination of:
+- **Tools** - Functions the AI model can execute
+- **Resources** - Context and data access
+- **Prompts** - Templated messages and workflows
+
+## 1. Git Server (`mcp-server-git`)
+
+### Dependencies
+- Git CLI or go-git library
+- File system access for repository operations
+
+### Tools
+
+#### `git_status`
+- **Description**: Shows the working tree status
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"}
+    },
+    "required": ["repo_path"]
+  }
+  ```
+- **Implementation**: Execute `git status` or use go-git Status()
+- **Output**: TextContent with repository status
+
+#### `git_diff_unstaged`
+- **Description**: Shows changes in working directory not yet staged
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"}
+    },
+    "required": ["repo_path"]
+  }
+  ```
+- **Implementation**: Execute `git diff` or use go-git diff functionality
+- **Output**: TextContent with unstaged changes
+
+#### `git_diff_staged`
+- **Description**: Shows changes that are staged for commit
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"}
+    },
+    "required": ["repo_path"]
+  }
+  ```
+- **Implementation**: Execute `git diff --cached`
+- **Output**: TextContent with staged changes
+
+#### `git_diff`
+- **Description**: Shows differences between branches or commits
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "target": {"type": "string", "description": "Target branch or commit to compare with"}
+    },
+    "required": ["repo_path", "target"]
+  }
+  ```
+- **Implementation**: Execute `git diff <target>`
+- **Output**: TextContent with diff output
+
+#### `git_commit`
+- **Description**: Records changes to the repository
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "message": {"type": "string", "description": "Commit message"}
+    },
+    "required": ["repo_path", "message"]
+  }
+  ```
+- **Implementation**: Execute `git commit -m "message"`
+- **Output**: TextContent with commit confirmation and hash
+
+#### `git_add`
+- **Description**: Adds file contents to the staging area
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "files": {"type": "array", "items": {"type": "string"}, "description": "Array of file paths to stage"}
+    },
+    "required": ["repo_path", "files"]
+  }
+  ```
+- **Implementation**: Execute `git add <files...>`
+- **Output**: TextContent with staging confirmation
+
+#### `git_reset`
+- **Description**: Unstages all staged changes
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"}
+    },
+    "required": ["repo_path"]
+  }
+  ```
+- **Implementation**: Execute `git reset`
+- **Output**: TextContent with reset confirmation
+
+#### `git_log`
+- **Description**: Shows the commit logs
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "max_count": {"type": "integer", "description": "Maximum number of commits to show", "default": 10}
+    },
+    "required": ["repo_path"]
+  }
+  ```
+- **Implementation**: Execute `git log --max-count=<n>` or use go-git log iterator
+- **Output**: TextContent with formatted commit history
+
+#### `git_create_branch`
+- **Description**: Creates a new branch from an optional base branch
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "branch_name": {"type": "string", "description": "Name of the new branch"},
+      "base_branch": {"type": "string", "description": "Starting point for the new branch", "default": null}
+    },
+    "required": ["repo_path", "branch_name"]
+  }
+  ```
+- **Implementation**: Execute `git checkout -b <branch_name> [base_branch]`
+- **Output**: TextContent with branch creation confirmation
+
+#### `git_checkout`
+- **Description**: Switches branches
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "branch_name": {"type": "string", "description": "Name of branch to checkout"}
+    },
+    "required": ["repo_path", "branch_name"]
+  }
+  ```
+- **Implementation**: Execute `git checkout <branch_name>`
+- **Output**: TextContent with checkout confirmation
+
+#### `git_show`
+- **Description**: Shows the contents of a commit
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to Git repository"},
+      "revision": {"type": "string", "description": "The revision (commit hash, branch name, tag) to show"}
+    },
+    "required": ["repo_path", "revision"]
+  }
+  ```
+- **Implementation**: Execute `git show <revision>` or use go-git commit details
+- **Output**: TextContent with commit details and diff
+
+#### `git_init`
+- **Description**: Initialize a new Git repository
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "repo_path": {"type": "string", "description": "Path to directory to initialize git repo"}
+    },
+    "required": ["repo_path"]
+  }
+  ```
+- **Implementation**: Execute `git init` or use go-git PlainInit()
+- **Output**: TextContent with initialization confirmation
+
+### Configuration
+- Command line argument: `--repository <path>` to specify default repository
+- Can also detect repositories from MCP roots capability
+
+## 2. Filesystem Server (`mcp-server-filesystem`)
+
+### Dependencies
+- File system access with configurable permissions
+- Pattern matching for file search
+- Text diff generation
+
+### Security Model
+- **Sandboxing**: Only allow operations within specified directories
+- **Validation**: All paths must be validated against allowed directories
+- **Error Handling**: Graceful handling of permission errors
+
+### Tools
+
+#### `read_file`
+- **Description**: Read complete contents of a file
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "Absolute path to the file to read"}
+    },
+    "required": ["path"]
+  }
+  ```
+- **Implementation**: Read file with UTF-8 encoding, handle various encodings
+- **Output**: TextContent with file contents
+- **Security**: Validate path is within allowed directories
+
+#### `read_multiple_files`
+- **Description**: Read multiple files simultaneously
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "paths": {"type": "array", "items": {"type": "string"}, "description": "Array of file paths to read"}
+    },
+    "required": ["paths"]
+  }
+  ```
+- **Implementation**: Read multiple files, continue on individual failures
+- **Output**: TextContent with results for each file
+- **Security**: Validate all paths are within allowed directories
+
+#### `write_file`
+- **Description**: Create new file or overwrite existing
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "File location"},
+      "content": {"type": "string", "description": "File content"}
+    },
+    "required": ["path", "content"]
+  }
+  ```
+- **Implementation**: Write content to file with UTF-8 encoding
+- **Output**: TextContent with write confirmation
+- **Security**: Validate path is within allowed directories
+
+#### `edit_file`
+- **Description**: Make selective edits using pattern matching
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "File to edit"},
+      "edits": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "oldText": {"type": "string", "description": "Text to search for"},
+            "newText": {"type": "string", "description": "Text to replace with"}
+          },
+          "required": ["oldText", "newText"]
+        }
+      },
+      "dryRun": {"type": "boolean", "description": "Preview changes without applying", "default": false}
+    },
+    "required": ["path", "edits"]
+  }
+  ```
+- **Implementation**: 
+  - Line-based and multi-line content matching
+  - Whitespace normalization with indentation preservation
+  - Multiple simultaneous edits with correct positioning
+  - Git-style diff output
+- **Output**: TextContent with diff information or edit confirmation
+- **Security**: Validate path is within allowed directories
+
+#### `create_directory`
+- **Description**: Create new directory or ensure it exists
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "Directory path to create"}
+    },
+    "required": ["path"]
+  }
+  ```
+- **Implementation**: Create directory and parent directories if needed
+- **Output**: TextContent with creation confirmation
+- **Security**: Validate path is within allowed directories
+
+#### `list_directory`
+- **Description**: List directory contents with type indicators
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "Directory path to list"}
+    },
+    "required": ["path"]
+  }
+  ```
+- **Implementation**: List files and directories with [FILE] or [DIR] prefixes
+- **Output**: TextContent with directory listing
+- **Security**: Validate path is within allowed directories
+
+#### `move_file`
+- **Description**: Move or rename files and directories
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "source": {"type": "string", "description": "Source path"},
+      "destination": {"type": "string", "description": "Destination path"}
+    },
+    "required": ["source", "destination"]
+  }
+  ```
+- **Implementation**: Move/rename operation, fail if destination exists
+- **Output**: TextContent with move confirmation
+- **Security**: Validate both paths are within allowed directories
+
+#### `search_files`
+- **Description**: Recursively search for files/directories
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "Starting directory"},
+      "pattern": {"type": "string", "description": "Search pattern"},
+      "excludePatterns": {"type": "array", "items": {"type": "string"}, "description": "Exclude patterns", "default": []}
+    },
+    "required": ["path", "pattern"]
+  }
+  ```
+- **Implementation**: Case-insensitive recursive search with glob patterns
+- **Output**: TextContent with matching file paths
+- **Security**: Validate path is within allowed directories
+
+#### `get_file_info`
+- **Description**: Get detailed file/directory metadata
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "path": {"type": "string", "description": "File/directory path"}
+    },
+    "required": ["path"]
+  }
+  ```
+- **Implementation**: Return size, timestamps, permissions, type
+- **Output**: TextContent with file metadata
+- **Security**: Validate path is within allowed directories
+
+#### `list_allowed_directories`
+- **Description**: List all directories the server is allowed to access
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {},
+    "required": []
+  }
+  ```
+- **Implementation**: Return configured allowed directories
+- **Output**: TextContent with allowed directory list
+
+### Configuration
+- Command line arguments: List of allowed directories
+- Example: `mcp-server-filesystem /home/user/documents /home/user/projects`
+
+## 3. Fetch Server (`mcp-server-fetch`)
+
+### Dependencies
+- HTTP client for web requests
+- HTML to Markdown conversion
+- robots.txt parsing
+- URL validation and parsing
+
+### Security Considerations
+- **robots.txt Compliance**: Check robots.txt before autonomous fetching
+- **User Agent**: Different user agents for autonomous vs manual requests
+- **Proxy Support**: Optional proxy configuration
+- **Content Filtering**: Handle various content types appropriately
+
+### Tools
+
+#### `fetch`
+- **Description**: Fetches a URL from the internet and extracts its contents as markdown
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "url": {"type": "string", "format": "uri", "description": "URL to fetch"},
+      "max_length": {"type": "integer", "description": "Maximum number of characters to return", "default": 5000, "minimum": 1, "maximum": 999999},
+      "start_index": {"type": "integer", "description": "Start content from this character index", "default": 0, "minimum": 0},
+      "raw": {"type": "boolean", "description": "Get raw content without markdown conversion", "default": false}
+    },
+    "required": ["url"]
+  }
+  ```
+- **Implementation**:
+  - Check robots.txt compliance for autonomous requests
+  - Fetch URL with appropriate user agent
+  - Convert HTML to markdown unless raw requested
+  - Handle content truncation and pagination
+  - Support content type detection
+- **Output**: TextContent with fetched content
+- **Error Handling**: Detailed error messages for robots.txt violations, HTTP errors
+
+### Prompts
+
+#### `fetch`
+- **Description**: Fetch a URL and extract its contents as markdown
+- **Arguments**:
+  ```json
+  [
+    {
+      "name": "url",
+      "description": "URL to fetch",
+      "required": true
+    }
+  ]
+  ```
+- **Implementation**: Manual fetch without robots.txt restrictions
+
+### Configuration Options
+- `--custom-user-agent`: Custom User-Agent string
+- `--ignore-robots-txt`: Ignore robots.txt restrictions
+- `--proxy-url`: Proxy URL for requests
+
+## 4. Memory Server (`mcp-server-memory`)
+
+### Dependencies
+- JSON file storage for persistence
+- Graph data structure for entities and relations
+- Text search capabilities
+
+### Data Model
+
+#### Entity
+```json
+{
+  "name": "string (unique identifier)",
+  "entityType": "string (e.g., 'person', 'organization', 'event')",
+  "observations": ["array of strings"]
+}
+```
+
+#### Relation
+```json
+{
+  "from": "string (source entity name)",
+  "to": "string (target entity name)",
+  "relationType": "string (relationship type in active voice)"
+}
+```
+
+### Tools
+
+#### `create_entities`
+- **Description**: Create multiple new entities in the knowledge graph
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "entities": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "name": {"type": "string", "description": "Entity identifier"},
+            "entityType": {"type": "string", "description": "Type classification"},
+            "observations": {"type": "array", "items": {"type": "string"}, "description": "Associated observations"}
+          },
+          "required": ["name", "entityType", "observations"]
+        }
+      }
+    },
+    "required": ["entities"]
+  }
+  ```
+- **Implementation**: Add entities to graph, ignore duplicates by name
+- **Output**: TextContent with creation results
+
+#### `create_relations`
+- **Description**: Create multiple new relations between entities
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "relations": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "from": {"type": "string", "description": "Source entity name"},
+            "to": {"type": "string", "description": "Target entity name"},
+            "relationType": {"type": "string", "description": "Relationship type"}
+          },
+          "required": ["from", "to", "relationType"]
+        }
+      }
+    },
+    "required": ["relations"]
+  }
+  ```
+- **Implementation**: Add relations to graph, skip duplicates
+- **Output**: TextContent with creation results
+
+#### `add_observations`
+- **Description**: Add new observations to existing entities
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "observations": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "entityName": {"type": "string", "description": "Target entity"},
+            "contents": {"type": "array", "items": {"type": "string"}, "description": "New observations to add"}
+          },
+          "required": ["entityName", "contents"]
+        }
+      }
+    },
+    "required": ["observations"]
+  }
+  ```
+- **Implementation**: Add observations to existing entities
+- **Output**: TextContent with added observations per entity
+- **Error**: Fail if entity doesn't exist
+
+#### `delete_entities`
+- **Description**: Remove entities and their relations
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "entityNames": {"type": "array", "items": {"type": "string"}, "description": "Entity names to delete"}
+    },
+    "required": ["entityNames"]
+  }
+  ```
+- **Implementation**: Remove entities and cascade delete relations
+- **Output**: TextContent with deletion results
+
+#### `delete_observations`
+- **Description**: Remove specific observations from entities
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "deletions": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "entityName": {"type": "string", "description": "Target entity"},
+            "observations": {"type": "array", "items": {"type": "string"}, "description": "Observations to remove"}
+          },
+          "required": ["entityName", "observations"]
+        }
+      }
+    },
+    "required": ["deletions"]
+  }
+  ```
+- **Implementation**: Remove specific observations from entities
+- **Output**: TextContent with deletion results
+
+#### `delete_relations`
+- **Description**: Remove specific relations from the graph
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "relations": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "from": {"type": "string", "description": "Source entity name"},
+            "to": {"type": "string", "description": "Target entity name"},
+            "relationType": {"type": "string", "description": "Relationship type"}
+          },
+          "required": ["from", "to", "relationType"]
+        }
+      }
+    },
+    "required": ["relations"]
+  }
+  ```
+- **Implementation**: Remove specific relations
+- **Output**: TextContent with deletion results
+
+#### `read_graph`
+- **Description**: Read the entire knowledge graph
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {},
+    "required": []
+  }
+  ```
+- **Implementation**: Return complete graph structure
+- **Output**: TextContent with full graph data
+
+#### `search_nodes`
+- **Description**: Search for nodes based on query
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "query": {"type": "string", "description": "Search query"}
+    },
+    "required": ["query"]
+  }
+  ```
+- **Implementation**: Search entity names, types, and observation content
+- **Output**: TextContent with matching entities and relations
+
+#### `open_nodes`
+- **Description**: Retrieve specific nodes by name
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "names": {"type": "array", "items": {"type": "string"}, "description": "Entity names to retrieve"}
+    },
+    "required": ["names"]
+  }
+  ```
+- **Implementation**: Return requested entities and their relations
+- **Output**: TextContent with requested nodes
+
+### Configuration
+- Environment variable: `MEMORY_FILE_PATH` for custom storage location
+- Default: `memory.json` in server directory
+
+## 5. Sequential Thinking Server (`mcp-server-sequential-thinking`)
+
+### Dependencies
+- State management for thought sequences
+- Dynamic thought process tracking
+
+### Tools
+
+#### `sequentialthinking`
+- **Description**: Dynamic and reflective problem-solving through thought sequences
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "thought": {"type": "string", "description": "Your current thinking step"},
+      "nextThoughtNeeded": {"type": "boolean", "description": "Whether another thought step is needed"},
+      "thoughtNumber": {"type": "integer", "description": "Current thought number", "minimum": 1},
+      "totalThoughts": {"type": "integer", "description": "Estimated total thoughts needed", "minimum": 1},
+      "isRevision": {"type": "boolean", "description": "Whether this revises previous thinking"},
+      "revisesThought": {"type": "integer", "description": "Which thought is being reconsidered", "minimum": 1},
+      "branchFromThought": {"type": "integer", "description": "Branching point thought number", "minimum": 1},
+      "branchId": {"type": "string", "description": "Branch identifier"},
+      "needsMoreThoughts": {"type": "boolean", "description": "If more thoughts are needed"}
+    },
+    "required": ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"]
+  }
+  ```
+- **Implementation**:
+  - Track thought sequences and branching
+  - Support revision and refinement of previous thoughts
+  - Maintain context across thought progression
+  - Generate and verify solution hypotheses
+- **Output**: TextContent with thought processing results
+
+## 6. Time Server (`mcp-server-time`)
+
+### Dependencies
+- Timezone database (IANA timezones)
+- System timezone detection
+- Time parsing and formatting
+
+### Tools
+
+#### `get_current_time`
+- **Description**: Get current time in a specific timezone
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "timezone": {"type": "string", "description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London')"}
+    },
+    "required": ["timezone"]
+  }
+  ```
+- **Implementation**: Get current time in specified timezone with DST information
+- **Output**: JSON TextContent with timezone, datetime (ISO format), is_dst
+
+#### `convert_time`
+- **Description**: Convert time between timezones
+- **Input Schema**:
+  ```json
+  {
+    "type": "object",
+    "properties": {
+      "source_timezone": {"type": "string", "description": "Source IANA timezone name"},
+      "time": {"type": "string", "description": "Time in 24-hour format (HH:MM)"},
+      "target_timezone": {"type": "string", "description": "Target IANA timezone name"}
+    },
+    "required": ["source_timezone", "time", "target_timezone"]
+  }
+  ```
+- **Implementation**: Convert time between timezones, calculate time difference
+- **Output**: JSON TextContent with source and target time information plus difference
+
+### Configuration
+- Command line argument: `--local-timezone` to override system timezone detection
+
+## Common Go Implementation Requirements
+
+### JSON-RPC 2.0 Implementation
+- Message framing over stdio
+- Request/response correlation with IDs
+- Notification handling (no response expected)
+- Error response formatting
+
+### Shared Types
+```go
+type TextContent struct {
+    Type string `json:"type"`
+    Text string `json:"text"`
+}
+
+type Tool struct {
+    Name        string      `json:"name"`
+    Description string      `json:"description"`
+    InputSchema interface{} `json:"inputSchema"`
+}
+
+type ServerInfo struct {
+    Name    string `json:"name"`
+    Version string `json:"version"`
+}
+```
+
+### Error Handling
+- Standard JSON-RPC error codes
+- Descriptive error messages
+- Graceful degradation
+- Input validation with clear error messages
+
+### Logging and Debugging
+- Structured logging support
+- Debug mode for development
+- Request/response tracing capability
+
+### Configuration
+- Command line argument parsing
+- Environment variable support
+- Configuration file support where appropriate
+- Sensible defaults
+
+### Testing Requirements
+- Unit tests for all tools
+- Integration tests with MCP clients
+- Error condition testing
+- Mock external dependencies where needed
+
+This specification provides the foundation for implementing full-featured MCP servers in Go with complete compatibility with the Python reference implementations.
\ No newline at end of file
pkg/mcp/server.go
@@ -0,0 +1,340 @@
+package mcp
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"fmt"
+	"log"
+	"os"
+	"sync"
+)
+
+// Server represents an MCP server
+type Server struct {
+	name         string
+	version      string
+	capabilities ServerCapabilities
+	
+	// Handler functions
+	toolHandlers     map[string]ToolHandler
+	promptHandlers   map[string]PromptHandler
+	resourceHandlers map[string]ResourceHandler
+	
+	// Lifecycle handlers
+	initializeHandler func(InitializeRequest) (InitializeResult, error)
+	shutdownHandler   func() error
+	
+	mu sync.RWMutex
+}
+
+// Handler types
+type ToolHandler func(CallToolRequest) (CallToolResult, error)
+type PromptHandler func(GetPromptRequest) (GetPromptResult, error) 
+type ResourceHandler func(ReadResourceRequest) (ReadResourceResult, error)
+
+// NewServer creates a new MCP server
+func NewServer(name, version string) *Server {
+	return &Server{
+		name:             name,
+		version:          version,
+		toolHandlers:     make(map[string]ToolHandler),
+		promptHandlers:   make(map[string]PromptHandler),
+		resourceHandlers: make(map[string]ResourceHandler),
+		capabilities: ServerCapabilities{
+			Tools:     &ToolsCapability{},
+			Prompts:   &PromptsCapability{},
+			Resources: &ResourcesCapability{},
+			Logging:   &LoggingCapability{},
+		},
+	}
+}
+
+// RegisterTool registers a tool handler
+func (s *Server) RegisterTool(name string, handler ToolHandler) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.toolHandlers[name] = handler
+}
+
+// RegisterPrompt registers a prompt handler
+func (s *Server) RegisterPrompt(name string, handler PromptHandler) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.promptHandlers[name] = handler
+}
+
+// RegisterResource registers a resource handler
+func (s *Server) RegisterResource(uri string, handler ResourceHandler) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	s.resourceHandlers[uri] = handler
+}
+
+// SetInitializeHandler sets the initialize handler
+func (s *Server) SetInitializeHandler(handler func(InitializeRequest) (InitializeResult, error)) {
+	s.initializeHandler = handler
+}
+
+// SetShutdownHandler sets the shutdown handler
+func (s *Server) SetShutdownHandler(handler func() error) {
+	s.shutdownHandler = handler
+}
+
+// ListTools returns all registered tools
+func (s *Server) ListTools() []Tool {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+	
+	tools := make([]Tool, 0, len(s.toolHandlers))
+	for name := range s.toolHandlers {
+		// Tools will be defined by individual servers
+		// This is a placeholder - real implementation would store tool definitions
+		tools = append(tools, Tool{
+			Name:        name,
+			Description: "Tool: " + name,
+			InputSchema: map[string]interface{}{
+				"type":       "object",
+				"properties": map[string]interface{}{},
+			},
+		})
+	}
+	
+	return tools
+}
+
+// Run starts the server and handles JSON-RPC over stdio
+func (s *Server) Run(ctx context.Context) error {
+	scanner := bufio.NewScanner(os.Stdin)
+	encoder := json.NewEncoder(os.Stdout)
+	
+	for scanner.Scan() {
+		line := scanner.Bytes()
+		
+		var req JSONRPCRequest
+		if err := json.Unmarshal(line, &req); err != nil {
+			s.sendError(encoder, nil, ParseError, "Parse error")
+			continue
+		}
+		
+		response := s.handleRequest(req)
+		if err := encoder.Encode(response); err != nil {
+			log.Printf("Error encoding response: %v", err)
+		}
+		
+		// Check for shutdown
+		if req.Method == "shutdown" {
+			break
+		}
+	}
+	
+	if err := scanner.Err(); err != nil {
+		return fmt.Errorf("scanner error: %w", err)
+	}
+	
+	return nil
+}
+
+// handleRequest processes a JSON-RPC request
+func (s *Server) handleRequest(req JSONRPCRequest) JSONRPCResponse {
+	switch req.Method {
+	case "initialize":
+		return s.handleInitialize(req)
+	case "initialized":
+		return s.handleInitialized(req)
+	case "shutdown":
+		return s.handleShutdown(req)
+	case "tools/list":
+		return s.handleListTools(req)
+	case "tools/call":
+		return s.handleCallTool(req)
+	case "prompts/list":
+		return s.handleListPrompts(req)
+	case "prompts/get":
+		return s.handleGetPrompt(req)
+	case "resources/list":
+		return s.handleListResources(req)
+	case "resources/read":
+		return s.handleReadResource(req)
+	default:
+		return s.createErrorResponse(req.ID, MethodNotFound, "Method not found")
+	}
+}
+
+func (s *Server) handleInitialize(req JSONRPCRequest) JSONRPCResponse {
+	var initReq InitializeRequest
+	if err := json.Unmarshal(req.Params, &initReq); err != nil {
+		return s.createErrorResponse(req.ID, InvalidParams, "Invalid params")
+	}
+	
+	result := InitializeResult{
+		ProtocolVersion: "2024-11-05",
+		Capabilities:    s.capabilities,
+		ServerInfo: ServerInfo{
+			Name:    s.name,
+			Version: s.version,
+		},
+	}
+	
+	if s.initializeHandler != nil {
+		if customResult, err := s.initializeHandler(initReq); err == nil {
+			result = customResult
+		}
+	}
+	
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) handleInitialized(req JSONRPCRequest) JSONRPCResponse {
+	// Initialized notification - no response needed
+	return JSONRPCResponse{
+		JSONRPC: "2.0",
+		ID:      req.ID,
+	}
+}
+
+func (s *Server) handleShutdown(req JSONRPCRequest) JSONRPCResponse {
+	if s.shutdownHandler != nil {
+		s.shutdownHandler()
+	}
+	
+	return s.createSuccessResponse(req.ID, nil)
+}
+
+func (s *Server) handleListTools(req JSONRPCRequest) JSONRPCResponse {
+	tools := s.ListTools()
+	result := ListToolsResult{Tools: tools}
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) handleCallTool(req JSONRPCRequest) JSONRPCResponse {
+	var callReq CallToolRequest
+	if err := json.Unmarshal(req.Params, &callReq); err != nil {
+		return s.createErrorResponse(req.ID, InvalidParams, "Invalid params")
+	}
+	
+	s.mu.RLock()
+	handler, exists := s.toolHandlers[callReq.Name]
+	s.mu.RUnlock()
+	
+	if !exists {
+		return s.createErrorResponse(req.ID, MethodNotFound, "Tool not found")
+	}
+	
+	result, err := handler(callReq)
+	if err != nil {
+		return s.createErrorResponse(req.ID, InternalError, err.Error())
+	}
+	
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) handleListPrompts(req JSONRPCRequest) JSONRPCResponse {
+	// Return empty prompts list for now
+	result := ListPromptsResult{Prompts: []Prompt{}}
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) handleGetPrompt(req JSONRPCRequest) JSONRPCResponse {
+	var promptReq GetPromptRequest
+	if err := json.Unmarshal(req.Params, &promptReq); err != nil {
+		return s.createErrorResponse(req.ID, InvalidParams, "Invalid params")
+	}
+	
+	s.mu.RLock()
+	handler, exists := s.promptHandlers[promptReq.Name]
+	s.mu.RUnlock()
+	
+	if !exists {
+		return s.createErrorResponse(req.ID, MethodNotFound, "Prompt not found")
+	}
+	
+	result, err := handler(promptReq)
+	if err != nil {
+		return s.createErrorResponse(req.ID, InternalError, err.Error())
+	}
+	
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) handleListResources(req JSONRPCRequest) JSONRPCResponse {
+	// Return empty resources list for now
+	result := ListResourcesResult{Resources: []Resource{}}
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) handleReadResource(req JSONRPCRequest) JSONRPCResponse {
+	var readReq ReadResourceRequest
+	if err := json.Unmarshal(req.Params, &readReq); err != nil {
+		return s.createErrorResponse(req.ID, InvalidParams, "Invalid params")
+	}
+	
+	s.mu.RLock()
+	handler, exists := s.resourceHandlers[readReq.URI]
+	s.mu.RUnlock()
+	
+	if !exists {
+		return s.createErrorResponse(req.ID, MethodNotFound, "Resource not found")
+	}
+	
+	result, err := handler(readReq)
+	if err != nil {
+		return s.createErrorResponse(req.ID, InternalError, err.Error())
+	}
+	
+	return s.createSuccessResponse(req.ID, result)
+}
+
+func (s *Server) createSuccessResponse(id *int, result interface{}) JSONRPCResponse {
+	var resultBytes *json.RawMessage
+	if result != nil {
+		bytes, _ := json.Marshal(result)
+		rawMsg := json.RawMessage(bytes)
+		resultBytes = &rawMsg
+	}
+	
+	return JSONRPCResponse{
+		JSONRPC: "2.0",
+		ID:      id,
+		Result:  resultBytes,
+	}
+}
+
+func (s *Server) createErrorResponse(id *int, code int, message string) JSONRPCResponse {
+	return JSONRPCResponse{
+		JSONRPC: "2.0",
+		ID:      id,
+		Error: &JSONRPCError{
+			Code:    code,
+			Message: message,
+		},
+	}
+}
+
+func (s *Server) sendError(encoder *json.Encoder, id *int, code int, message string) {
+	response := s.createErrorResponse(id, code, message)
+	encoder.Encode(response)
+}
+
+// Helper function to create text content
+func NewTextContent(text string) TextContent {
+	return TextContent{
+		Type: "text",
+		Text: text,
+	}
+}
+
+// Helper function to create tool result
+func NewToolResult(content ...Content) CallToolResult {
+	return CallToolResult{
+		Content: content,
+	}
+}
+
+// Helper function to create tool error result
+func NewToolError(message string) CallToolResult {
+	return CallToolResult{
+		Content: []Content{NewTextContent("Error: " + message)},
+		IsError: true,
+	}
+}
\ No newline at end of file
pkg/mcp/types.go
@@ -0,0 +1,228 @@
+package mcp
+
+import (
+	"encoding/json"
+)
+
+// JSON-RPC 2.0 types
+type JSONRPCRequest struct {
+	JSONRPC string          `json:"jsonrpc"`
+	ID      *int            `json:"id,omitempty"`
+	Method  string          `json:"method"`
+	Params  json.RawMessage `json:"params,omitempty"`
+}
+
+type JSONRPCResponse struct {
+	JSONRPC string           `json:"jsonrpc"`
+	ID      *int             `json:"id,omitempty"`
+	Result  *json.RawMessage `json:"result,omitempty"`
+	Error   *JSONRPCError    `json:"error,omitempty"`
+}
+
+type JSONRPCError struct {
+	Code    int         `json:"code"`
+	Message string      `json:"message"`
+	Data    interface{} `json:"data,omitempty"`
+}
+
+// JSON-RPC error codes
+const (
+	ParseError     = -32700
+	InvalidRequest = -32600
+	MethodNotFound = -32601
+	InvalidParams  = -32602
+	InternalError  = -32603
+)
+
+// MCP Protocol types
+type InitializeRequest struct {
+	ProtocolVersion string       `json:"protocolVersion"`
+	Capabilities    Capabilities `json:"capabilities"`
+	ClientInfo      ClientInfo   `json:"clientInfo"`
+}
+
+type Capabilities struct {
+	Roots       *RootsCapability       `json:"roots,omitempty"`
+	Sampling    *SamplingCapability    `json:"sampling,omitempty"`
+	Experimental map[string]interface{} `json:"experimental,omitempty"`
+}
+
+type RootsCapability struct {
+	ListChanged bool `json:"listChanged,omitempty"`
+}
+
+type SamplingCapability struct{}
+
+type ClientInfo struct {
+	Name    string `json:"name"`
+	Version string `json:"version"`
+}
+
+type InitializeResult struct {
+	ProtocolVersion   string            `json:"protocolVersion"`
+	Capabilities      ServerCapabilities `json:"capabilities"`
+	ServerInfo        ServerInfo        `json:"serverInfo"`
+	Instructions      string            `json:"instructions,omitempty"`
+}
+
+type ServerCapabilities struct {
+	Logging   *LoggingCapability   `json:"logging,omitempty"`
+	Prompts   *PromptsCapability   `json:"prompts,omitempty"`
+	Resources *ResourcesCapability `json:"resources,omitempty"`
+	Tools     *ToolsCapability     `json:"tools,omitempty"`
+}
+
+type LoggingCapability struct{}
+type PromptsCapability struct {
+	ListChanged bool `json:"listChanged,omitempty"`
+}
+type ResourcesCapability struct {
+	Subscribe   bool `json:"subscribe,omitempty"`
+	ListChanged bool `json:"listChanged,omitempty"`
+}
+type ToolsCapability struct {
+	ListChanged bool `json:"listChanged,omitempty"`
+}
+
+type ServerInfo struct {
+	Name    string `json:"name"`
+	Version string `json:"version"`
+}
+
+// Tool types
+type Tool struct {
+	Name        string      `json:"name"`
+	Description string      `json:"description"`
+	InputSchema interface{} `json:"inputSchema"`
+}
+
+type ListToolsResult struct {
+	Tools []Tool `json:"tools"`
+}
+
+type CallToolRequest struct {
+	Name      string                 `json:"name"`
+	Arguments map[string]interface{} `json:"arguments,omitempty"`
+}
+
+type CallToolResult struct {
+	Content []Content `json:"content"`
+	IsError bool      `json:"isError,omitempty"`
+}
+
+// Content types
+type Content interface {
+	GetType() string
+}
+
+type TextContent struct {
+	Type string `json:"type"`
+	Text string `json:"text"`
+}
+
+func (t TextContent) GetType() string {
+	return t.Type
+}
+
+type ImageContent struct {
+	Type     string `json:"type"`
+	Data     string `json:"data"`
+	MimeType string `json:"mimeType"`
+}
+
+func (i ImageContent) GetType() string {
+	return i.Type
+}
+
+// Prompt types
+type Prompt struct {
+	Name        string           `json:"name"`
+	Description string           `json:"description"`
+	Arguments   []PromptArgument `json:"arguments,omitempty"`
+}
+
+type PromptArgument struct {
+	Name        string `json:"name"`
+	Description string `json:"description"`
+	Required    bool   `json:"required,omitempty"`
+}
+
+type ListPromptsResult struct {
+	Prompts []Prompt `json:"prompts"`
+}
+
+type GetPromptRequest struct {
+	Name      string                 `json:"name"`
+	Arguments map[string]interface{} `json:"arguments,omitempty"`
+}
+
+type PromptMessage struct {
+	Role    string  `json:"role"`
+	Content Content `json:"content"`
+}
+
+type GetPromptResult struct {
+	Description string          `json:"description,omitempty"`
+	Messages    []PromptMessage `json:"messages"`
+}
+
+// Resource types  
+type Resource struct {
+	URI         string `json:"uri"`
+	Name        string `json:"name"`
+	Description string `json:"description,omitempty"`
+	MimeType    string `json:"mimeType,omitempty"`
+}
+
+type ListResourcesResult struct {
+	Resources []Resource `json:"resources"`
+}
+
+type ReadResourceRequest struct {
+	URI string `json:"uri"`
+}
+
+type ReadResourceResult struct {
+	Contents []Content `json:"contents"`
+}
+
+// Root types
+type Root struct {
+	URI  string `json:"uri"`
+	Name string `json:"name"`
+}
+
+type ListRootsResult struct {
+	Roots []Root `json:"roots"`
+}
+
+// Logging types
+type LoggingLevel string
+
+const (
+	LoggingLevelDebug   LoggingLevel = "debug"
+	LoggingLevelInfo    LoggingLevel = "info"
+	LoggingLevelNotice  LoggingLevel = "notice"
+	LoggingLevelWarning LoggingLevel = "warning"
+	LoggingLevelError   LoggingLevel = "error"
+	LoggingLevelCrit    LoggingLevel = "crit"
+	LoggingLevelAlert   LoggingLevel = "alert"
+	LoggingLevelEmerg   LoggingLevel = "emerg"
+)
+
+type SetLoggingLevelRequest struct {
+	Level LoggingLevel `json:"level"`
+}
+
+// Notification types
+type LoggingMessageNotification struct {
+	Level  LoggingLevel `json:"level"`
+	Data   interface{}  `json:"data"`
+	Logger string       `json:"logger,omitempty"`
+}
+
+type ProgressNotification struct {
+	ProgressToken string  `json:"progressToken"`
+	Progress      float64 `json:"progress"`
+	Total         float64 `json:"total,omitempty"`
+}
\ No newline at end of file
.gitignore
@@ -0,0 +1,35 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+# Go workspace file
+go.work
+
+# Build output
+bin/
+dist/
+
+# Memory file for development
+memory.json
+
+# IDE files
+.idea/
+.vscode/
+*.swp
+*.swo
+
+# OS files
+.DS_Store
+Thumbs.db
\ No newline at end of file
go.mod
@@ -0,0 +1,3 @@
+module github.com/xlgmokha/mcp
+
+go 1.24.0