Commit fe64f8b
Changed files (22)
cmd
pkg
fetch
filesystem
memory
thinking
time
cmd/fetch/main.go
@@ -4,21 +4,21 @@ import (
"context"
"log"
- "github.com/xlgmokha/mcp/pkg/fetchserver"
+ "github.com/xlgmokha/mcp/pkg/fetch"
"github.com/xlgmokha/mcp/pkg/mcp"
)
func main() {
- server := fetchserver.New()
-
+ server := fetch.New()
+
// 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/filesystem/main.go
@@ -5,7 +5,7 @@ import (
"log"
"os"
- "github.com/xlgmokha/mcp/pkg/fsserver"
+ "github.com/xlgmokha/mcp/pkg/filesystem"
"github.com/xlgmokha/mcp/pkg/mcp"
)
@@ -16,16 +16,16 @@ func main() {
}
allowedDirs := os.Args[1:]
- server := fsserver.New(allowedDirs)
-
+ server := filesystem.New(allowedDirs)
+
// 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/main.go
@@ -4,21 +4,21 @@ import (
"context"
"log"
- "github.com/xlgmokha/mcp/pkg/gitserver"
+ "github.com/xlgmokha/mcp/pkg/git"
"github.com/xlgmokha/mcp/pkg/mcp"
)
func main() {
- server := gitserver.New()
-
+ server := git.New()
+
// 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/memory/main.go
@@ -7,7 +7,7 @@ import (
"path/filepath"
"github.com/xlgmokha/mcp/pkg/mcp"
- "github.com/xlgmokha/mcp/pkg/memoryserver"
+ "github.com/xlgmokha/mcp/pkg/memory"
)
func main() {
@@ -21,16 +21,16 @@ func main() {
memoryFile = filepath.Join(homeDir, ".mcp_memory.json")
}
- server := memoryserver.New(memoryFile)
-
+ server := memory.New(memoryFile)
+
// 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/sequential-thinking/main.go
@@ -5,20 +5,20 @@ import (
"log"
"github.com/xlgmokha/mcp/pkg/mcp"
- "github.com/xlgmokha/mcp/pkg/thinkingserver"
+ "github.com/xlgmokha/mcp/pkg/thinking"
)
func main() {
- server := thinkingserver.New()
-
+ server := thinking.New()
+
// 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/time/main.go
@@ -5,20 +5,20 @@ import (
"log"
"github.com/xlgmokha/mcp/pkg/mcp"
- "github.com/xlgmokha/mcp/pkg/timeserver"
+ "github.com/xlgmokha/mcp/pkg/time"
)
func main() {
- server := timeserver.New()
-
+ server := time.New()
+
// 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
+}
pkg/fetchserver/server.go → pkg/fetch/server.go
@@ -1,4 +1,4 @@
-package fetchserver
+package fetch
import (
"encoding/json"
@@ -76,7 +76,7 @@ func (fs *Server) ListTools() []mcp.Tool {
"default": 5000,
},
"start_index": map[string]interface{}{
- "type": "integer",
+ "type": "integer",
"description": "Start reading content from this character index. Defaults to 0",
"minimum": 0,
"default": 0,
@@ -192,7 +192,7 @@ func (fs *Server) fetchContent(urlStr string, maxLength, startIndex int, raw boo
// Get content type
contentType := resp.Header.Get("Content-Type")
-
+
// Process content
var content string
if raw || !isHTMLContent(string(body), contentType) {
@@ -246,7 +246,7 @@ func isHTMLContent(content, contentType string) bool {
if len(prefix) > 100 {
prefix = prefix[:100]
}
-
+
return strings.Contains(strings.ToLower(prefix), "<html")
}
@@ -358,11 +358,11 @@ func (fs *Server) stripHTMLTags(content string) string {
// Remove HTML tags using regex
re := regexp.MustCompile(`<[^>]*>`)
text := re.ReplaceAllString(content, " ")
-
+
// Clean up whitespace
re = regexp.MustCompile(`\s+`)
text = re.ReplaceAllString(text, " ")
-
+
return strings.TrimSpace(text)
}
@@ -370,13 +370,13 @@ func (fs *Server) cleanMarkdown(content string) string {
// Remove excessive newlines
re := regexp.MustCompile(`\n{3,}`)
content = re.ReplaceAllString(content, "\n\n")
-
+
// Remove excessive spaces
re = regexp.MustCompile(` {2,}`)
content = re.ReplaceAllString(content, " ")
-
+
// Trim whitespace
content = strings.TrimSpace(content)
-
+
return content
-}
\ No newline at end of file
+}
pkg/fetchserver/server_test.go → pkg/fetch/server_test.go
@@ -1,4 +1,4 @@
-package fetchserver
+package fetch
import (
"net/http"
@@ -148,7 +148,7 @@ func TestServer_FetchWithMaxLength(t *testing.T) {
if !contains(textContent.Text, "\"length\": 100") {
t.Fatalf("Expected content length to be exactly 100 chars, got: %s", textContent.Text)
}
-
+
if !contains(textContent.Text, "\"truncated\": true") {
t.Fatalf("Expected truncated flag to be true, got: %s", textContent.Text)
}
@@ -341,4 +341,4 @@ func TestServer_ListTools(t *testing.T) {
// Helper functions
func contains(s, substr string) bool {
return strings.Contains(s, substr)
-}
\ No newline at end of file
+}
pkg/fsserver/server.go → pkg/filesystem/server.go
@@ -1,4 +1,4 @@
-package fsserver
+package filesystem
import (
"fmt"
@@ -20,7 +20,7 @@ type Server struct {
// New creates a new Filesystem MCP server
func New(allowedDirs []string) *Server {
server := mcp.NewServer("secure-filesystem-server", "0.2.0")
-
+
// Normalize and validate allowed directories
normalizedDirs := make([]string, len(allowedDirs))
for i, dir := range allowedDirs {
@@ -30,15 +30,15 @@ func New(allowedDirs []string) *Server {
}
normalizedDirs[i] = filepath.Clean(absPath)
}
-
+
fsServer := &Server{
Server: server,
allowedDirectories: normalizedDirs,
}
-
+
// Register all filesystem tools
fsServer.registerTools()
-
+
return fsServer
}
@@ -539,7 +539,7 @@ func (fs *Server) HandleListDirectoryWithSizes(req mcp.CallToolRequest) (mcp.Cal
totalFiles++
totalSize += entry.size
}
-
+
formatted = append(formatted, fmt.Sprintf("%s %-30s %s", prefix, entry.name, sizeStr))
}
@@ -672,7 +672,7 @@ func (fs *Server) HandleListAllowedDirectories(req mcp.CallToolRequest) (mcp.Cal
func (fs *Server) validatePath(requestedPath string) (string, error) {
expandedPath := expandHome(requestedPath)
var absolute string
-
+
if filepath.IsAbs(expandedPath) {
absolute = filepath.Clean(expandedPath)
} else {
@@ -886,7 +886,7 @@ func (fs *Server) buildDirectoryTree(rootPath string) (string, error) {
var result []TreeEntry
for _, entry := range entries {
entryPath := filepath.Join(currentPath, entry.Name())
-
+
// Validate each path
if _, err := fs.validatePath(entryPath); err != nil {
continue // Skip unauthorized paths
@@ -956,4 +956,4 @@ func (fs *Server) searchFiles(rootPath, pattern string, excludePatterns []string
})
return results, err
-}
\ No newline at end of file
+}
pkg/fsserver/server_test.go → pkg/filesystem/server_test.go
@@ -1,4 +1,4 @@
-package fsserver
+package filesystem
import (
"os"
@@ -139,7 +139,7 @@ func TestFilesystemServer_ListDirectory(t *testing.T) {
// Create test files and directories
testFile := filepath.Join(tempDir, "test.txt")
testDir := filepath.Join(tempDir, "subdir")
-
+
if err := os.WriteFile(testFile, []byte("test"), 0644); err != nil {
t.Fatal(err)
}
@@ -293,9 +293,9 @@ func TestFilesystemServer_EditFile(t *testing.T) {
// Helper functions
func contains(s, substr string) bool {
- return len(s) > 0 && len(substr) > 0 &&
- (s == substr || (len(s) >= len(substr) &&
- findSubstring(s, substr)))
+ return len(s) > 0 && len(substr) > 0 &&
+ (s == substr || (len(s) >= len(substr) &&
+ findSubstring(s, substr)))
}
func findSubstring(s, substr string) bool {
@@ -305,4 +305,4 @@ func findSubstring(s, substr string) bool {
}
}
return false
-}
\ No newline at end of file
+}
pkg/gitserver/server.go → pkg/git/server.go
@@ -1,4 +1,4 @@
-package gitserver
+package git
import (
"fmt"
@@ -19,14 +19,14 @@ type Server struct {
// New creates a new Git MCP server
func New() *Server {
server := mcp.NewServer("mcp-git", "1.0.0")
-
+
gitServer := &Server{
Server: server,
}
-
+
// Register all git tools
gitServer.registerTools()
-
+
return gitServer
}
@@ -501,7 +501,7 @@ func (gs *Server) runGitCommand(repoPath string, args ...string) (string, error)
cmd := exec.Command("git", args...)
cmd.Dir = repoPath
-
+
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v: %s", err, string(output))
@@ -527,4 +527,4 @@ func (gs *Server) convertToStringSlice(input interface{}) ([]string, error) {
default:
return nil, fmt.Errorf("input is not a slice")
}
-}
\ No newline at end of file
+}
pkg/gitserver/server_test.go → pkg/git/server_test.go
@@ -1,4 +1,4 @@
-package gitserver
+package git
import (
"fmt"
@@ -23,7 +23,7 @@ func TestGitServer_GitStatus(t *testing.T) {
}
server := New()
-
+
// Test git status
req := mcp.CallToolRequest{
Name: "git_status",
@@ -65,7 +65,7 @@ func TestGitServer_GitInit(t *testing.T) {
defer os.RemoveAll(tempDir)
server := New()
-
+
// Test git init
req := mcp.CallToolRequest{
Name: "git_init",
@@ -120,7 +120,7 @@ func TestGitServer_GitAdd(t *testing.T) {
}
server := New()
-
+
// Test git add
req := mcp.CallToolRequest{
Name: "git_add",
@@ -186,23 +186,23 @@ func initGitRepo(dir string) error {
"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)))
+ return len(s) > 0 && len(substr) > 0 &&
+ (s == substr || (len(s) >= len(substr) &&
+ findSubstring(s, substr)))
}
func findSubstring(s, substr string) bool {
@@ -212,4 +212,4 @@ func findSubstring(s, substr string) bool {
}
}
return false
-}
\ No newline at end of file
+}
pkg/mcp/server.go
@@ -15,22 +15,22 @@ 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 PromptHandler func(GetPromptRequest) (GetPromptResult, error)
type ResourceHandler func(ReadResourceRequest) (ReadResourceResult, error)
// NewServer creates a new MCP server
@@ -85,7 +85,7 @@ func (s *Server) SetShutdownHandler(handler func() error) {
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
@@ -99,7 +99,7 @@ func (s *Server) ListTools() []Tool {
},
})
}
-
+
return tools
}
@@ -107,31 +107,31 @@ func (s *Server) ListTools() []Tool {
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
}
@@ -166,7 +166,7 @@ func (s *Server) handleInitialize(req JSONRPCRequest) JSONRPCResponse {
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,
@@ -175,13 +175,13 @@ func (s *Server) handleInitialize(req JSONRPCRequest) JSONRPCResponse {
Version: s.version,
},
}
-
+
if s.initializeHandler != nil {
if customResult, err := s.initializeHandler(initReq); err == nil {
result = customResult
}
}
-
+
return s.createSuccessResponse(req.ID, result)
}
@@ -197,7 +197,7 @@ func (s *Server) handleShutdown(req JSONRPCRequest) JSONRPCResponse {
if s.shutdownHandler != nil {
s.shutdownHandler()
}
-
+
return s.createSuccessResponse(req.ID, nil)
}
@@ -212,20 +212,20 @@ func (s *Server) handleCallTool(req JSONRPCRequest) JSONRPCResponse {
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)
}
@@ -240,20 +240,20 @@ func (s *Server) handleGetPrompt(req JSONRPCRequest) JSONRPCResponse {
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)
}
@@ -268,20 +268,20 @@ func (s *Server) handleReadResource(req JSONRPCRequest) JSONRPCResponse {
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)
}
@@ -292,7 +292,7 @@ func (s *Server) createSuccessResponse(id *int, result interface{}) JSONRPCRespo
rawMsg := json.RawMessage(bytes)
resultBytes = &rawMsg
}
-
+
return JSONRPCResponse{
JSONRPC: "2.0",
ID: id,
@@ -337,4 +337,4 @@ func NewToolError(message string) CallToolResult {
Content: []Content{NewTextContent("Error: " + message)},
IsError: true,
}
-}
\ No newline at end of file
+}
pkg/mcp/types.go
@@ -42,8 +42,8 @@ type InitializeRequest struct {
}
type Capabilities struct {
- Roots *RootsCapability `json:"roots,omitempty"`
- Sampling *SamplingCapability `json:"sampling,omitempty"`
+ Roots *RootsCapability `json:"roots,omitempty"`
+ Sampling *SamplingCapability `json:"sampling,omitempty"`
Experimental map[string]interface{} `json:"experimental,omitempty"`
}
@@ -59,10 +59,10 @@ type ClientInfo struct {
}
type InitializeResult struct {
- ProtocolVersion string `json:"protocolVersion"`
- Capabilities ServerCapabilities `json:"capabilities"`
- ServerInfo ServerInfo `json:"serverInfo"`
- Instructions string `json:"instructions,omitempty"`
+ ProtocolVersion string `json:"protocolVersion"`
+ Capabilities ServerCapabilities `json:"capabilities"`
+ ServerInfo ServerInfo `json:"serverInfo"`
+ Instructions string `json:"instructions,omitempty"`
}
type ServerCapabilities struct {
@@ -166,7 +166,7 @@ type GetPromptResult struct {
Messages []PromptMessage `json:"messages"`
}
-// Resource types
+// Resource types
type Resource struct {
URI string `json:"uri"`
Name string `json:"name"`
@@ -225,4 +225,4 @@ type ProgressNotification struct {
ProgressToken string `json:"progressToken"`
Progress float64 `json:"progress"`
Total float64 `json:"total,omitempty"`
-}
\ No newline at end of file
+}
pkg/memoryserver/server.go → pkg/memory/server.go
@@ -1,4 +1,4 @@
-package memoryserver
+package memory
import (
"encoding/json"
@@ -801,4 +801,4 @@ func (ms *Server) saveGraph() error {
}
return os.WriteFile(ms.memoryFile, data, 0644)
-}
\ No newline at end of file
+}
pkg/memoryserver/server_test.go → pkg/memory/server_test.go
@@ -1,4 +1,4 @@
-package memoryserver
+package memory
import (
"path/filepath"
@@ -26,7 +26,7 @@ func TestMemoryServer_CreateEntities(t *testing.T) {
},
map[string]interface{}{
"name": "Anthropic",
- "entityType": "organization",
+ "entityType": "organization",
"observations": []interface{}{"AI safety company"},
},
},
@@ -552,4 +552,4 @@ func TestMemoryServer_Persistence(t *testing.T) {
// Helper functions
func contains(s, substr string) bool {
return strings.Contains(s, substr)
-}
\ No newline at end of file
+}
pkg/thinkingserver/server.go → pkg/thinking/server.go
@@ -1,4 +1,4 @@
-package thinkingserver
+package thinking
import (
"encoding/json"
@@ -16,21 +16,21 @@ type Server struct {
// ThinkingSession represents a thinking session with sequential thoughts
type ThinkingSession struct {
- Thoughts []Thought `json:"thoughts"`
- CurrentThought int `json:"current_thought"`
- TotalThoughts int `json:"total_thoughts"`
- Status string `json:"status"` // "active", "completed"
+ Thoughts []Thought `json:"thoughts"`
+ CurrentThought int `json:"current_thought"`
+ TotalThoughts int `json:"total_thoughts"`
+ Status string `json:"status"` // "active", "completed"
}
// Thought represents a single thought in the sequence
type Thought struct {
- Number int `json:"number"`
- Content string `json:"content"`
- IsRevision bool `json:"is_revision,omitempty"`
- RevisesThought *int `json:"revises_thought,omitempty"`
- BranchFromThought *int `json:"branch_from_thought,omitempty"`
- BranchID string `json:"branch_id,omitempty"`
- NeedsMoreThoughts bool `json:"needs_more_thoughts,omitempty"`
+ Number int `json:"number"`
+ Content string `json:"content"`
+ IsRevision bool `json:"is_revision,omitempty"`
+ RevisesThought *int `json:"revises_thought,omitempty"`
+ BranchFromThought *int `json:"branch_from_thought,omitempty"`
+ BranchID string `json:"branch_id,omitempty"`
+ NeedsMoreThoughts bool `json:"needs_more_thoughts,omitempty"`
}
// ThinkingResponse represents the response structure
@@ -241,18 +241,18 @@ func (sts *Server) HandleSequentialThinking(req mcp.CallToolRequest) (mcp.CallTo
// Add context information
var contextInfo []string
-
+
if isRevision && revisesThought != nil {
contextInfo = append(contextInfo, fmt.Sprintf("Revising thought %d", *revisesThought))
}
-
+
if branchFromThought != nil {
contextInfo = append(contextInfo, fmt.Sprintf("Branching from thought %d", *branchFromThought))
if branchID != "" {
contextInfo = append(contextInfo, fmt.Sprintf("Branch: %s", branchID))
}
}
-
+
if needsMoreThoughts {
contextInfo = append(contextInfo, "Requesting additional thoughts beyond initial estimate")
}
@@ -268,13 +268,13 @@ func (sts *Server) HandleSequentialThinking(req mcp.CallToolRequest) (mcp.CallTo
func (sts *Server) extractSolution(finalThought string) string {
// Simple heuristic to extract a solution from the final thought
content := strings.ToLower(finalThought)
-
+
// Look for solution indicators (with and without colons)
solutionKeywords := []string{
"solution:",
"solution is",
"answer:",
- "answer is",
+ "answer is",
"conclusion:",
"final answer:",
"result:",
@@ -283,7 +283,7 @@ func (sts *Server) extractSolution(finalThought string) string {
"therefore the",
"in conclusion:",
}
-
+
for _, keyword := range solutionKeywords {
if idx := strings.Index(content, keyword); idx != -1 {
// Extract text after the keyword
@@ -302,7 +302,7 @@ func (sts *Server) extractSolution(finalThought string) string {
}
}
}
-
+
// If no explicit solution found, return the last sentence or a portion
sentences := strings.Split(strings.TrimSpace(finalThought), ".")
if len(sentences) > 0 {
@@ -314,57 +314,57 @@ func (sts *Server) extractSolution(finalThought string) string {
return lastSentence
}
}
-
+
return "Solution extracted from final thought"
}
func (sts *Server) formatThinkingResult(response ThinkingResponse, thought Thought, contextInfo []string) string {
var result strings.Builder
-
+
// Header
- result.WriteString(fmt.Sprintf("🧠 Sequential Thinking - Thought %d/%d\n",
+ result.WriteString(fmt.Sprintf("🧠 Sequential Thinking - Thought %d/%d\n",
response.ThoughtNumber, response.TotalThoughts))
result.WriteString("═══════════════════════════════════════\n\n")
-
+
// Context information if any
if len(contextInfo) > 0 {
result.WriteString("📝 Context: " + strings.Join(contextInfo, ", ") + "\n\n")
}
-
+
// Main thought content
result.WriteString("💭 Current Thought:\n")
result.WriteString(response.Thought + "\n\n")
-
+
// Progress indicator
progressBar := sts.createProgressBar(response.ThoughtNumber, response.TotalThoughts)
result.WriteString("📊 Progress: " + progressBar + "\n\n")
-
+
// Status
statusEmoji := "🔄"
if response.Status == "completed" {
statusEmoji = "✅"
}
result.WriteString(fmt.Sprintf("%s Status: %s\n", statusEmoji, response.Status))
-
+
if response.NextThoughtNeeded {
result.WriteString("⏭️ Next thought needed\n")
} else {
result.WriteString("🏁 Thinking sequence complete\n")
}
-
+
// Solution if available
if response.Solution != "" {
result.WriteString("\n🎯 Extracted Solution:\n")
result.WriteString(response.Solution + "\n")
}
-
+
// JSON data for programmatic access
result.WriteString("\n📋 Structured Data:\n")
jsonData, _ := json.MarshalIndent(response, "", " ")
result.WriteString("```json\n")
result.WriteString(string(jsonData))
result.WriteString("\n```")
-
+
return result.String()
}
@@ -372,15 +372,15 @@ func (sts *Server) createProgressBar(current, total int) string {
if total <= 0 {
return "[████████████████████] 100%"
}
-
+
percentage := float64(current) / float64(total) * 100
if percentage > 100 {
percentage = 100
}
-
+
barLength := 20
filledLength := int(percentage / 100 * float64(barLength))
-
+
bar := "["
for i := 0; i < barLength; i++ {
if i < filledLength {
@@ -390,6 +390,6 @@ func (sts *Server) createProgressBar(current, total int) string {
}
}
bar += "] " + strconv.Itoa(int(percentage)) + "%"
-
+
return bar
-}
\ No newline at end of file
+}
pkg/thinkingserver/server_test.go → pkg/thinking/server_test.go
@@ -1,4 +1,4 @@
-package thinkingserver
+package thinking
import (
"strings"
@@ -13,10 +13,10 @@ func TestServer_BasicThinking(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "Let me analyze this problem step by step.",
- "nextThoughtNeeded": true,
- "thoughtNumber": 1,
- "totalThoughts": 3,
+ "thought": "Let me analyze this problem step by step.",
+ "nextThoughtNeeded": true,
+ "thoughtNumber": 1,
+ "totalThoughts": 3,
},
}
@@ -57,10 +57,10 @@ func TestServer_CompletedThinking(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "Therefore, the solution is 42.",
- "nextThoughtNeeded": false,
- "thoughtNumber": 3,
- "totalThoughts": 3,
+ "thought": "Therefore, the solution is 42.",
+ "nextThoughtNeeded": false,
+ "thoughtNumber": 3,
+ "totalThoughts": 3,
},
}
@@ -101,12 +101,12 @@ func TestServer_RevisionThinking(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "Actually, let me reconsider my previous analysis.",
- "nextThoughtNeeded": true,
- "thoughtNumber": 4,
- "totalThoughts": 5,
- "isRevision": true,
- "revisesThought": 2,
+ "thought": "Actually, let me reconsider my previous analysis.",
+ "nextThoughtNeeded": true,
+ "thoughtNumber": 4,
+ "totalThoughts": 5,
+ "isRevision": true,
+ "revisesThought": 2,
},
}
@@ -142,12 +142,12 @@ func TestServer_BranchThinking(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "Let me explore an alternative approach.",
- "nextThoughtNeeded": true,
- "thoughtNumber": 6,
- "totalThoughts": 8,
- "branchFromThought": 3,
- "branchId": "alternative_path",
+ "thought": "Let me explore an alternative approach.",
+ "nextThoughtNeeded": true,
+ "thoughtNumber": 6,
+ "totalThoughts": 8,
+ "branchFromThought": 3,
+ "branchId": "alternative_path",
},
}
@@ -182,11 +182,11 @@ func TestServer_NeedsMoreThoughts(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "I realize I need more steps to solve this.",
- "nextThoughtNeeded": true,
- "thoughtNumber": 5,
- "totalThoughts": 5,
- "needsMoreThoughts": true,
+ "thought": "I realize I need more steps to solve this.",
+ "nextThoughtNeeded": true,
+ "thoughtNumber": 5,
+ "totalThoughts": 5,
+ "needsMoreThoughts": true,
},
}
@@ -252,10 +252,10 @@ func TestServer_InvalidParams(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "Test thought",
- "nextThoughtNeeded": true,
- "thoughtNumber": 0, // Invalid: must be >= 1
- "totalThoughts": 3,
+ "thought": "Test thought",
+ "nextThoughtNeeded": true,
+ "thoughtNumber": 0, // Invalid: must be >= 1
+ "totalThoughts": 3,
},
}
@@ -285,10 +285,10 @@ func TestServer_SolutionExtraction(t *testing.T) {
req := mcp.CallToolRequest{
Name: "sequentialthinking",
Arguments: map[string]interface{}{
- "thought": "After careful analysis, the answer is: The optimal solution involves using a binary search algorithm.",
- "nextThoughtNeeded": false,
- "thoughtNumber": 3,
- "totalThoughts": 3,
+ "thought": "After careful analysis, the answer is: The optimal solution involves using a binary search algorithm.",
+ "nextThoughtNeeded": false,
+ "thoughtNumber": 3,
+ "totalThoughts": 3,
},
}
@@ -371,4 +371,4 @@ func TestServer_ProgressBar(t *testing.T) {
// Helper functions
func contains(s, substr string) bool {
return strings.Contains(s, substr)
-}
\ No newline at end of file
+}
pkg/timeserver/server.go → pkg/time/server.go
@@ -1,4 +1,4 @@
-package timeserver
+package time
import (
"encoding/json"
@@ -33,18 +33,18 @@ type TimeConversionResult struct {
// New creates a new Time MCP server
func New() *Server {
server := mcp.NewServer("mcp-time", "1.0.0")
-
+
// Get local timezone
localTZ := getLocalTimezone()
-
+
timeServer := &Server{
Server: server,
localTimezone: localTZ,
}
-
+
// Register all time tools
timeServer.registerTools()
-
+
return timeServer
}
@@ -155,7 +155,7 @@ func (ts *Server) getCurrentTime(timezone string) (*TimeResult, error) {
}
currentTime := time.Now().In(loc)
-
+
return &TimeResult{
Timezone: timezone,
Datetime: currentTime.Format("2006-01-02T15:04:05-07:00"),
@@ -193,7 +193,7 @@ func (ts *Server) convertTime(sourceTimezone, timeStr, targetTimezone string) (*
// Use current date in source timezone
now := time.Now().In(sourceLoc)
sourceTime := time.Date(now.Year(), now.Month(), now.Day(), hour, minute, 0, 0, sourceLoc)
-
+
// Convert to target timezone
targetTime := sourceTime.In(targetLoc)
@@ -240,14 +240,14 @@ func getLocalTimezone() string {
func isDST(t time.Time) bool {
// Check if time is in daylight saving time
_, offset := t.Zone()
-
+
// Create time in January (standard time) and July (potentially DST)
jan := time.Date(t.Year(), 1, 1, 12, 0, 0, 0, t.Location())
jul := time.Date(t.Year(), 7, 1, 12, 0, 0, 0, t.Location())
-
+
_, janOffset := jan.Zone()
_, julOffset := jul.Zone()
-
+
// If offsets are different, the timezone observes DST
if janOffset != julOffset {
// We're in DST if current offset matches the "more positive" offset
@@ -258,7 +258,7 @@ func isDST(t time.Time) bool {
return offset == janOffset
}
}
-
+
// No DST observed in this timezone
return false
}
@@ -280,4 +280,4 @@ func formatTimeDifference(hours float64) string {
formatted = strings.TrimRight(formatted, ".")
return formatted + "h"
}
-}
\ No newline at end of file
+}
pkg/timeserver/server_test.go → pkg/time/server_test.go
@@ -1,4 +1,4 @@
-package timeserver
+package time
import (
"strings"
@@ -203,4 +203,4 @@ func TestServer_ConvertTimeWithDST(t *testing.T) {
// Helper functions
func contains(s, substr string) bool {
return strings.Contains(s, substr)
-}
\ No newline at end of file
+}
go.mod
@@ -2,10 +2,4 @@ module github.com/xlgmokha/mcp
go 1.24.0
-require github.com/spf13/cobra v1.9.1
-
-require (
- github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/spf13/pflag v1.0.6 // indirect
- golang.org/x/net v0.41.0 // indirect
-)
+require golang.org/x/net v0.41.0
go.sum
@@ -1,12 +1,2 @@
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=