From 840706d05d8ae07dc3ca594d3fa06054f7499573 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 4 Nov 2025 19:01:11 +0000 Subject: [PATCH 1/3] refactor: remove build from ci --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce5b049..222b889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,11 +71,3 @@ jobs: coverage.out coverage.html retention-days: 1 - - build: - name: Build - needs: [lint, test] - uses: ./.github/workflows/build.yml - with: - version: 'ci-build' - upload-artifacts: false From d6eb72dd9f01a4768a27303ff728099640d0020f Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 4 Nov 2025 19:12:21 +0000 Subject: [PATCH 2/3] feat: implement MCP initialize protocol for Claude Code integration - Add handleInitialize for MCP handshake - Add handleToolsList to return available tools - Add handleToolsCall to route tool invocations - Fix connection failure with Claude Code MCP client --- internal/mcp/server.go | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/internal/mcp/server.go b/internal/mcp/server.go index 0b2391d..3588b02 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -122,6 +122,15 @@ func (s *Server) handleHTTPRequest(w http.ResponseWriter, r *http.Request) { var rpcErr *RPCError switch req.Method { + case "initialize": + result, rpcErr = s.handleInitialize(req.Params) + case "initialized": + // Notification - no response needed, but we'll send empty result + result = nil + case "tools/list": + result, rpcErr = s.handleToolsList(req.Params) + case "tools/call": + result, rpcErr = s.handleToolsCall(req.Params) case "query_conventions": result, rpcErr = s.handleQueryConventions(req.Params) case "validate_code": @@ -188,6 +197,15 @@ func (s *Server) handleRequests(in io.Reader, out io.Writer) error { var rpcErr *RPCError switch req.Method { + case "initialize": + result, rpcErr = s.handleInitialize(req.Params) + case "initialized": + // Notification - no response needed, but we'll send empty result + result = nil + case "tools/list": + result, rpcErr = s.handleToolsList(req.Params) + case "tools/call": + result, rpcErr = s.handleToolsCall(req.Params) case "query_conventions": result, rpcErr = s.handleQueryConventions(req.Params) case "validate_code": @@ -418,3 +436,99 @@ func containsAny(haystack, needles []string) bool { } return false } + +// handleInitialize handles MCP initialize request. +// This is the first request from the client to establish protocol version and capabilities. +func (s *Server) handleInitialize(params map[string]interface{}) (interface{}, *RPCError) { + return map[string]interface{}{ + "protocolVersion": "2024-11-05", + "capabilities": map[string]interface{}{ + "tools": map[string]interface{}{}, + }, + "serverInfo": map[string]interface{}{ + "name": "symphony", + "version": "1.0.0", + }, + }, nil +} + +// handleToolsList handles tools/list request. +// Returns the list of available tools that clients can call. +func (s *Server) handleToolsList(params map[string]interface{}) (interface{}, *RPCError) { + tools := []map[string]interface{}{ + { + "name": "query_conventions", + "description": "Query conventions for given context (category, files, languages)", + "inputSchema": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "category": map[string]interface{}{ + "type": "string", + "description": "Filter by category (naming, formatting, security, etc.)", + }, + "files": map[string]interface{}{ + "type": "array", + "items": map[string]string{"type": "string"}, + "description": "File paths to check conventions for", + }, + "languages": map[string]interface{}{ + "type": "array", + "items": map[string]string{"type": "string"}, + "description": "Programming languages to filter by", + }, + }, + }, + }, + { + "name": "validate_code", + "description": "Validate code compliance with conventions", + "inputSchema": map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "files": map[string]interface{}{ + "type": "array", + "items": map[string]string{"type": "string"}, + "description": "File paths to validate", + }, + "role": map[string]interface{}{ + "type": "string", + "description": "RBAC role for validation", + }, + }, + }, + }, + } + + return map[string]interface{}{ + "tools": tools, + }, nil +} + +// handleToolsCall handles tools/call request. +// This routes tool calls to the appropriate handler based on tool name. +func (s *Server) handleToolsCall(params map[string]interface{}) (interface{}, *RPCError) { + toolName, ok := params["name"].(string) + if !ok { + return nil, &RPCError{ + Code: -32602, + Message: "tool name is required", + } + } + + arguments, ok := params["arguments"].(map[string]interface{}) + if !ok { + arguments = make(map[string]interface{}) + } + + switch toolName { + case "query_conventions": + return s.handleQueryConventions(arguments) + case "validate_code": + return s.handleValidateCode(arguments) + default: + return nil, &RPCError{ + Code: -32601, + Message: fmt.Sprintf("unknown tool: %s", toolName), + } + } +} From 8641205d4ce3300a2c49884ab66a669371ba0f79 Mon Sep 17 00:00:00 2001 From: ikjeong Date: Tue, 4 Nov 2025 19:13:33 +0000 Subject: [PATCH 3/3] chore: bump version to 0.1.1 --- npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/package.json b/npm/package.json index 4782abc..5f52300 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "@dev-symphony/sym", - "version": "0.1.0", + "version": "0.1.1", "description": "Symphony - LLM-friendly convention linter for AI coding assistants", "keywords": [ "mcp",