How to Build a Document Editor Assistant with Arcade's Google Docs Toolkit

How to Build a Document Editor Assistant with Arcade's Google Docs Toolkit

Arcade.dev Team's avatar
Arcade.dev Team
OCTOBER 16, 2025
8 MIN READ
TUTORIALS
Rays decoration image
Ghost Icon

Building AI agents that can interact with documents represents a significant leap from basic chatbots to practical workflow automation. While AI models excel at generating and analyzing text, they typically lack the ability to directly create, edit, or manage documents in Google Workspace. This limitation prevents teams from automating document-heavy workflows that consume hours of manual work daily.

Arcade's Google Docs toolkit provides a pre-built set of tools for interacting with Google Docs, making it easy to build agents and AI apps that can create, update, list, and delete documents. This article walks through building a production-ready document editor assistant that can autonomously manage Google Docs on behalf of users, complete with secure authentication and multi-user support.

Overview of Arcade's Document Automation Architecture

AI agents need native API integrations, not hacky browser automation. Using official APIs and standard auth flows through code rather than trying to fake a web UI login is more robust and avoids the arms race of trying to dodge anti-bot measures. Arcade addresses this by providing a secure authentication layer between AI agents and Google Docs APIs.

The platform handles three critical challenges:

  • OAuth Challenges: Managing Google's OAuth 2.0 flow for document access
  • Token Management: Securely storing and refreshing access tokens for each user
  • Permission Scoping: Ensuring agents only access documents with appropriate permissions

This toolkit currently requires a self-hosted instance of Arcade, which provides complete control over authentication flows and enables enterprise features like custom domains and audit logging.

Setting Up Your Development Environment

Prerequisites

Before starting, ensure you have:

  • An Arcade.dev account with API key (Get started here)
  • Google Cloud Console project with OAuth 2.0 credentials
  • Python 3.10+ installed
  • Access to deploy a self-hosted Arcade Engine

Installing the Arcade SDK

Start by setting up your Python environment and installing necessary packages:

# Create and activate virtual environment
python -m venv arcade-env
source arcade-env/bin/activate  # On Windows: arcade-env\Scripts\activate

# Install Arcade packages
pip install arcade-mcp arcadepy

Configure your environment variables:

export ARCADE_API_KEY="your_arcade_api_key"
export GOOGLE_CLIENT_ID="your_google_client_id"
export GOOGLE_CLIENT_SECRET="your_google_client_secret"

Configuring the Google Docs Toolkit

Setting Up Google OAuth Provider

Configure Google auth provider for production with proper OAuth credentials and redirect URLs:

Access the Arcade Dashboard

To access the Arcade Cloud dashboard, go to api.arcade.dev/dashboard. If you are self-hosting, by default the dashboard will be available at http://localhost:9099/dashboard. Adjust the host and port number to match your environment.

  • Under the OAuth section of the Arcade Dashboard left-side menu, click Providers.
  • Click Add OAuth Provider in the top right corner.
  • Select the Included Providers tab at the top.
  • In the Provider dropdown, select Google.

Enter the provider details

  • Choose a unique ID for your provider (e.g. "my-google-provider").
  • Optionally enter a Description.
  • Enter the Client ID and Client Secret from your Google app.
  • Note the Redirect URL generated by Arcade. This must be added to your Google app's Authorized redirect URIs list.

Create the provider

Hit the Create button and the provider will be ready to be used.

Available Google Docs Tools

The toolkit currently includes four core tools: Google.GetDocumentById to retrieve documents, Google.InsertTextAtEndOfDocument to append text, Google.CreateBlankDocument to create new blank documents, and Google.CreateDocumentFromText to create documents with initial content.

Tool Specifications

Tool Name Description
GoogleDocs.WhoAmI Get comprehensive user profile and Google Docs environment information.
GoogleDocs.GetDocumentById Retrieve a Google Docs document by ID.
GoogleDocs.GetDocumentAsDocMD Retrieve a Google Docs document by ID in DocMD format with metadata tags.
GoogleDocs.EditDocument Edit a Google Docs document using natural language edit requests.
GoogleDocs.InsertTextAtEndOfDocument Insert text at the end of a Google Docs document.
GoogleDocs.CreateBlankDocument Create a new blank Google Docs document with a title.
GoogleDocs.CreateDocumentFromText Create a new Google Docs document with specified text content.
GoogleDocs.SearchDocuments Search for documents in the user's Google Drive.
GoogleDocs.SearchAndRetrieveDocuments Search and retrieve the contents of Google documents in the user's Google Drive.
GoogleDocs.ListDocumentComments List all comments on the specified Google Docs document.
GoogleDocs.CommentOnDocument Comment on a specific document by its ID.

Building the Document Editor Assistant

