Commit f847fd0

mo khan <mo@mokhan.ca>
2025-08-14 23:33:14
fix: imap server tools/list
1 parent 5d2408c
Changed files (1)
pkg
pkg/imap/server.go
@@ -75,18 +75,7 @@ func NewServer(server, username, password string, port int, useTLS bool) *Server
 		useTLS:   useTLS,
 	}
 
-	s.RegisterTool("imap_list_folders", s.handleListFolders)
-	s.RegisterTool("imap_list_messages", s.handleListMessages)
-	s.RegisterTool("imap_read_message", s.handleReadMessage)
-	s.RegisterTool("imap_search_messages", s.handleSearchMessages)
-	s.RegisterTool("imap_get_folder_stats", s.handleGetFolderStats)
-	s.RegisterTool("imap_mark_as_read", s.handleMarkAsRead)
-	s.RegisterTool("imap_get_attachments", s.handleGetAttachments)
-	s.RegisterTool("imap_get_connection_info", s.handleGetConnectionInfo)
-	s.RegisterTool("imap_delete_message", s.handleDeleteMessage)
-	s.RegisterTool("imap_delete_messages", s.handleDeleteMessages)
-	s.RegisterTool("imap_move_to_trash", s.handleMoveToTrash)
-	s.RegisterTool("imap_expunge_folder", s.handleExpungeFolder)
+	s.registerTools()
 
 	// Register prompts
 	analysisPrompt := mcp.Prompt{
@@ -103,6 +92,307 @@ func NewServer(server, username, password string, port int, useTLS bool) *Server
 	return s
 }
 
