Commit 5d6ca21
cmd/sequential-thinking/main.go
@@ -21,11 +21,15 @@ USAGE:
OPTIONS:
--help Show this help message
+ --session-file <path> File to persist thinking sessions (optional)
EXAMPLE USAGE:
# Start the sequential thinking server
mcp-sequential-thinking
+ # Start with session persistence
+ mcp-sequential-thinking --session-file sessions.json
+
# Test with MCP protocol
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "sequentialthinking", "arguments": {"task": "Analyze the problem step by step"}}}' | mcp-sequential-thinking
@@ -42,6 +46,7 @@ For detailed documentation, see: cmd/sequential-thinking/README.md
func main() {
// Parse command line flags
var help = flag.Bool("help", false, "Show help message")
+ var sessionFile = flag.String("session-file", "", "File to persist thinking sessions")
flag.Parse()
if *help {
@@ -49,7 +54,7 @@ func main() {
return
}
- server := thinking.New()
+ server := thinking.NewWithPersistence(*sessionFile)
ctx := context.Background()
if err := server.Run(ctx); err != nil {
pkg/thinking/server.go
@@ -3,8 +3,11 @@ package thinking
import (
"encoding/json"
"fmt"
+ "os"
"strconv"
"strings"
+ "sync"
+ "time"
"github.com/xlgmokha/mcp/pkg/mcp"
)
@@ -12,14 +15,32 @@ import (
// Server implements the Sequential Thinking MCP server
type Server struct {
*mcp.Server
+ sessions map[string]*ThinkingSession
+ branches map[string]*Branch
+ mu sync.RWMutex
+ persistFile string
}
// ThinkingSession represents a thinking session with sequential thoughts
type ThinkingSession struct {
+ ID string `json:"id"`
Thoughts []Thought `json:"thoughts"`
CurrentThought int `json:"current_thought"`
TotalThoughts int `json:"total_thoughts"`
Status string `json:"status"` // "active", "completed"
+ CreatedAt time.Time `json:"created_at"`
+ LastActivity time.Time `json:"last_activity"`
+ ActiveBranches []string `json:"active_branches"`
+}
+
+// Branch represents a reasoning branch with its own thought sequence
+type Branch struct {
+ ID string `json:"id"`
+ SessionID string `json:"session_id"`
+ FromThought int `json:"from_thought"`
+ Thoughts []Thought `json:"thoughts"`
+ CreatedAt time.Time `json:"created_at"`
+ LastActivity time.Time `json:"last_activity"`
}
// Thought represents a single thought in the sequence
@@ -35,20 +56,37 @@ type Thought struct {
// ThinkingResponse represents the response structure
type ThinkingResponse struct {
- Thought string `json:"thought"`
- ThoughtNumber int `json:"thought_number"`
- TotalThoughts int `json:"total_thoughts"`
- NextThoughtNeeded bool `json:"next_thought_needed"`
- Status string `json:"status"`
- Solution string `json:"solution,omitempty"`
+ Thought string `json:"thought"`
+ ThoughtNumber int `json:"thought_number"`
+ TotalThoughts int `json:"total_thoughts"`
+ NextThoughtNeeded bool `json:"next_thought_needed"`
+ Status string `json:"status"`
+ Solution string `json:"solution,omitempty"`
+ SessionID string `json:"session_id"`
+ ThoughtHistorySize int `json:"thought_history_size"`
+ ActiveBranches []string `json:"active_branches"`
+ BranchContext string `json:"branch_context,omitempty"`
}
// New creates a new Sequential Thinking MCP server
func New() *Server {
+ return NewWithPersistence("")
+}
+
+// NewWithPersistence creates a new Sequential Thinking MCP server with optional persistence
+func NewWithPersistence(persistFile string) *Server {
server := mcp.NewServer("mcp-sequential-thinking", "1.0.0")
thinkingServer := &Server{
- Server: server,
+ Server: server,
+ sessions: make(map[string]*ThinkingSession),
+ branches: make(map[string]*Branch),
+ persistFile: persistFile,
+ }
+
+ // Load existing sessions if persistence file is provided
+ if persistFile != "" {
+ thinkingServer.loadSessions()
}
// Register all sequential thinking tools
@@ -60,6 +98,10 @@ func New() *Server {
// registerTools registers all Sequential Thinking tools with the server
func (sts *Server) registerTools() {
sts.RegisterTool("sequentialthinking", sts.HandleSequentialThinking)
+ sts.RegisterTool("get_session_history", sts.HandleGetSessionHistory)
+ sts.RegisterTool("list_sessions", sts.HandleListSessions)
+ sts.RegisterTool("get_branch_history", sts.HandleGetBranchHistory)
+ sts.RegisterTool("clear_session", sts.HandleClearSession)
}
// ListTools returns all available Sequential Thinking tools
@@ -113,157 +155,450 @@ func (sts *Server) ListTools() []mcp.Tool {
"description": "If more thoughts are needed",
"default": false,
},
+ "sessionId": map[string]interface{}{
+ "type": "string",
+ "description": "Session ID for thought continuity (optional, auto-generated if not provided)",
+ },
},
"required": []string{"thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"},
},
},
+ {
+ Name: "get_session_history",
+ Description: "Get the complete thought history for a thinking session",
+ InputSchema: map[string]interface{}{
+ "type": "object",
+ "properties": map[string]interface{}{
+ "sessionId": map[string]interface{}{
+ "type": "string",
+ "description": "Session ID to get history for",
+ },
+ },
+ "required": []string{"sessionId"},
+ },
+ },
+ {
+ Name: "list_sessions",
+ Description: "List all active thinking sessions",
+ InputSchema: map[string]interface{}{
+ "type": "object",
+ "properties": map[string]interface{}{},
+ },
+ },
+ {
+ Name: "get_branch_history",
+ Description: "Get the thought history for a specific reasoning branch",
+ InputSchema: map[string]interface{}{
+ "type": "object",
+ "properties": map[string]interface{}{
+ "branchId": map[string]interface{}{
+ "type": "string",
+ "description": "Branch ID to get history for",
+ },
+ },
+ "required": []string{"branchId"},
+ },
+ },
+ {
+ Name: "clear_session",
+ Description: "Clear a thinking session and all its branches",
+ InputSchema: map[string]interface{}{
+ "type": "object",
+ "properties": map[string]interface{}{
+ "sessionId": map[string]interface{}{
+ "type": "string",
+ "description": "Session ID to clear",
+ },
+ },
+ "required": []string{"sessionId"},
+ },
+ },
}
}
// Tool handlers
func (sts *Server) HandleSequentialThinking(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
- // Parse input parameters
- thought, ok := req.Arguments["thought"].(string)
+ // Parse and validate input parameters
+ params, err := sts.parseThinkingParameters(req.Arguments)
+ if err != nil {
+ return mcp.NewToolError(err.Error()), nil
+ }
+
+ sts.mu.Lock()
+ defer sts.mu.Unlock()
+
+ // Get or create session
+ session := sts.getOrCreateSession(params.SessionID)
+
+ // Create thought object
+ currentThought := Thought{
+ Number: params.ThoughtNumber,
+ Content: params.Thought,
+ IsRevision: params.IsRevision,
+ RevisesThought: params.RevisesThought,
+ BranchFromThought: params.BranchFromThought,
+ BranchID: params.BranchID,
+ NeedsMoreThoughts: params.NeedsMoreThoughts,
+ }
+
+ // Handle branching
+ var activeBranch *Branch
+ if params.BranchFromThought != nil && params.BranchID != "" {
+ activeBranch = sts.getOrCreateBranch(session.ID, params.BranchID, *params.BranchFromThought)
+ activeBranch.Thoughts = append(activeBranch.Thoughts, currentThought)
+ activeBranch.LastActivity = time.Now()
+ } else {
+ // Add to main session
+ session.Thoughts = append(session.Thoughts, currentThought)
+ }
+
+ // Update session state
+ session.CurrentThought = params.ThoughtNumber
+ session.TotalThoughts = params.TotalThoughts
+ session.LastActivity = time.Now()
+
+ // Save to persistence if configured
+ sts.saveSessions()
+
+ // Determine status
+ status := "thinking"
+ if !params.NextThoughtNeeded {
+ status = "completed"
+ } else if params.ThoughtNumber >= params.TotalThoughts && !params.NeedsMoreThoughts {
+ status = "completed"
+ }
+ session.Status = status
+
+ // Create response with session context
+ response := ThinkingResponse{
+ Thought: params.Thought,
+ ThoughtNumber: params.ThoughtNumber,
+ TotalThoughts: params.TotalThoughts,
+ NextThoughtNeeded: params.NextThoughtNeeded,
+ Status: status,
+ SessionID: session.ID,
+ ThoughtHistorySize: len(session.Thoughts),
+ ActiveBranches: session.ActiveBranches,
+ }
+
+ // Add branch context if applicable
+ if activeBranch != nil {
+ response.BranchContext = fmt.Sprintf("Branch %s (from thought %d)", params.BranchID, *params.BranchFromThought)
+ }
+
+ // If this is the final thought, try to extract a solution
+ if status == "completed" {
+ response.Solution = sts.extractSolution(params.Thought)
+ }
+
+ // Format the result with session context
+ resultText := sts.formatThinkingResultWithSession(response, currentThought, session, activeBranch)
+
+ return mcp.NewToolResult(mcp.NewTextContent(resultText)), nil
+}
+
+// Helper methods
+
+// PersistentData represents the data structure for persistence
+type PersistentData struct {
+ Sessions map[string]*ThinkingSession `json:"sessions"`
+ Branches map[string]*Branch `json:"branches"`
+}
+
+// loadSessions loads sessions from persistence file
+func (sts *Server) loadSessions() error {
+ if sts.persistFile == "" {
+ return nil
+ }
+
+ if _, err := os.Stat(sts.persistFile); os.IsNotExist(err) {
+ return nil // File doesn't exist, start fresh
+ }
+
+ data, err := os.ReadFile(sts.persistFile)
+ if err != nil {
+ return err
+ }
+
+ if len(data) == 0 {
+ return nil // Empty file
+ }
+
+ var persistentData PersistentData
+ if err := json.Unmarshal(data, &persistentData); err != nil {
+ return err
+ }
+
+ sts.sessions = persistentData.Sessions
+ sts.branches = persistentData.Branches
+
+ // Initialize maps if nil
+ if sts.sessions == nil {
+ sts.sessions = make(map[string]*ThinkingSession)
+ }
+ if sts.branches == nil {
+ sts.branches = make(map[string]*Branch)
+ }
+
+ return nil
+}
+
+// saveSessions saves sessions to persistence file
+func (sts *Server) saveSessions() error {
+ if sts.persistFile == "" {
+ return nil
+ }
+
+ persistentData := PersistentData{
+ Sessions: sts.sessions,
+ Branches: sts.branches,
+ }
+
+ data, err := json.MarshalIndent(persistentData, "", " ")
+ if err != nil {
+ return err
+ }
+
+ return os.WriteFile(sts.persistFile, data, 0644)
+}
+
+// ThinkingParameters holds parsed parameters for thinking operations
+type ThinkingParameters struct {
+ Thought string
+ NextThoughtNeeded bool
+ ThoughtNumber int
+ TotalThoughts int
+ IsRevision bool
+ RevisesThought *int
+ BranchFromThought *int
+ BranchID string
+ NeedsMoreThoughts bool
+ SessionID string
+}
+
+// parseThinkingParameters parses and validates input parameters
+func (sts *Server) parseThinkingParameters(args map[string]interface{}) (*ThinkingParameters, error) {
+ params := &ThinkingParameters{}
+
+ // Required parameters
+ thought, ok := args["thought"].(string)
if !ok {
- return mcp.NewToolError("thought parameter is required and must be a string"), nil
+ return nil, fmt.Errorf("thought parameter is required and must be a string")
}
+ params.Thought = thought
- nextThoughtNeeded, ok := req.Arguments["nextThoughtNeeded"].(bool)
+ nextThoughtNeeded, ok := args["nextThoughtNeeded"].(bool)
if !ok {
- return mcp.NewToolError("nextThoughtNeeded parameter is required and must be a boolean"), nil
+ return nil, fmt.Errorf("nextThoughtNeeded parameter is required and must be a boolean")
}
+ params.NextThoughtNeeded = nextThoughtNeeded
- thoughtNumber := 1
- if tn, ok := req.Arguments["thoughtNumber"]; ok {
+ if tn, ok := args["thoughtNumber"]; ok {
switch v := tn.(type) {
case float64:
- thoughtNumber = int(v)
+ params.ThoughtNumber = int(v)
case int:
- thoughtNumber = v
+ params.ThoughtNumber = v
default:
- return mcp.NewToolError("thoughtNumber must be a number"), nil
+ return nil, fmt.Errorf("thoughtNumber must be a number")
}
- if thoughtNumber < 1 {
- return mcp.NewToolError("thoughtNumber must be >= 1"), nil
+ if params.ThoughtNumber < 1 {
+ return nil, fmt.Errorf("thoughtNumber must be >= 1")
}
+ } else {
+ params.ThoughtNumber = 1
}
- totalThoughts := 1
- if tt, ok := req.Arguments["totalThoughts"]; ok {
+ if tt, ok := args["totalThoughts"]; ok {
switch v := tt.(type) {
case float64:
- totalThoughts = int(v)
+ params.TotalThoughts = int(v)
case int:
- totalThoughts = v
+ params.TotalThoughts = v
default:
- return mcp.NewToolError("totalThoughts must be a number"), nil
+ return nil, fmt.Errorf("totalThoughts must be a number")
}
- if totalThoughts < 1 {
- return mcp.NewToolError("totalThoughts must be >= 1"), nil
+ if params.TotalThoughts < 1 {
+ return nil, fmt.Errorf("totalThoughts must be >= 1")
}
+ } else {
+ params.TotalThoughts = 1
}
- // Parse optional parameters
- isRevision := false
- if ir, ok := req.Arguments["isRevision"].(bool); ok {
- isRevision = ir
+ // Optional parameters
+ if ir, ok := args["isRevision"].(bool); ok {
+ params.IsRevision = ir
}
- var revisesThought *int
- if rt, ok := req.Arguments["revisesThought"]; ok && isRevision {
+ if rt, ok := args["revisesThought"]; ok && params.IsRevision {
switch v := rt.(type) {
case float64:
val := int(v)
- revisesThought = &val
+ params.RevisesThought = &val
case int:
- revisesThought = &v
+ params.RevisesThought = &v
default:
- return mcp.NewToolError("revisesThought must be a number"), nil
+ return nil, fmt.Errorf("revisesThought must be a number")
}
}
- var branchFromThought *int
- if bft, ok := req.Arguments["branchFromThought"]; ok {
+ if bft, ok := args["branchFromThought"]; ok {
switch v := bft.(type) {
case float64:
val := int(v)
- branchFromThought = &val
+ params.BranchFromThought = &val
case int:
- branchFromThought = &v
+ params.BranchFromThought = &v
default:
- return mcp.NewToolError("branchFromThought must be a number"), nil
+ return nil, fmt.Errorf("branchFromThought must be a number")
}
}
- branchID := ""
- if bid, ok := req.Arguments["branchId"].(string); ok {
- branchID = bid
+ if bid, ok := args["branchId"].(string); ok {
+ params.BranchID = bid
}
- needsMoreThoughts := false
- if nmt, ok := req.Arguments["needsMoreThoughts"].(bool); ok {
- needsMoreThoughts = nmt
+ if nmt, ok := args["needsMoreThoughts"].(bool); ok {
+ params.NeedsMoreThoughts = nmt
}
- // Create thought object
- currentThought := Thought{
- Number: thoughtNumber,
- Content: thought,
- IsRevision: isRevision,
- RevisesThought: revisesThought,
- BranchFromThought: branchFromThought,
- BranchID: branchID,
- NeedsMoreThoughts: needsMoreThoughts,
+ if sid, ok := args["sessionId"].(string); ok {
+ params.SessionID = sid
}
- // Determine status
- status := "thinking"
- if !nextThoughtNeeded {
- status = "completed"
- } else if thoughtNumber >= totalThoughts && !needsMoreThoughts {
- status = "completed"
+ return params, nil
+}
+
+// getOrCreateSession gets existing session or creates new one
+func (sts *Server) getOrCreateSession(sessionID string) *ThinkingSession {
+ if sessionID == "" {
+ sessionID = sts.generateSessionID()
}
- // Create response
- response := ThinkingResponse{
- Thought: thought,
- ThoughtNumber: thoughtNumber,
- TotalThoughts: totalThoughts,
- NextThoughtNeeded: nextThoughtNeeded,
- Status: status,
+ if session, exists := sts.sessions[sessionID]; exists {
+ return session
}
- // If this is the final thought, try to extract a solution
- if status == "completed" {
- response.Solution = sts.extractSolution(thought)
+ session := &ThinkingSession{
+ ID: sessionID,
+ Thoughts: make([]Thought, 0),
+ CurrentThought: 0,
+ TotalThoughts: 1,
+ Status: "active",
+ CreatedAt: time.Now(),
+ LastActivity: time.Now(),
+ ActiveBranches: make([]string, 0),
}
- // Add context information
- var contextInfo []string
+ sts.sessions[sessionID] = session
+ return session
+}
- if isRevision && revisesThought != nil {
- contextInfo = append(contextInfo, fmt.Sprintf("Revising thought %d", *revisesThought))
+// getOrCreateBranch gets existing branch or creates new one
+func (sts *Server) getOrCreateBranch(sessionID, branchID string, fromThought int) *Branch {
+ if branch, exists := sts.branches[branchID]; exists {
+ return branch
}
- if branchFromThought != nil {
- contextInfo = append(contextInfo, fmt.Sprintf("Branching from thought %d", *branchFromThought))
- if branchID != "" {
- contextInfo = append(contextInfo, fmt.Sprintf("Branch: %s", branchID))
- }
+ branch := &Branch{
+ ID: branchID,
+ SessionID: sessionID,
+ FromThought: fromThought,
+ Thoughts: make([]Thought, 0),
+ CreatedAt: time.Now(),
+ LastActivity: time.Now(),
}
- if needsMoreThoughts {
- contextInfo = append(contextInfo, "Requesting additional thoughts beyond initial estimate")
+ sts.branches[branchID] = branch
+
+ // Add to session's active branches
+ if session, exists := sts.sessions[sessionID]; exists {
+ session.ActiveBranches = append(session.ActiveBranches, branchID)
}
- // Format the result
- resultText := sts.formatThinkingResult(response, currentThought, contextInfo)
+ return branch
+}
- return mcp.NewToolResult(mcp.NewTextContent(resultText)), nil
+// generateSessionID generates a unique session ID
+func (sts *Server) generateSessionID() string {
+ return fmt.Sprintf("session_%d", time.Now().UnixNano())
}
-// Helper methods
+// formatThinkingResultWithSession formats result with session context
+func (sts *Server) formatThinkingResultWithSession(response ThinkingResponse, thought Thought, session *ThinkingSession, branch *Branch) string {
+ var result strings.Builder
+
+ // Header with session info
+ result.WriteString(fmt.Sprintf("๐ง Sequential Thinking - Thought %d/%d (Session: %s)\n",
+ response.ThoughtNumber, response.TotalThoughts, response.SessionID))
+ result.WriteString("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n\n")
+
+ // Session context
+ result.WriteString(fmt.Sprintf("๐ Session Info:\n"))
+ result.WriteString(fmt.Sprintf(" โข Session ID: %s\n", session.ID))
+ result.WriteString(fmt.Sprintf(" โข Total thoughts in session: %d\n", len(session.Thoughts)))
+ result.WriteString(fmt.Sprintf(" โข Active branches: %d\n", len(session.ActiveBranches)))
+ if response.BranchContext != "" {
+ result.WriteString(fmt.Sprintf(" โข Branch context: %s\n", response.BranchContext))
+ }
+ result.WriteString("\n")
+
+ // Context information
+ var contextInfo []string
+ if thought.IsRevision && thought.RevisesThought != nil {
+ contextInfo = append(contextInfo, fmt.Sprintf("Revising thought %d", *thought.RevisesThought))
+ }
+ if thought.BranchFromThought != nil {
+ contextInfo = append(contextInfo, fmt.Sprintf("Branching from thought %d", *thought.BranchFromThought))
+ if thought.BranchID != "" {
+ contextInfo = append(contextInfo, fmt.Sprintf("Branch: %s", thought.BranchID))
+ }
+ }
+ if thought.NeedsMoreThoughts {
+ contextInfo = append(contextInfo, "Requesting additional thoughts beyond initial estimate")
+ }
+
+ 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()
+}
func (sts *Server) extractSolution(finalThought string) string {
// Simple heuristic to extract a solution from the final thought
@@ -393,3 +728,122 @@ func (sts *Server) createProgressBar(current, total int) string {
return bar
}
+
+// New tool handlers for session management
+
+func (sts *Server) HandleGetSessionHistory(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+ sessionID, ok := req.Arguments["sessionId"].(string)
+ if !ok {
+ return mcp.NewToolError("sessionId parameter is required"), nil
+ }
+
+ sts.mu.RLock()
+ defer sts.mu.RUnlock()
+
+ session, exists := sts.sessions[sessionID]
+ if !exists {
+ return mcp.NewToolError(fmt.Sprintf("Session %s not found", sessionID)), nil
+ }
+
+ result := map[string]interface{}{
+ "sessionId": session.ID,
+ "status": session.Status,
+ "totalThoughts": len(session.Thoughts),
+ "createdAt": session.CreatedAt,
+ "lastActivity": session.LastActivity,
+ "activeBranches": session.ActiveBranches,
+ "thoughts": session.Thoughts,
+ }
+
+ jsonData, _ := json.MarshalIndent(result, "", " ")
+ return mcp.NewToolResult(mcp.NewTextContent(string(jsonData))), nil
+}
+
+func (sts *Server) HandleListSessions(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+ sts.mu.RLock()
+ defer sts.mu.RUnlock()
+
+ sessions := make([]map[string]interface{}, 0, len(sts.sessions))
+ for _, session := range sts.sessions {
+ sessionInfo := map[string]interface{}{
+ "sessionId": session.ID,
+ "status": session.Status,
+ "thoughtCount": len(session.Thoughts),
+ "branchCount": len(session.ActiveBranches),
+ "createdAt": session.CreatedAt,
+ "lastActivity": session.LastActivity,
+ }
+ sessions = append(sessions, sessionInfo)
+ }
+
+ result := map[string]interface{}{
+ "sessions": sessions,
+ "total": len(sessions),
+ }
+
+ jsonData, _ := json.MarshalIndent(result, "", " ")
+ return mcp.NewToolResult(mcp.NewTextContent(string(jsonData))), nil
+}
+
+func (sts *Server) HandleGetBranchHistory(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+ branchID, ok := req.Arguments["branchId"].(string)
+ if !ok {
+ return mcp.NewToolError("branchId parameter is required"), nil
+ }
+
+ sts.mu.RLock()
+ defer sts.mu.RUnlock()
+
+ branch, exists := sts.branches[branchID]
+ if !exists {
+ return mcp.NewToolError(fmt.Sprintf("Branch %s not found", branchID)), nil
+ }
+
+ result := map[string]interface{}{
+ "branchId": branch.ID,
+ "sessionId": branch.SessionID,
+ "fromThought": branch.FromThought,
+ "thoughtCount": len(branch.Thoughts),
+ "createdAt": branch.CreatedAt,
+ "lastActivity": branch.LastActivity,
+ "thoughts": branch.Thoughts,
+ }
+
+ jsonData, _ := json.MarshalIndent(result, "", " ")
+ return mcp.NewToolResult(mcp.NewTextContent(string(jsonData))), nil
+}
+
+func (sts *Server) HandleClearSession(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+ sessionID, ok := req.Arguments["sessionId"].(string)
+ if !ok {
+ return mcp.NewToolError("sessionId parameter is required"), nil
+ }
+
+ sts.mu.Lock()
+ defer sts.mu.Unlock()
+
+ session, exists := sts.sessions[sessionID]
+ if !exists {
+ return mcp.NewToolError(fmt.Sprintf("Session %s not found", sessionID)), nil
+ }
+
+ // Remove associated branches
+ for _, branchID := range session.ActiveBranches {
+ delete(sts.branches, branchID)
+ }
+
+ // Remove the session
+ delete(sts.sessions, sessionID)
+
+ // Save to persistence if configured
+ sts.saveSessions()
+
+ result := map[string]interface{}{
+ "message": fmt.Sprintf("Session %s and %d branches cleared", sessionID, len(session.ActiveBranches)),
+ "sessionId": sessionID,
+ "branchesCount": len(session.ActiveBranches),
+ }
+
+ jsonData, _ := json.MarshalIndent(result, "", " ")
+ return mcp.NewToolResult(mcp.NewTextContent(string(jsonData))), nil
+}