Core Assistant Implementation

Create a Python class that manages document operations with proper authentication:

from arcadepy import AsyncArcade
from typing import Dict, Any, Optional
import asyncio

class DocumentEditorAssistant:
    def __init__(self):
        self.client = AsyncArcade()

    async def authenticate_user(self, user_id: str):
        """Handle OAuth flow for Google Calendar access"""

        # Check if calendar tools require authorization
        auth_response = await self.arcade.tools.authorize(
            tool_name="Google.CreateBlankDocument",
            user_id=user_id
        )

        if auth_response.status != "completed":
            # User needs to complete OAuth flow
            print(f"Authorize at: {auth_response.url}")
            await self.arcade.auth.wait_for_completion(auth_response)

        return {"authenticated": True}

    async def create_document(self, user_id: str, title: str,
                            content: Optional[str] = None) -> Dict:
        """Create a new Google Doc with optional content"""

        # Ensure authentication
        auth_result = await self.authenticate_user(user_id)
        if auth_result.get("authorization_required"):
            return auth_result

        # Choose appropriate tool based on content
        if content:
            tool_name = "Google.CreateDocumentFromText"
            params = {"title": title, "text_content": content}
        else:
            tool_name = "Google.CreateBlankDocument"
            params = {"title": title}

        # Execute tool
        result = await self.client.tools.execute(
            tool_name=tool_name,
            input=params,
            user_id=user_id
        )

        return {"success": True, "document": result.output}

    async def append_to_document(self, user_id: str, document_id: str,
                                text: str) -> Dict:
        """Append text to an existing document"""

        # Ensure authentication
        auth_result = await self.authenticate_user(user_id)
        if auth_result.get("authorization_required"):
            return auth_result

        result = await self.client.tools.execute(
            tool_name="Google.InsertTextAtEndOfDocument",
            input={
                "document_id": document_id,
                "text_content": text
            },
            user_id=user_id
        )

        return {"success": True, "result": result.output}

    async def get_document(self, user_id: str, document_id: str) -> Dict:
        """Retrieve document content"""

        # Ensure authentication
        auth_result = await self.authenticate_user(user_id)
        if auth_result.get("authorization_required"):
            return auth_result

        result = await self.client.tools.execute(
            tool_name="Google.GetDocumentById",
            input={"document_id": document_id},
            user_id=user_id
        )

        return {"success": True, "content": result.output}

Implementing Document Processing Workflows

Extend the assistant with intelligent document processing capabilities:

class IntelligentDocumentProcessor(DocumentEditorAssistant):
    def __init__(self, llm_client):
        super().__init__()
        self.llm = llm_client

    async def summarize_and_create_report(self, user_id: str,
                                         source_doc_ids: list) -> Dict:
        """Read multiple documents and create a summary report"""

        # Gather source documents
        all_content = []
        for doc_id in source_doc_ids:
            doc_result = await self.get_document(user_id, doc_id)
            if doc_result.get("success"):
                all_content.append(doc_result["content"])

        # Generate summary using LLM
        summary_prompt = f"""
        Analyze these documents and create an executive summary:
        {' '.join(all_content)}

        Include key findings, recommendations, and action items.
        """

        summary = await self.llm.generate(summary_prompt)

        # Create new report document
        report_title = f"Executive Summary - {asyncio.get_event_loop().time()}"
        return await self.create_document(
            user_id=user_id,
            title=report_title,
            content=summary
        )

    async def collaborative_editing_session(self, user_id: str,
                                          document_id: str,
                                          instructions: str) -> Dict:
        """AI-assisted document editing based on instructions"""

        # Retrieve current content
        doc_result = await self.get_document(user_id, document_id)
        if not doc_result.get("success"):
            return doc_result

        current_content = doc_result["content"]

        # Generate improvements
        edit_prompt = f"""
        Current document content: {current_content}

        User instructions: {instructions}

        Provide the additional content to append to this document.
        """

        new_content = await self.llm.generate(edit_prompt)

        # Append improvements
        return await self.append_to_document(
            user_id=user_id,
            document_id=document_id,
            text=new_content
        )

Integration with AI Frameworks

LangGraph Integration

Arcade offers methods to convert tools into Zod schemas, which is essential since LangGraph defines tools using Zod. Integrate your document assistant with LangGraph:

from langchain_arcade import ArcadeToolManager

# Initialize tool manager
manager = ArcadeToolManager(api_key=arcade_api_key)

# Get Google Docs tools
google_docs_tools = manager.get_tools(
    tools=[
        "Google.CreateBlankDocument",
        "Google.CreateDocumentFromText",
        "Google.InsertTextAtEndOfDocument",
        "Google.GetDocumentById"
    ]
)

