2025-06-09 16:41:09 -04:00
"""
Google Forms MCP Tools
This module provides MCP tools for interacting with Google Forms API.
"""
import logging
import asyncio
2025-07-10 14:08:08 -04:00
from typing import Optional , Dict , Any
2025-06-09 16:41:09 -04:00
from mcp import types
from auth.service_decorator import require_google_service
from core.server import server
2025-07-14 15:08:03 +01:00
from core.utils import handle_http_errors , retry_on_ssl_error
2025-06-09 16:41:09 -04:00
logger = logging . getLogger ( __name__ )
@server.tool ()
@require_google_service ( "forms" , "forms" )
2025-06-18 16:29:35 -04:00
@handle_http_errors ( "create_form" )
2025-07-14 15:08:03 +01:00
@retry_on_ssl_error ()
2025-06-09 16:41:09 -04:00
async def create_form (
service ,
user_google_email : str ,
title : str ,
description : Optional [ str ] = None ,
document_title : Optional [ str ] = None
) -> str :
"""
Create a new form using the title given in the provided form message in the request.
Args:
user_google_email (str): The user's Google email address. Required.
title (str): The title of the form.
description (Optional[str]): The description of the form.
document_title (Optional[str]): The document title (shown in browser tab).
Returns:
str: Confirmation message with form ID and edit URL.
"""
logger . info ( f "[create_form] Invoked. Email: ' { user_google_email } ', Title: { title } " )
2025-06-18 16:29:35 -04:00
form_body : Dict [ str , Any ] = {
"info" : {
"title" : title
2025-06-09 16:41:09 -04:00
}
2025-06-18 16:29:35 -04:00
}
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
if description :
form_body [ "info" ][ "description" ] = description
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
if document_title :
form_body [ "info" ][ "document_title" ] = document_title
2025-06-09 16:41:09 -04:00
2025-06-18 16:29:35 -04:00
created_form = await asyncio . to_thread (
service . forms () . create ( body = form_body ) . execute
)
form_id = created_form . get ( "formId" )
edit_url = f "https://docs.google.com/forms/d/ { form_id } /edit"
responder_url = created_form . get ( "responderUri" , f "https://docs.google.com/forms/d/ { form_id } /viewform" )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
confirmation_message = f "Successfully created form ' { created_form . get ( 'info' , {}) . get ( 'title' , title ) } ' for { user_google_email } . Form ID: { form_id } . Edit URL: { edit_url } . Responder URL: { responder_url } "
logger . info ( f "Form created successfully for { user_google_email } . ID: { form_id } " )
return confirmation_message
2025-06-09 16:41:09 -04:00
@server.tool ()
@require_google_service ( "forms" , "forms" )
2025-06-18 16:29:35 -04:00
@handle_http_errors ( "get_form" )
2025-07-14 15:08:03 +01:00
@retry_on_ssl_error ()
2025-06-09 16:41:09 -04:00
async def get_form (
service ,
user_google_email : str ,
form_id : str
) -> str :
"""
Get a form.
Args:
user_google_email (str): The user's Google email address. Required.
form_id (str): The ID of the form to retrieve.
Returns:
str: Form details including title, description, questions, and URLs.
"""
logger . info ( f "[get_form] Invoked. Email: ' { user_google_email } ', Form ID: { form_id } " )
2025-06-18 16:29:35 -04:00
form = await asyncio . to_thread (
service . forms () . get ( formId = form_id ) . execute
)
form_info = form . get ( "info" , {})
title = form_info . get ( "title" , "No Title" )
description = form_info . get ( "description" , "No Description" )
document_title = form_info . get ( "documentTitle" , title )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
edit_url = f "https://docs.google.com/forms/d/ { form_id } /edit"
responder_url = form . get ( "responderUri" , f "https://docs.google.com/forms/d/ { form_id } /viewform" )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
items = form . get ( "items" , [])
questions_summary = []
for i , item in enumerate ( items , 1 ):
item_title = item . get ( "title" , f "Question { i } " )
item_type = item . get ( "questionItem" , {}) . get ( "question" , {}) . get ( "required" , False )
required_text = " (Required)" if item_type else ""
questions_summary . append ( f " { i } . { item_title }{ required_text } " )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
questions_text = " \n " . join ( questions_summary ) if questions_summary else " No questions found"
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
result = f """Form Details for { user_google_email } :
2025-06-09 16:41:09 -04:00
- Title: " { title } "
- Description: " { description } "
- Document Title: " { document_title } "
- Form ID: { form_id }
- Edit URL: { edit_url }
- Responder URL: { responder_url }
- Questions ( { len ( items ) } total):
{ questions_text } """
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
logger . info ( f "Successfully retrieved form for { user_google_email } . ID: { form_id } " )
return result
2025-06-09 16:41:09 -04:00
@server.tool ()
@require_google_service ( "forms" , "forms" )
2025-06-18 16:29:35 -04:00
@handle_http_errors ( "set_publish_settings" )
2025-07-14 15:08:03 +01:00
@retry_on_ssl_error ()
2025-06-09 16:41:09 -04:00
async def set_publish_settings (
service ,
user_google_email : str ,
form_id : str ,
publish_as_template : bool = False ,
require_authentication : bool = False
) -> str :
"""
Updates the publish settings of a form.
Args:
user_google_email (str): The user's Google email address. Required.
form_id (str): The ID of the form to update publish settings for.
publish_as_template (bool): Whether to publish as a template. Defaults to False.
require_authentication (bool): Whether to require authentication to view/submit. Defaults to False.
Returns:
str: Confirmation message of the successful publish settings update.
"""
logger . info ( f "[set_publish_settings] Invoked. Email: ' { user_google_email } ', Form ID: { form_id } " )
2025-06-18 16:29:35 -04:00
settings_body = {
"publishAsTemplate" : publish_as_template ,
"requireAuthentication" : require_authentication
}
2025-06-09 16:41:09 -04:00
2025-06-18 16:29:35 -04:00
await asyncio . to_thread (
service . forms () . setPublishSettings ( formId = form_id , body = settings_body ) . execute
)
2025-06-09 16:41:09 -04:00
2025-06-18 16:29:35 -04:00
confirmation_message = f "Successfully updated publish settings for form { form_id } for { user_google_email } . Publish as template: { publish_as_template } , Require authentication: { require_authentication } "
logger . info ( f "Publish settings updated successfully for { user_google_email } . Form ID: { form_id } " )
return confirmation_message
2025-06-09 16:41:09 -04:00
@server.tool ()
@require_google_service ( "forms" , "forms" )
2025-06-18 16:29:35 -04:00
@handle_http_errors ( "get_form_response" )
2025-07-14 15:08:03 +01:00
@retry_on_ssl_error ()
2025-06-09 16:41:09 -04:00
async def get_form_response (
service ,
user_google_email : str ,
form_id : str ,
response_id : str
) -> str :
"""
Get one response from the form.
Args:
user_google_email (str): The user's Google email address. Required.
form_id (str): The ID of the form.
response_id (str): The ID of the response to retrieve.
Returns:
str: Response details including answers and metadata.
"""
logger . info ( f "[get_form_response] Invoked. Email: ' { user_google_email } ', Form ID: { form_id } , Response ID: { response_id } " )
2025-06-18 16:29:35 -04:00
response = await asyncio . to_thread (
service . forms () . responses () . get ( formId = form_id , responseId = response_id ) . execute
)
response_id = response . get ( "responseId" , "Unknown" )
create_time = response . get ( "createTime" , "Unknown" )
last_submitted_time = response . get ( "lastSubmittedTime" , "Unknown" )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
answers = response . get ( "answers" , {})
answer_details = []
for question_id , answer_data in answers . items ():
question_response = answer_data . get ( "textAnswers" , {}) . get ( "answers" , [])
if question_response :
answer_text = ", " . join ([ ans . get ( "value" , "" ) for ans in question_response ])
answer_details . append ( f " Question ID { question_id } : { answer_text } " )
else :
answer_details . append ( f " Question ID { question_id } : No answer provided" )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
answers_text = " \n " . join ( answer_details ) if answer_details else " No answers found"
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
result = f """Form Response Details for { user_google_email } :
2025-06-09 16:41:09 -04:00
- Form ID: { form_id }
- Response ID: { response_id }
- Created: { create_time }
- Last Submitted: { last_submitted_time }
- Answers:
{ answers_text } """
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
logger . info ( f "Successfully retrieved response for { user_google_email } . Response ID: { response_id } " )
return result
2025-06-09 16:41:09 -04:00
@server.tool ()
@require_google_service ( "forms" , "forms" )
2025-06-18 16:29:35 -04:00
@handle_http_errors ( "list_form_responses" )
2025-07-14 15:08:03 +01:00
@retry_on_ssl_error ()
2025-06-09 16:41:09 -04:00
async def list_form_responses (
service ,
user_google_email : str ,
form_id : str ,
page_size : int = 10 ,
page_token : Optional [ str ] = None
) -> str :
"""
List a form's responses.
Args:
user_google_email (str): The user's Google email address. Required.
form_id (str): The ID of the form.
page_size (int): Maximum number of responses to return. Defaults to 10.
page_token (Optional[str]): Token for retrieving next page of results.
Returns:
str: List of responses with basic details and pagination info.
"""
logger . info ( f "[list_form_responses] Invoked. Email: ' { user_google_email } ', Form ID: { form_id } " )
2025-06-18 16:29:35 -04:00
params = {
"formId" : form_id ,
"pageSize" : page_size
}
if page_token :
params [ "pageToken" ] = page_token
responses_result = await asyncio . to_thread (
service . forms () . responses () . list ( ** params ) . execute
)
responses = responses_result . get ( "responses" , [])
next_page_token = responses_result . get ( "nextPageToken" )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
if not responses :
return f "No responses found for form { form_id } for { user_google_email } ."
response_details = []
for i , response in enumerate ( responses , 1 ):
response_id = response . get ( "responseId" , "Unknown" )
create_time = response . get ( "createTime" , "Unknown" )
last_submitted_time = response . get ( "lastSubmittedTime" , "Unknown" )
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
answers_count = len ( response . get ( "answers" , {}))
response_details . append (
f " { i } . Response ID: { response_id } | Created: { create_time } | Last Submitted: { last_submitted_time } | Answers: { answers_count } "
2025-06-09 16:41:09 -04:00
)
2025-06-18 16:29:35 -04:00
pagination_info = f " \n Next page token: { next_page_token } " if next_page_token else " \n No more pages."
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
result = f """Form Responses for { user_google_email } :
2025-06-09 16:41:09 -04:00
- Form ID: { form_id }
- Total responses returned: { len ( responses ) }
- Responses:
{ chr ( 10 ) . join ( response_details ) }{ pagination_info } """
2025-07-10 14:08:08 -04:00
2025-06-18 16:29:35 -04:00
logger . info ( f "Successfully retrieved { len ( responses ) } responses for { user_google_email } . Form ID: { form_id } " )
return result