+// ListTools returns all available IMAP email management tools
+func (s *Server) ListTools() []mcp.Tool {
+	return []mcp.Tool{
+		{
+			Name:        "imap_list_folders",
+			Description: "List all folders in the email account (INBOX, Sent, Drafts, etc.)",
+			InputSchema: map[string]interface{}{
+				"type":       "object",
+				"properties": map[string]interface{}{},
+			},
+		},
+		{
+			Name:        "imap_list_messages",
+			Description: "List messages in a folder with optional pagination and filtering",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+					"limit": map[string]interface{}{
+						"type":        "integer",
+						"description": "Maximum number of messages to return (default: 20)",
+						"minimum":     1,
+						"maximum":     100,
+					},
+					"offset": map[string]interface{}{
+						"type":        "integer",
+						"description": "Number of messages to skip (for pagination) (default: 0)",
+						"minimum":     0,
+					},
+					"unread_only": map[string]interface{}{
+						"type":        "boolean",
+						"description": "Show only unread messages (default: false)",
+					},
+				},
+			},
+		},
+		{
+			Name:        "imap_read_message",
+			Description: "Read the full content of a specific email message",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"uid": map[string]interface{}{
+						"type":        "integer",
+						"description": "Message UID",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+					"mark_as_read": map[string]interface{}{
+						"type":        "boolean",
+						"description": "Mark message as read when reading (default: false)",
+					},
+				},
+				"required": []string{"uid"},
+			},
+		},
+		{
+			Name:        "imap_search_messages",
+			Description: "Search for messages by content, sender, subject, or other criteria",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"query": map[string]interface{}{
+						"type":        "string",
+						"description": "Search query text",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder to search in (default: INBOX)",
+					},
+					"search_type": map[string]interface{}{
+						"type":        "string",
+						"description": "Type of search (subject, from, body, all) (default: all)",
+						"enum":        []string{"subject", "from", "to", "body", "all"},
+					},
+					"since_date": map[string]interface{}{
+						"type":        "string",
+						"description": "Search messages since this date (YYYY-MM-DD format)",
+					},
+					"before_date": map[string]interface{}{
+						"type":        "string",
+						"description": "Search messages before this date (YYYY-MM-DD format)",
+					},
+					"limit": map[string]interface{}{
+						"type":        "integer",
+						"description": "Maximum number of results (default: 50)",
+						"minimum":     1,
+						"maximum":     200,
+					},
+				},
+				"required": []string{"query"},
+			},
+		},
+		{
+			Name:        "imap_get_folder_stats",
+			Description: "Get statistics for a folder (total messages, unread count, etc.)",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+				},
+			},
+		},
+		{
+			Name:        "imap_mark_as_read",
+			Description: "Mark one or more messages as read or unread",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"uid": map[string]interface{}{
+						"type":        "integer",
+						"description": "Message UID to mark",
+					},
+					"uids": map[string]interface{}{
+						"type": "array",
+						"items": map[string]interface{}{
+							"type": "integer",
+						},
+						"description": "Array of message UIDs to mark (alternative to single uid)",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+					"read": map[string]interface{}{
+						"type":        "boolean",
+						"description": "Mark as read (true) or unread (false) (default: true)",
+					},
+				},
+			},
+		},
+		{
+			Name:        "imap_get_attachments",
+			Description: "List attachments for a message",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"uid": map[string]interface{}{
+						"type":        "integer",
+						"description": "Message UID",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+				},
+				"required": []string{"uid"},
+			},
+		},
+		{
+			Name:        "imap_get_connection_info",
+			Description: "Get information about the IMAP server connection and capabilities",
+			InputSchema: map[string]interface{}{
+				"type":       "object",
+				"properties": map[string]interface{}{},
+			},
+		},
+		{
+			Name:        "imap_delete_message",
+			Description: "Delete a single message (requires confirmation for safety)",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"uid": map[string]interface{}{
+						"type":        "integer",
+						"description": "Message UID to delete",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+					"confirmed": map[string]interface{}{
+						"type":        "boolean",
+						"description": "Confirmation that you want to permanently delete this message",
+					},
+				},
+				"required": []string{"uid", "confirmed"},
+			},
+		},
+		{
+			Name:        "imap_delete_messages",
+			Description: "Delete multiple messages in bulk (requires confirmation for safety)",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"uids": map[string]interface{}{
+						"type": "array",
+						"items": map[string]interface{}{
+							"type": "integer",
+						},
+						"description": "Array of message UIDs to delete",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name (default: INBOX)",
+					},
+					"confirmed": map[string]interface{}{
+						"type":        "boolean",
+						"description": "Confirmation that you want to permanently delete these messages",
+					},
+				},
+				"required": []string{"uids", "confirmed"},
+			},
+		},
+		{
+			Name:        "imap_move_to_trash",
+			Description: "Move messages to trash folder (safer alternative to permanent deletion)",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"uid": map[string]interface{}{
+						"type":        "integer",
+						"description": "Message UID to move to trash",
+					},
+					"uids": map[string]interface{}{
+						"type": "array",
+						"items": map[string]interface{}{
+							"type": "integer",
+						},
+						"description": "Array of message UIDs to move to trash (alternative to single uid)",
+					},
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Source folder name (default: INBOX)",
+					},
+					"trash_folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Trash folder name (default: Trash or [Gmail]/Trash)",
+					},
+				},
+			},
+		},
+		{
+			Name:        "imap_expunge_folder",
+			Description: "Permanently remove messages marked for deletion from a folder",
+			InputSchema: map[string]interface{}{
+				"type": "object",
+				"properties": map[string]interface{}{
+					"folder": map[string]interface{}{
+						"type":        "string",
+						"description": "Folder name to expunge (default: INBOX)",
+					},
+					"confirmed": map[string]interface{}{
+						"type":        "boolean",
+						"description": "Confirmation that you want to permanently expunge deleted messages",
+					},
+				},
+				"required": []string{"confirmed"},
+			},
+		},
+	}
+}
+
+// registerTools registers all IMAP tools with the server
+func (s *Server) registerTools() {
+	// Get all tool definitions from ListTools method
+	tools := s.ListTools()
+	
+	// Register each tool with its proper definition
+	for _, tool := range tools {
+		var handler mcp.ToolHandler
+		switch tool.Name {
+		case "imap_list_folders":
+			handler = s.handleListFolders
+		case "imap_list_messages":
+			handler = s.handleListMessages
+		case "imap_read_message":
+			handler = s.handleReadMessage
+		case "imap_search_messages":
+			handler = s.handleSearchMessages
+		case "imap_get_folder_stats":
+			handler = s.handleGetFolderStats
+		case "imap_mark_as_read":
+			handler = s.handleMarkAsRead
+		case "imap_get_attachments":
+			handler = s.handleGetAttachments
+		case "imap_get_connection_info":
+			handler = s.handleGetConnectionInfo
+		case "imap_delete_message":
+			handler = s.handleDeleteMessage
+		case "imap_delete_messages":
+			handler = s.handleDeleteMessages
+		case "imap_move_to_trash":
+			handler = s.handleMoveToTrash
+		case "imap_expunge_folder":
+			handler = s.handleExpungeFolder
+		default:
+			continue
+		}
+		s.RegisterToolWithDefinition(tool, handler)
+	}
+}
+
 func (s *Server) connect() error {
 	if s.connected && s.client != nil {
 		return nil