# Create LangGraph agent with document capabilities
from langgraph.graph import Graph

def create_document_agent():
    graph = Graph()

    # Add document processing nodes
    graph.add_node("authenticate", authenticate_user)
    graph.add_node("create_doc", create_document)
    graph.add_node("edit_doc", edit_document)
    graph.add_node("retrieve_doc", retrieve_document)

    # Define edges based on document workflow
    graph.add_edge("authenticate", "create_doc")
    graph.add_edge("create_doc", "edit_doc")
    graph.add_edge("edit_doc", "retrieve_doc")

    return graph.compile()

Google ADK Integration

For Google's AI Development Kit users:

from google_adk_arcade.tools import get_arcade_tools
from google.adk import Agent, Runner

async def create_google_adk_document_agent():
    client = AsyncArcade()

    # Get Google Docs tools
    docs_tools = await get_arcade_tools(
        client,
        tools=[
            "Google.CreateDocumentFromText",
            "Google.InsertTextAtEndOfDocument"
        ]
    )

    # Authorize tools for user
    user_id = "user@example.com"
    for tool in docs_tools:
        result = await client.tools.authorize(
            tool_name=tool.name,
            user_id=user_id
        )
        if result.status != "completed":
            print(f"Authorize at: {result.url}")
            await client.auth.wait_for_completion(result)

    # Create agent
    document_agent = Agent(
        model="gemini-2.0-flash",
        name="document_editor",
        instruction="Manage and edit Google Docs efficiently",
        tools=docs_tools
    )

    return document_agent

Handling Multi-User Authentication

Arcade provides an authorization system that handles OAuth 2.0, API keys, and user tokens needed by AI agents to access external services through tools. Implement secure multi-user document management:

class MultiUserDocumentManager:
    def __init__(self):
        self.arcade = AsyncArcade()

    async def initialize_user_tools(self, user_id: str):
        """Cache user-specific tool configurations"""

        # List available Google tools
        googledocs_tools = await self.arcade.tools.list(
            toolkit="GoogleDocs",
            limit=30,
            user_id=user_id
        )

        # collect the scopes
        scopes = set()
        for tool in googledocs_tools:
        if tool.requirements.authorization.oauth2.scopes:
            scopes |= set(tool.requirements.authorization.oauth2.scopes)
        # start auth
        auth_response = client.auth.start(user_id=user_id, scopes=list(scopes), provider="google")
        # show the url to the user if needed
        if auth_response.status != "complete":
            print(f"Please click here to authorize: {auth_response.url}") # Wait for the authorization to complete
            client.auth.wait_for_completion(auth_response)

    async def process_document_request(self, user_id: str,
                                      request: Dict) -> Dict:
        """Route document requests to appropriate tools"""

        tools = await self.initialize_user_tools(user_id)

        try:
            # Map request to tool
            tool_name = self.determine_tool(request)
            params = self.parse_parameters(request)

            result = await self.arcade.tools.execute(
                tool_name=tool_name,
                input=params,
                user_id=user_id
            )

            return {"success": True, "data": result.output}

        except Exception as e:
            if getattr(e, "type", "") == "authorization_required":
                return {
                    "success": False,
                    "authRequired": True,
                    "authUrl": getattr(e, "url", "")
                }
            raise

    def determine_tool(self, request: Dict) -> str:
        """Map request actions to Google Docs tools"""

        action_map = {
            "create": "Google.CreateDocumentFromText",
            "create_blank": "Google.CreateBlankDocument",
            "append": "Google.InsertTextAtEndOfDocument",
            "get": "Google.GetDocumentById"
        }

        return action_map.get(request.get("action"), "Google.GetDocumentById")

Advanced Use Cases

Automated Report Generation

Create a system that generates periodic reports from multiple documents:

async def generate_weekly_report(user_id: str, department: str):
    """Generate weekly department reports from various documents"""

    assistant = DocumentEditorAssistant()

    # Gather relevant documents
    doc_ids = await get_department_documents(department)

    # Process and analyze content
    analysis = await analyze_documents(assistant, user_id, doc_ids)

    # Create formatted report
    report_content = format_weekly_report(analysis)

    # Create and share report
    result = await assistant.create_document(
        user_id=user_id,
        title=f"Weekly Report - {department} - {datetime.now().strftime('%Y-%m-%d')}",
        content=report_content
    )

    return result

Template-Based Document Creation

Implement a template system for standardized documents:

