Skip to main content
Glama
tallpizza

Dooray MCP Server

by tallpizza

dooray_tasks

Manage Dooray project tasks by listing, creating, updating, deleting, changing status, and assigning members through the Dooray MCP Server.

Instructions

Manage Dooray tasks - list, get details, create, update, delete, change status, assign members

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform
taskIdNoTask ID (required for get/update/delete/status/assign)
titleNoTask title (for create/update)
descriptionNoTask description (for create/update)
statusNoTask status class or workflow name (for create/update/change_status)
workflowIdNoWorkflow ID (for change_status when you know the exact workflow)
assigneeIdNoAssignee member ID (for assign action)
priorityNoTask priority (for create/update)

Implementation Reference

  • TasksTool class implements the handler logic for dooray_tasks tool. The async handle method processes the input arguments based on the 'action' field and delegates to specific private methods that interact with DoorayClient.
    class TasksTool:
        """Tool for managing Dooray tasks."""
        
        def __init__(self, dooray_client):
            """Initialize with Dooray client."""
            self.client = dooray_client
        
        async def handle(self, arguments: Dict[str, Any]) -> str:
            """Handle tasks tool requests.
            
            Args:
                arguments: Tool arguments containing action and parameters
                
            Returns:
                JSON string with results
            """
            action = arguments.get("action")
            if not action:
                return json.dumps({"error": "Action parameter is required"})
            
            try:
                if action == "list":
                    return await self._list_tasks(arguments)
                elif action == "get":
                    return await self._get_task(arguments)
                elif action == "create":
                    return await self._create_task(arguments)
                elif action == "update":
                    return await self._update_task(arguments)
                elif action == "delete":
                    return await self._delete_task(arguments)
                elif action == "change_status":
                    return await self._change_status(arguments)
                elif action == "assign":
                    return await self._assign_task(arguments)
                else:
                    return json.dumps({"error": f"Unknown action: {action}"})
                    
            except Exception as e:
                logger.error(f"Error in tasks tool: {e}")
                return json.dumps({"error": str(e)})
        
        def _resolve_project_id(self, arguments: Dict[str, Any]) -> Optional[str]:
            """Resolve project ID from arguments or defaults."""
            project_id = arguments.get("projectId") or self.client.project_id or os.getenv("DOORAY_DEFAULT_PROJECT_ID")
            if project_id:
                return str(project_id)
            return None
    
        def _ensure_task_id(self, arguments: Dict[str, Any]) -> Optional[str]:
            task_id = arguments.get("taskId")
            if task_id is None:
                return None
            return str(task_id)
    
        def _ensure_assignee_id(self, arguments: Dict[str, Any]) -> Optional[str]:
            assignee_id = arguments.get("assigneeId")
            if assignee_id is None:
                return None
            return str(assignee_id)
    
        async def _list_tasks(self, arguments: Dict[str, Any]) -> str:
            """List tasks in a project."""
            project_id = self._resolve_project_id(arguments)
            if not project_id:
                return json.dumps({"error": "projectId is required for list action (set DOORAY_DEFAULT_PROJECT_ID or provide projectId)"})
            
            # Build query parameters
            params = {}
            if arguments.get("status"):
                params["workflowClass"] = arguments["status"]
            assignee_id = self._ensure_assignee_id(arguments)
            if assignee_id:
                params["assigneeId"] = assignee_id
            
            result = await self.client.list_tasks(project_id, **params)
            return json.dumps(result, ensure_ascii=False)
        
        async def _get_task(self, arguments: Dict[str, Any]) -> str:
            """Get a specific task."""
            project_id = self._resolve_project_id(arguments)
            task_id = self._ensure_task_id(arguments)
    
            if not project_id or not task_id:
                return json.dumps({"error": "projectId and taskId are required for get action"})
    
            result = await self.client.get_task(project_id, task_id)
            return json.dumps(result, ensure_ascii=False)
        
        async def _create_task(self, arguments: Dict[str, Any]) -> str:
            """Create a new task."""
            project_id = self._resolve_project_id(arguments)
            title = arguments.get("title")
    
            if not project_id or not title:
                return json.dumps({"error": "projectId and title are required for create action"})
            
            # Build task data
            task_data = {
                "subject": title,
                "body": {
                    "mimeType": "text/x-markdown",
                    "content": arguments.get("description", "")
                }
            }
            
            assignee_id = self._ensure_assignee_id(arguments)
            if assignee_id:
                task_data["users"] = [{"member": {"id": assignee_id}}]
            
            if arguments.get("priority"):
                task_data["priority"] = arguments["priority"]
            
            if arguments.get("status"):
                task_data["workflowClass"] = arguments["status"]
            
            result = await self.client.create_task(project_id, task_data)
            return json.dumps(result, ensure_ascii=False)
        
        async def _update_task(self, arguments: Dict[str, Any]) -> str:
            """Update an existing task."""
            project_id = self._resolve_project_id(arguments)
            task_id = self._ensure_task_id(arguments)
    
            if not project_id or not task_id:
                return json.dumps({"error": "projectId and taskId are required for update action"})
            
            # Build update data
            task_data = {}
            
            if arguments.get("title"):
                task_data["subject"] = arguments["title"]
            
            if arguments.get("description"):
                task_data["body"] = {
                    "mimeType": "text/x-markdown",
                    "content": arguments["description"]
                }
            
            assignee_id = self._ensure_assignee_id(arguments)
            if assignee_id:
                task_data["users"] = [{"member": {"id": assignee_id}}]
    
            if arguments.get("priority"):
                task_data["priority"] = arguments["priority"]
    
            if arguments.get("status"):
                task_data["workflowClass"] = arguments["status"]
            
            result = await self.client.update_task(project_id, task_id, task_data)
            return json.dumps(result, ensure_ascii=False)
        
        async def _delete_task(self, arguments: Dict[str, Any]) -> str:
            """Delete a task."""
            project_id = self._resolve_project_id(arguments)
            task_id = self._ensure_task_id(arguments)
    
            if not project_id or not task_id:
                return json.dumps({"error": "projectId and taskId are required for delete action"})
    
            result = await self.client.delete_task(project_id, task_id)
            return json.dumps({"success": True, "message": "Task deleted successfully"})
        
        async def _change_status(self, arguments: Dict[str, Any]) -> str:
            """Change task status."""
            project_id = self._resolve_project_id(arguments)
            task_id = self._ensure_task_id(arguments)
            status = arguments.get("status")
    
            workflow_id_arg = arguments.get("workflowId")
    
            if not project_id or not task_id or (status is None and workflow_id_arg is None):
                return json.dumps({"error": "projectId, taskId, and either status or workflowId are required for change_status action"})
    
            workflow_id = str(workflow_id_arg) if workflow_id_arg is not None else None
    
            if not workflow_id and status is not None:
                workflow_id = await self._resolve_workflow_id(project_id, status)
    
            if not workflow_id:
                return json.dumps({"error": "Unable to resolve workflow. Provide workflowId or use status/workflow name."}, ensure_ascii=False)
    
            result = await self.client.set_task_workflow(project_id, task_id, workflow_id)
            return json.dumps(result, ensure_ascii=False)
        
        async def _assign_task(self, arguments: Dict[str, Any]) -> str:
            """Assign task to a member."""
            project_id = self._resolve_project_id(arguments)
            task_id = self._ensure_task_id(arguments)
            assignee_id = self._ensure_assignee_id(arguments)
    
            if not task_id or not assignee_id:
                return json.dumps({"error": "taskId and assigneeId are required for assign action"})
            
            task_data = {"users": [{"member": {"id": assignee_id}}]}
            result = await self.client.update_task(project_id, task_id, task_data)
            return json.dumps(result, ensure_ascii=False)
    
        async def _resolve_workflow_id(self, project_id: str, identifier: str) -> Optional[str]:
            if identifier is None:
                return None
    
            identifier_text = str(identifier).strip()
            if not identifier_text:
                return None
    
            workflows = await self.client.list_workflows(project_id)
            items = workflows.get("result") or []
    
            normalized = identifier_text.lower()
            class_matches: list[tuple[int, str]] = []
    
            for item in items:
                workflow_id = str(item.get("id")) if item.get("id") is not None else None
                if not workflow_id:
                    continue
    
                if workflow_id == identifier_text:
                    return workflow_id
    
                name = item.get("name")
                if isinstance(name, str) and name.lower() == normalized:
                    return workflow_id
    
                for localized in item.get("names", []) or []:
                    localized_name = localized.get("name")
                    if isinstance(localized_name, str) and localized_name.lower() == normalized:
                        return workflow_id
    
                workflow_class = item.get("class")
                if isinstance(workflow_class, str) and workflow_class.lower() == normalized:
                    order_value = item.get("order")
                    try:
                        order = int(order_value)
                    except (TypeError, ValueError):
                        order = 0
                    class_matches.append((order, workflow_id))
    
            if class_matches:
                class_matches.sort(key=lambda pair: pair[0])
                return class_matches[0][1]
    
            return None
  • Defines the input schema for the dooray_tasks tool, specifying parameters like action, taskId, title, description, status, etc.
    types.Tool(
        name="dooray_tasks",
        description="Manage Dooray tasks - list, get details, create, update, delete, change status, assign members",
        inputSchema={
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "enum": ["list", "get", "create", "update", "delete", "change_status", "assign"],
                    "description": "Action to perform"
                },
                "taskId": {
                    "type": "string", 
                    "description": "Task ID (required for get/update/delete/status/assign)"
                },
                "title": {
                    "type": "string",
                    "description": "Task title (for create/update)"
                },
                "description": {
                    "type": "string",
                    "description": "Task description (for create/update)"
                },
                "status": {
                    "type": "string",
                    "description": "Task status class or workflow name (for create/update/change_status)"
                },
                "workflowId": {
                    "type": "string",
                    "description": "Workflow ID (for change_status when you know the exact workflow)"
                },
                "assigneeId": {
                    "type": "string",
                    "description": "Assignee member ID (for assign action)"
                },
                "priority": {
                    "type": "string",
                    "description": "Task priority (for create/update)"
                }
            },
            "required": ["action"]
        }
    ),
  • Registers the dooray_tasks tool via @app.list_tools() returning Tool objects, and handles calls via @app.call_tool() by instantiating TasksTool and calling its handle method.
    @app.list_tools()
    async def handle_list_tools() -> list[types.Tool]:
        """List all available Dooray tools."""
        return [
            types.Tool(
                name="dooray_tasks",
                description="Manage Dooray tasks - list, get details, create, update, delete, change status, assign members",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "action": {
                            "type": "string",
                            "enum": ["list", "get", "create", "update", "delete", "change_status", "assign"],
                            "description": "Action to perform"
                        },
                        "taskId": {
                            "type": "string", 
                            "description": "Task ID (required for get/update/delete/status/assign)"
                        },
                        "title": {
                            "type": "string",
                            "description": "Task title (for create/update)"
                        },
                        "description": {
                            "type": "string",
                            "description": "Task description (for create/update)"
                        },
                        "status": {
                            "type": "string",
                            "description": "Task status class or workflow name (for create/update/change_status)"
                        },
                        "workflowId": {
                            "type": "string",
                            "description": "Workflow ID (for change_status when you know the exact workflow)"
                        },
                        "assigneeId": {
                            "type": "string",
                            "description": "Assignee member ID (for assign action)"
                        },
                        "priority": {
                            "type": "string",
                            "description": "Task priority (for create/update)"
                        }
                    },
                    "required": ["action"]
                }
            ),
            types.Tool(
                name="dooray_comments",
                description="Manage Dooray task comments - get list, create, update, delete comments with mention support",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "action": {
                            "type": "string",
                            "enum": ["list", "create", "update", "delete"],
                            "description": "Action to perform on comments"
                        },
                        "taskId": {
                            "type": "string",
                            "description": "Task ID (required)"
                        },
                        "commentId": {
                            "type": "string",
                            "description": "Comment ID (required for update/delete)"
                        },
                        "content": {
                            "type": "string",
                            "description": "Comment content (for create/update)"
                        },
                        "mentions": {
                            "type": "array",
                            "items": {"type": "string"},
                            "description": "User IDs to mention (optional)"
                        }
                    },
                    "required": ["action", "taskId"]
                }
            ),
            types.Tool(
                name="dooray_tags",
                description="Manage Dooray tags - list available tags, create new tags, add/remove tags from tasks",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "action": {
                            "type": "string",
                            "enum": ["list", "create", "add_to_task", "remove_from_task"],
                            "description": "Action to perform on tags"
                        },
                        "taskId": {
                            "type": "string",
                            "description": "Task ID (required for add_to_task/remove_from_task)"
                        },
                        "tagName": {
                            "type": "string",
                            "description": "Tag name (for create/add_to_task/remove_from_task)"
                        },
                        "tagColor": {
                            "type": "string",
                            "description": "Tag color (for create action, optional)"
                        },
                        "filter": {
                            "type": "string",
                            "description": "Optional substring to filter tag list by name (list action)"
                        }
                    },
                    "required": ["action"]
                }
            ),
            types.Tool(
                name="dooray_search",
                description="Search Dooray content - tasks by various criteria including workflow, assignee, tags, status, date range with AND/OR logic",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "searchType": {
                            "type": "string",
                            "enum": ["tasks", "by_assignee", "by_status", "by_tag", "by_date_range", "by_workflow", "advanced"],
                            "description": "Type of search to perform"
                        },
                        "query": {
                            "type": "string",
                            "description": "Search query text (for tasks search)"
                        },
                        "assigneeId": {
                            "type": "string",
                            "description": "Assignee ID (for by_assignee search)"
                        },
                        "status": {
                            "type": "string",
                            "description": "Task status (for by_status search)"
                        },
                        "tagName": {
                            "type": "string",
                            "description": "Tag name (for by_tag search)"
                        },
                        "workflowId": {
                            "type": "string",
                            "description": "Workflow ID (for by_workflow search)"
                        },
                        "startDate": {
                            "type": "string",
                            "description": "Start date (for by_date_range search)"
                        },
                        "endDate": {
                            "type": "string",
                            "description": "End date (for by_date_range search)"
                        },
                        "conditions": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string",
                                        "enum": ["workflow", "assignee", "tag", "status", "query", "date_range"],
                                        "description": "Type of search condition"
                                    },
                                    "value": {
                                        "type": "string",
                                        "description": "Value for the condition (workflow ID, assignee ID, tag name, status, or query text)"
                                    },
                                    "startDate": {
                                        "type": "string",
                                        "description": "Start date (for date_range type)"
                                    },
                                    "endDate": {
                                        "type": "string",
                                        "description": "End date (for date_range type)"
                                    }
                                },
                                "required": ["type"]
                            },
                            "description": "Array of search conditions (for advanced search)"
                        },
                        "logicOperator": {
                            "type": "string",
                            "enum": ["AND", "OR"],
                            "description": "Logic operator for combining conditions in advanced search (default: AND)"
                        },
                        "limit": {
                            "type": "integer",
                            "description": "Maximum results to return (optional)"
                        }
                    },
                    "required": ["searchType"]
                }
            ),
            types.Tool(
                name="dooray_members",
                description="Manage Dooray members - search by email/ID, get member details, check project membership",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "action": {
                            "type": "string",
                            "enum": ["search_by_email", "search_by_id", "get_details", "list_project_members"],
                            "description": "Action to perform on members"
                        },
                        "email": {
                            "type": "string",
                            "description": "Email address (for search_by_email)"
                        },
                        "userId": {
                            "type": "string",
                            "description": "User ID (for search_by_id/get_details)"
                        },
                        "projectId": {
                            "type": "string",
                            "description": "Project ID (optional - uses default from environment if not provided)"
                        }
                    },
                    "required": ["action"]
                }
            ),
            types.Tool(
                name="dooray_files",
                description="Manage Dooray files and images - list task files, get file metadata, download file content from tasks or directly by content ID",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "action": {
                            "type": "string",
                            "enum": ["list_task_files", "get_task_file_metadata", "get_task_file_content", "get_drive_file_metadata", "get_drive_file_content"],
                            "description": "Action to perform on files"
                        },
                        "taskId": {
                            "type": "string",
                            "description": "Task ID (required for task file actions)"
                        },
                        "fileId": {
                            "type": "string",
                            "description": "File ID (required for file operations)"
                        },
                        "projectId": {
                            "type": "string",
                            "description": "Project ID (optional - uses default from environment if not provided, required for task file actions)"
                        }
                    },
                    "required": ["action"]
                }
            ),
            types.Tool(
                name="dooray_workflows",
                description="Manage Dooray workflows - list project workflows, get workflow details, create, update, delete workflows",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "action": {
                            "type": "string",
                            "enum": ["list", "get", "create", "update", "delete"],
                            "description": "Action to perform on workflows"
                        },
                        "workflowId": {
                            "type": "string",
                            "description": "Workflow ID (required for get/update/delete)"
                        },
                        "name": {
                            "type": "string",
                            "description": "Workflow name (for create/update)"
                        },
                        "projectId": {
                            "type": "string",
                            "description": "Project ID (optional - uses default from environment if not provided)"
                        }
                    },
                    "required": ["action"]
                }
            )
        ]
    
    @app.call_tool()
    async def handle_call_tool(name: str, arguments: dict[str, Any] | None) -> list[types.TextContent]:
        """Handle tool calls from Claude."""
        global dooray_client, default_project_id
        
        if not dooray_client:
            return [types.TextContent(
                type="text",
                text="Error: Dooray client not initialized. Please check your DOORAY_API_TOKEN environment variable."
            )]
        
        # Use default project ID from environment for tools that need it
        args = arguments or {}
        
        # Only add projectId for tools that need it (all except some member operations)
        if name in ["dooray_tasks", "dooray_comments", "dooray_tags", "dooray_search", "dooray_workflows"]:
            if not default_project_id:
                return [types.TextContent(
                    type="text",
                    text="Error: DOORAY_DEFAULT_PROJECT_ID environment variable is required"
                )]
            args["projectId"] = default_project_id
        elif name == "dooray_files":
            # Add projectId for task file actions only if not provided
            if args.get("action") in ["list_task_files", "get_task_file_metadata", "get_task_file_content"]:
                if not args.get("projectId") and not default_project_id:
                    return [types.TextContent(
                        type="text",
                        text="Error: DOORAY_DEFAULT_PROJECT_ID environment variable is required for task file operations"
                    )]
                if not args.get("projectId"):
                    args["projectId"] = default_project_id
        elif name == "dooray_members":
            # Only add projectId for list_project_members action
            if args.get("action") == "list_project_members":
                if not default_project_id:
                    return [types.TextContent(
                        type="text",
                        text="Error: DOORAY_DEFAULT_PROJECT_ID environment variable is required"
                    )]
                args["projectId"] = default_project_id
        
        try:
            if name == "dooray_tasks":
                tool = TasksTool(dooray_client)
                result = await tool.handle(args)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tallpizza/dooray-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server