Skip to main content
Glama
cdmx-in
by cdmx-in

search_project_documents

Search for documents within a specific Goodday project by name or content. Filter results with optional document name matching and include full document content when needed.

Instructions

Search for documents in a specific project.

Args: project_name: The name of the project to search in (case-insensitive) document_name: Optional document name to filter by (case-insensitive partial match) include_content: Whether to include the full content of each document

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_nameYes
document_nameNo
include_contentNo

Implementation Reference

  • The handler function implementing the 'search_project_documents' MCP tool. It locates the project, retrieves and filters documents, optionally fetches and includes content, and returns formatted results.
    async def search_project_documents(project_name: str, document_name: Optional[str] = None, include_content: bool = False) -> str:
        """Search for documents in a specific project.
    
        Args:
            project_name: The name of the project to search in (case-insensitive)
            document_name: Optional document name to filter by (case-insensitive partial match)
            include_content: Whether to include the full content of each document
        """
        # Find project
        projects_data = await make_goodday_request("projects?archived=true")
        if not projects_data or not isinstance(projects_data, list):
            return "Unable to fetch projects."
    
        project_type_projects = [p for p in projects_data if isinstance(p, dict) and p.get('systemType') in ['PROJECT', 'FOLDER']]
        matching_projects = []
        search_term = project_name.lower()
        for project in project_type_projects:
            if isinstance(project, dict) and search_term in project.get('name', '').lower():
                matching_projects.append(project)
    
        if not matching_projects:
            return f"No projects found containing '{project_name}' in their name."
    
        target_project = matching_projects[0]
        actual_project_name = target_project.get('name')
        project_id = target_project.get('id')
    
        # Get documents
        documents_data = await make_goodday_request(f"project/{project_id}/documents")
        if not documents_data:
            return f"No documents found in project '{actual_project_name}'."
    
        if isinstance(documents_data, dict) and "error" in documents_data:
            return f"Unable to fetch documents: {documents_data.get('error', 'Unknown error')}"
    
        if not isinstance(documents_data, list):
            return f"Unexpected response format for documents: {str(documents_data)}"
    
        # Filter by document name if specified
        if document_name:
            filtered_documents = []
            for doc in documents_data:
                if isinstance(doc, dict) and document_name.lower() in doc.get('name', '').lower():
                    filtered_documents.append(doc)
            documents_data = filtered_documents
    
        if not documents_data:
            return f"No documents found matching '{document_name}' in project '{actual_project_name}'."
    
        # Get mappings
        user_id_to_name = await get_user_mapping()
        project_id_to_name = await get_project_mapping()
    
        # Format documents
        formatted_docs = []
        for doc in documents_data:
            if isinstance(doc, dict):
                doc_id = doc.get('id', 'N/A')
                doc_content = ""
                
                if include_content and doc_id != 'N/A':
                    try:
                        content_data = await make_goodday_request(f"document/{doc_id}")
                        if content_data:
                            if isinstance(content_data, dict):
                                doc_content = content_data.get('content', content_data.get('text', str(content_data)))
                            else:
                                doc_content = str(content_data)
                    except Exception as e:
                        doc_content = f"Error fetching content: {str(e)}"
                
                project_id_val = doc.get('projectId', 'N/A')
                project_name_val = project_id_to_name.get(project_id_val, f"Project {project_id_val}") if project_id_val != 'N/A' else 'N/A'
                
                created_by_id = doc.get('createdByUserId', 'N/A')
                created_by_name = user_id_to_name.get(created_by_id, f"User {created_by_id}") if created_by_id != 'N/A' else 'N/A'
                
                formatted_doc = f"""
    **Document ID:** {doc_id}
    **Name:** {doc.get('name', 'N/A')}
    **Project:** {project_name_val}
    **Created By:** {created_by_name}
    **Created:** {format_timestamp_ist(doc.get('momentCreated', 'N/A'))}
    **Updated:** {format_timestamp_ist(doc.get('momentUpdated', 'N/A'))}"""
                
                if include_content:
                    formatted_doc += f"\n**Content:**\n{doc_content}"
                
                formatted_docs.append(formatted_doc.strip())
    
        result = "\n---\n".join(formatted_docs)
        filter_text = f" matching '{document_name}'" if document_name else ""
        return f"**Documents in project '{actual_project_name}'{filter_text}:**\n\n{result}"
  • The @mcp.tool() decorator registers the search_project_documents function as an MCP tool.
    async def search_project_documents(project_name: str, document_name: Optional[str] = None, include_content: bool = False) -> str:
  • Helper function used by search_project_documents to make API requests to Goodday.
    async def make_goodday_request(endpoint: str, method: str = "GET", data: dict = None, subfolders: bool = True) -> dict[str, Any] | list[Any] | None:
        """Make a request to the Goodday API with proper error handling."""
        api_token = os.getenv("GOODDAY_API_TOKEN")
        if not api_token:
            raise ValueError("GOODDAY_API_TOKEN environment variable is required")
        
        headers = {
            "User-Agent": USER_AGENT,
            "gd-api-token": api_token,
            "Content-Type": "application/json"
        }
        
        # Automatically add subfolders=true for project task and document endpoints if not already present
        if subfolders and endpoint.startswith("project/") and ("/tasks" in endpoint or "/documents" in endpoint):
            if "?" in endpoint:
                if "subfolders=" not in endpoint:
                    endpoint += "&subfolders=true"
            else:
                endpoint += "?subfolders=true"
        
        url = f"{GOODDAY_API_BASE}/{endpoint.lstrip('/')}"
        
        async with httpx.AsyncClient() as client:
            try:
                if method.upper() == "POST":
                    response = await client.post(url, headers=headers, json=data, timeout=30.0)
                elif method.upper() == "PUT":
                    response = await client.put(url, headers=headers, json=data, timeout=30.0)
                elif method.upper() == "DELETE":
                    response = await client.delete(url, headers=headers, timeout=30.0)
                else:
                    response = await client.get(url, headers=headers, timeout=30.0)
    
                response.raise_for_status()
                return response.json()
    
            except httpx.HTTPStatusError as e:
                raise Exception(f"HTTP error {e.response.status_code}: {e.response.text}")
            except httpx.RequestError as e:
                raise Exception(f"Request error: {str(e)}")
            except Exception as e:
                raise Exception(f"Unexpected error: {str(e)}")
  • Helper function providing project ID to name mapping, used in formatting documents.
    """Get mapping of project IDs to names."""
    data = await make_goodday_request("projects")
    project_id_to_name = {}
    if isinstance(data, list):
        for p in data:
            if isinstance(p, dict):
                project_id_to_name[p.get("id")] = p.get("name", "Unknown")
    return project_id_to_name
  • Helper function providing user ID to name mapping, used in formatting documents.
    """Get mapping of user IDs to names."""
    data = await make_goodday_request("users")
    user_id_to_name = {}
    if isinstance(data, list):
        for u in data:
            if isinstance(u, dict):
                user_id_to_name[u.get("id")] = u.get("name", "Unknown")
    return user_id_to_name

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/cdmx-in/goodday-mcp'

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