class DocumentTemplateManager:
    def __init__(self):
        self.templates = {
            "meeting_notes": self.meeting_notes_template,
            "project_proposal": self.project_proposal_template,
            "status_report": self.status_report_template
        }

    async def create_from_template(self, user_id: str,
                                  template_name: str,
                                  variables: Dict) -> Dict:
        """Create document from predefined template"""

        if template_name not in self.templates:
            return {"error": "Template not found"}

        # Generate content from template
        content = self.templates[template_name](variables)

        # Create document
        assistant = DocumentEditorAssistant()
        return await assistant.create_document(
            user_id=user_id,
            title=variables.get("title", f"New {template_name}"),
            content=content
        )

    def meeting_notes_template(self, vars: Dict) -> str:
        """Generate meeting notes template"""

        return f"""
        Meeting Notes
        Date: {vars.get('date')}
        Attendees: {', '.join(vars.get('attendees', []))}

        Agenda:
        {vars.get('agenda')}

        Discussion Points:
        {vars.get('discussion')}

        Action Items:
        {vars.get('action_items')}

        Next Steps:
        {vars.get('next_steps')}
        """

Monitoring and Observability

Track document operations for compliance and optimization:

class DocumentOperationMonitor:
    def __init__(self):
        self.metrics = {
            "documents_created": 0,
            "documents_edited": 0,
            "documents_retrieved": 0,
            "auth_attempts": 0,
            "errors": 0
        }

    async def track_operation(self, user_id: str, operation: str,
                             success: bool):
        """Track document operations"""

        if success:
            self.metrics[f"documents_{operation}"] += 1
        else:
            self.metrics["errors"] += 1

        # Send to monitoring system
        await self.send_metrics({
            "timestamp": datetime.now().isoformat(),
            "user_id": self.hash_user_id(user_id),
            "operation": operation,
            "success": success,
            "service": "google_docs"
        })

    def generate_health_report(self) -> Dict:
        """Generate system health metrics"""

        total_ops = sum([
            self.metrics["documents_created"],
            self.metrics["documents_edited"],
            self.metrics["documents_retrieved"]
        ])

        return {
            "status": "healthy",
            "total_operations": total_ops,
            "error_rate": self.metrics["errors"] / max(total_ops, 1),
            "operations_breakdown": self.metrics
        }

Conclusion

Building a document editor assistant with Arcade's Google Docs toolkit transforms how teams interact with their documentation. Less than 30% of AI projects reach production because agents can't access real systems. This disconnect between AI's intelligence and its ability to access real data and take action is why AI initiatives get stuck.

Arcade bridges this gap by providing secure, authenticated access to Google Docs through a production-ready toolkit. The platform handles OAuth challenges, token management, and multi-user authentication, allowing developers to focus on building intelligent document workflows rather than authentication infrastructure.

Key takeaways for successful implementation:

  • Use self-hosted Arcade Engine for full control over document operations
  • Implement proper error handling and token refresh mechanisms
  • Cache frequently accessed documents to optimize performance
  • Monitor all operations for compliance and debugging
  • Design workflows that leverage AI for intelligent document processing

With these patterns and Arcade's infrastructure, your document editor assistant can move from proof-of-concept to production, automating document workflows that previously required manual intervention.

Start building your document assistant today by getting your Arcade API key and exploring the complete documentation. For additional support and examples, check out the GitHub repository or review the API reference.

SHARE THIS POST

RECENT ARTICLES

Rays decoration image
THOUGHT LEADERSHIP

How to Query Postgres from GPT-5 via Arcade (MCP)

Large language models need structured data access to provide accurate, data-driven insights. This guide demonstrates how to connect GPT-5 to PostgreSQL databases through Arcade's Model Context Protocol implementation, enabling secure database queries without exposing credentials directly to language models. Prerequisites Before implementing database connectivity, ensure you have: * Python 3.8 or higher installed * PostgreSQL database with connection credentials * Arcade API key (free t

Rays decoration image
THOUGHT LEADERSHIP

How to Connect GPT-5 to Slack with Arcade (MCP)

Building AI agents that interact with Slack requires secure OAuth authentication, proper token management, and reliable tool execution. This guide shows you how to connect GPT-5 to Slack using Arcade's Model Context Protocol (MCP) implementation, enabling your agents to send messages, read conversations, and manage channels with production-grade security. Prerequisites Before starting, ensure you have: * Arcade.dev account with API key * Python 3.10+ or Node.js 18+ installed * OpenAI A

Rays decoration image
THOUGHT LEADERSHIP

How to Build a GPT-5 Gmail Agent with Arcade (MCP)

Building AI agents that can access and act on Gmail data represents a significant challenge in production environments. This guide demonstrates how to build a fully functional Gmail agent using OpenAI's latest models through Arcade's Model Context Protocol implementation, enabling secure OAuth-based authentication and real-world email operations. Prerequisites Before starting, ensure you have: * Active Arcade.dev account with API key * Python 3.10 or higher installed * OpenAI API key w

Blog CTA Icon

Get early access to Arcade, and start building now.