diff --git a/claude/code/.claude-plugin/plugin.json b/claude/code/.claude-plugin/plugin.json index caf6b50..1bac76f 100644 --- a/claude/code/.claude-plugin/plugin.json +++ b/claude/code/.claude-plugin/plugin.json @@ -18,5 +18,55 @@ "go", "php", "laravel" - ] + ], + "mcp": { + "server": "go run google/mcp/main.go", + "tools": [ + { + "name": "core_go_test", + "description": "Run Go tests", + "parameters": { + "type": "object", + "properties": { + "filter": { + "type": "string" + }, + "coverage": { + "type": "boolean", + "default": false + } + } + } + }, + { + "name": "core_dev_health", + "description": "Check monorepo status", + "parameters": { + "type": "object", + "properties": {} + } + }, + { + "name": "core_dev_commit", + "description": "Commit changes across repos", + "parameters": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "repos": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "message" + ] + } + } + ] + } } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fe97fce --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module core-agent + +go 1.24 diff --git a/google/mcp/README.md b/google/mcp/README.md new file mode 100644 index 0000000..e776021 --- /dev/null +++ b/google/mcp/README.md @@ -0,0 +1,67 @@ +# Core CLI MCP Server + +This directory contains an MCP server that exposes the core CLI commands as tools for AI agents. + +## Tools + +### `core_go_test` + +Run Go tests. + +**Parameters:** + +- `filter` (string, optional): Filter tests by name. +- `coverage` (boolean, optional): Enable code coverage. Defaults to `false`. + +**Example:** + +```json +{ + "tool": "core_go_test", + "parameters": { + "filter": "TestMyFunction", + "coverage": true + } +} +``` + +### `core_dev_health` + +Check the health of the monorepo. + +**Parameters:** + +None. + +**Example:** + +```json +{ + "tool": "core_dev_health", + "parameters": {} +} +``` + +### `core_dev_commit` + +Commit changes across repositories. + +**Parameters:** + +- `message` (string, required): The commit message. +- `repos` (array of strings, optional): A list of repositories to commit to. + +**Example:** + +```json +{ + "tool": "core_dev_commit", + "parameters": { + "message": "feat: Implement new feature", + "repos": [ + "core-agent", + "another-repo" + ] + } +} +``` diff --git a/google/mcp/main.go b/google/mcp/main.go new file mode 100644 index 0000000..3ac576b --- /dev/null +++ b/google/mcp/main.go @@ -0,0 +1,131 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os/exec" + "strings" +) + +type GoTestRequest struct { + Filter string `json:"filter,omitempty"` + Coverage bool `json:"coverage,omitempty"` +} + +type GoTestResponse struct { + Output string `json:"output"` + Error string `json:"error,omitempty"` +} + +type DevHealthResponse struct { + Output string `json:"output"` + Error string `json:"error,omitempty"` +} + +type DevCommitRequest struct { + Message string `json:"message"` + Repos []string `json:"repos,omitempty"` +} + +type DevCommitResponse struct { + Output string `json:"output"` + Error string `json:"error,omitempty"` +} + +func goTestHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) + return + } + var req GoTestRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + args := []string{"go", "test"} + if req.Filter != "" { + args = append(args, "-run", req.Filter) + } + if req.Coverage { + args = append(args, "-cover") + } + + cmd := exec.Command("core", args...) + output, err := cmd.CombinedOutput() + + resp := GoTestResponse{ + Output: string(output), + } + if err != nil { + resp.Error = err.Error() + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(resp) +} + +func devHealthHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) + return + } + cmd := exec.Command("core", "dev", "health") + output, err := cmd.CombinedOutput() + + resp := DevHealthResponse{ + Output: string(output), + } + if err != nil { + resp.Error = err.Error() + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(resp) +} + +func devCommitHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) + return + } + var req DevCommitRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + args := []string{"dev", "commit", "-m", req.Message} + if len(req.Repos) > 0 { + args = append(args, "--repos", strings.Join(req.Repos, ",")) + } + + cmd := exec.Command("core", args...) + output, err := cmd.CombinedOutput() + + resp := DevCommitResponse{ + Output: string(output), + } + if err != nil { + resp.Error = err.Error() + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(resp) +} + +func main() { + http.HandleFunc("/core_go_test", goTestHandler) + http.HandleFunc("/core_dev_health", devHealthHandler) + http.HandleFunc("/core_dev_commit", devCommitHandler) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "MCP Server is running") + }) + + log.Println("Starting MCP server on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + log.Fatalf("could not start server: %s\n", err) + } +} diff --git a/google/mcp/main_test.go b/google/mcp/main_test.go new file mode 100644 index 0000000..6921d43 --- /dev/null +++ b/google/mcp/main_test.go @@ -0,0 +1,112 @@ +package main + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" +) + +func TestMain(m *testing.M) { + // Get the absolute path to the testdata directory + wd, err := os.Getwd() + if err != nil { + panic(err) + } + testdataPath := filepath.Join(wd, "testdata") + + // Add the absolute path to the PATH + os.Setenv("PATH", testdataPath+":"+os.Getenv("PATH")) + m.Run() +} +func TestGoTestHandler(t *testing.T) { + reqBody := GoTestRequest{ + Filter: "TestMyFunction", + Coverage: true, + } + body, _ := json.Marshal(reqBody) + + req, err := http.NewRequest("POST", "/core_go_test", bytes.NewBuffer(body)) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(goTestHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + var resp GoTestResponse + if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil { + t.Fatalf("could not decode response: %v", err) + } + + if resp.Error != "" { + t.Errorf("handler returned an unexpected error: %v", resp.Error) + } +} + +func TestDevHealthHandler(t *testing.T) { + req, err := http.NewRequest("POST", "/core_dev_health", nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(devHealthHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + var resp DevHealthResponse + if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil { + t.Fatalf("could not decode response: %v", err) + } + + if resp.Error != "" { + t.Errorf("handler returned an unexpected error: %v", resp.Error) + } +} + +func TestDevCommitHandler(t *testing.T) { + reqBody := DevCommitRequest{ + Message: "test commit", + } + body, _ := json.Marshal(reqBody) + + req, err := http.NewRequest("POST", "/core_dev_commit", bytes.NewBuffer(body)) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(devCommitHandler) + + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + var resp DevCommitResponse + if err := json.NewDecoder(rr.Body).Decode(&resp); err != nil { + t.Fatalf("could not decode response: %v", err) + } + + if resp.Error != "" { + t.Errorf("handler returned an unexpected error: %v", resp.Error) + } +} diff --git a/google/mcp/testdata/core b/google/mcp/testdata/core new file mode 100755 index 0000000..06bd986 --- /dev/null +++ b/google/mcp/testdata/core @@ -0,0 +1,2 @@ +#!/bin/bash +exit 0