Skip to main content
Glama

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