refactored all tools to use fastmcp native responses

This commit is contained in:
Taylor Wilsdon
2025-06-06 17:32:09 -04:00
parent 047c44b472
commit 100fb4399d
7 changed files with 210 additions and 452 deletions

View File

@@ -29,9 +29,12 @@ async def search_docs(
user_google_email: str,
query: str,
page_size: int = 10,
) -> types.CallToolResult:
) -> str:
"""
Searches for Google Docs by name using Drive API (mimeType filter).
Returns:
str: A formatted list of Google Docs matching the search query.
"""
tool_name = "search_docs"
logger.info(f"[{tool_name}] Email={user_google_email}, Query='{query}'")
@@ -59,29 +62,31 @@ async def search_docs(
)
files = response.get('files', [])
if not files:
return types.CallToolResult(content=[types.TextContent(type="text",
text=f"No Google Docs found matching '{query}'.")])
return f"No Google Docs found matching '{query}'."
output = [f"Found {len(files)} Google Docs matching '{query}':"]
for f in files:
output.append(
f"- {f['name']} (ID: {f['id']}) Modified: {f.get('modifiedTime')} Link: {f.get('webViewLink')}"
)
return types.CallToolResult(content=[types.TextContent(type="text", text="\n".join(output))])
return "\n".join(output)
except HttpError as e:
logger.error(f"API error in search_docs: {e}", exc_info=True)
return types.CallToolResult(isError=True, content=[types.TextContent(type="text", text=f"API error: {e}")])
raise Exception(f"API error: {e}")
@server.tool()
async def get_doc_content(
user_google_email: str,
document_id: str,
) -> types.CallToolResult:
) -> str:
"""
Retrieves content of a Google Doc or a Drive file (like .docx) identified by document_id.
- Native Google Docs: Fetches content via Docs API.
- Office files (.docx, etc.) stored in Drive: Downloads via Drive API and extracts text.
Returns:
str: The document content with metadata header.
"""
tool_name = "get_doc_content"
logger.info(f"[{tool_name}] Invoked. Document/File ID: '{document_id}' for user '{user_google_email}'")
@@ -186,34 +191,29 @@ async def get_doc_content(
f'File: "{file_name}" (ID: {document_id}, Type: {mime_type})\n'
f'Link: {web_view_link}\n\n--- CONTENT ---\n'
)
return types.CallToolResult(
content=[types.TextContent(type="text", text=header + body_text)]
)
return header + body_text
except HttpError as error:
logger.error(
f"[{tool_name}] API error for ID {document_id}: {error}",
exc_info=True,
)
return types.CallToolResult(
isError=True,
content=[types.TextContent(type="text", text=f"API error processing document/file ID {document_id}: {error}")],
)
raise Exception(f"API error processing document/file ID {document_id}: {error}")
except Exception as e:
logger.exception(f"[{tool_name}] Unexpected error for ID {document_id}: {e}")
return types.CallToolResult(
isError=True,
content=[types.TextContent(type="text", text=f"Unexpected error processing document/file ID {document_id}: {e}")],
)
raise Exception(f"Unexpected error processing document/file ID {document_id}: {e}")
@server.tool()
async def list_docs_in_folder(
user_google_email: str,
folder_id: str = 'root',
page_size: int = 100
) -> types.CallToolResult:
) -> str:
"""
Lists Google Docs within a specific Drive folder.
Returns:
str: A formatted list of Google Docs in the specified folder.
"""
tool_name = "list_docs_in_folder"
logger.info(f"[{tool_name}] Invoked. Email: '{user_google_email}', Folder ID: '{folder_id}'")
@@ -239,28 +239,30 @@ async def list_docs_in_folder(
)
items = rsp.get('files', [])
if not items:
return types.CallToolResult(content=[types.TextContent(type="text",
text=f"No Google Docs found in folder '{folder_id}'.")])
return f"No Google Docs found in folder '{folder_id}'."
out = [f"Found {len(items)} Docs in folder '{folder_id}':"]
for f in items:
out.append(f"- {f['name']} (ID: {f['id']}) Modified: {f.get('modifiedTime')} Link: {f.get('webViewLink')}")
return types.CallToolResult(content=[types.TextContent(type="text", text="\n".join(out))])
return "\n".join(out)
except HttpError as e:
logger.error(f"API error in {tool_name}: {e}", exc_info=True)
return types.CallToolResult(isError=True, content=[types.TextContent(type="text", text=f"API error: {e}")])
raise Exception(f"API error: {e}")
except Exception as e:
logger.exception(f"Unexpected error in {tool_name}: {e}")
return types.CallToolResult(isError=True, content=[types.TextContent(type="text", text=f"Unexpected error: {e}")])
raise Exception(f"Unexpected error: {e}")
@server.tool()
async def create_doc(
user_google_email: str, # Made user_google_email required
title: str,
content: str = '',
) -> types.CallToolResult:
) -> str:
"""
Creates a new Google Doc and optionally inserts initial content.
Returns:
str: Confirmation message with document ID and link.
"""
tool_name = "create_doc"
logger.info(f"[{tool_name}] Invoked. Email: '{user_google_email}', Title='{title}'")
@@ -285,11 +287,11 @@ async def create_doc(
link = f"https://docs.google.com/document/d/{doc_id}/edit"
msg = f"Created Google Doc '{title}' (ID: {doc_id}) for {user_email}. Link: {link}"
logger.info(f"Successfully created Google Doc '{title}' (ID: {doc_id}) for {user_email}. Link: {link}")
return types.CallToolResult(content=[types.TextContent(type="text", text=msg)])
return msg
except HttpError as e:
logger.error(f"API error in {tool_name}: {e}", exc_info=True)
return types.CallToolResult(isError=True, content=[types.TextContent(type="text", text=f"API error: {e}")])
raise Exception(f"API error: {e}")
except Exception as e:
logger.exception(f"Unexpected error in {tool_name}: {e}")
return types.CallToolResult(isError=True, content=[types.TextContent(type="text", text=f"Unexpected error: {e}")])
raise Exception(f"Unexpected error: {e}")