Docker configuration for running the Google Docs MCP Server in a container.
This server provides Model Context Protocol (MCP) tools for interacting with Google Docs and Google Drive, enabling AI assistants like Claude to read, write, and manage your documents.
- Create blank documents - Create new Google Documents from scratch
- Import from Markdown - Create Google Docs with content imported from markdown, including support for:
- Headings (# to ######)
- Bold, italic, and bold+italic formatting
- Bullet and numbered lists
- Links and inline code
- Code blocks with monospace formatting
- Horizontal rules
- Read documents - Export content as text, JSON, or markdown
- Edit documents - Insert, append, and delete text
- Format text - Apply character and paragraph styles (bold, italic, colors, fonts, alignment, etc.)
- Manage structure - Insert tables, page breaks, and images
- Handle tabs - List and work with multi-tab documents
- Bulk operations - Execute multiple document operations in a single batched API call for 5-10x faster performance
- List, add, reply to, resolve, and delete comments on documents
- List, search, and get document metadata
- Create and manage folders
- Upload files and images
- Resource-based uploads - Upload files and images using resource identifiers from shared blob storage (for integration with other MCP servers)
- Docker and Docker Compose installed
- A Google Account
- Google Cloud Project with OAuth credentials
-
Go to the Google Cloud Console
-
Create or select a project:
- Click the project dropdown and select "NEW PROJECT"
- Name it (e.g., "Google Docs MCP") and click "CREATE"
-
Enable required APIs:
- Go to "APIs & Services" > "Library"
- Search for and enable Google Docs API
- Search for and enable Google Drive API
-
Configure OAuth Consent Screen:
- Go to "APIs & Services" > "OAuth consent screen"
- Select "External" and click "CREATE"
- Fill in:
- App name: e.g., "Google Docs MCP Server"
- User support email: your email
- Developer contact: your email
- Click "SAVE AND CONTINUE"
- Click "ADD OR REMOVE SCOPES" and add:
https://www.googleapis.com/auth/documentshttps://www.googleapis.com/auth/drive.file
- Click "UPDATE" then "SAVE AND CONTINUE"
- Add your Google email as a Test User
- Click "SAVE AND CONTINUE"
-
Create OAuth Credentials:
- Go to "APIs & Services" > "Credentials"
- Click "+ CREATE CREDENTIALS" > "OAuth client ID"
- Select "Desktop app" as the application type
- Name it (e.g., "MCP Docker Client")
- Click "CREATE"
- Download the JSON file
-
Create a
credentialsdirectory in this project:mkdir -p credentials
-
Copy your downloaded OAuth JSON file:
cp ~/Downloads/client_secret_*.json credentials/credentials.json
docker-compose buildThe first time you run the server, you need to authenticate with Google to generate a token. The authentication uses a loopback OAuth flow - the container starts a temporary web server on port 3000 to receive the OAuth callback.
Important: Create an empty token.json file before running if it doesn't exist:
touch credentials/token.jsonRun the container with port 3000 exposed:
docker run -it --rm \
-p 3000:3000 \
-v $(pwd)/credentials:/workspace/credentials \
workspace-google-docs-mcp:latestNote: On Windows, use full paths instead of $(pwd):
docker run -it --rm ^
-p 3000:3000 ^
-v C:/path/to/google-docs-mcp/credentials:/workspace/credentials ^
workspace-google-docs-mcp:latest- The server will output an authorization URL
- Copy the URL and open it in your browser
- Log in with your Google account (the one added as a Test User)
- Click "Allow" to grant permissions
- Google will redirect to
http://localhost:3000- the container captures this automatically - You'll see "Authentication Successful!" in your browser
- The
token.jsonfile will be saved to yourcredentials/directory - Press Ctrl+C to stop the container
Alternatively, use docker-compose with the auth profile:
docker-compose --profile auth run --rm -p 3000:3000 authdocker-compose up -dThe MCP server is now running and ready to accept connections.
Add this to your Claude Desktop config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Run the MCP server in a Docker container. This requires mounting the credentials and token files:
{
"mcpServers": {
"google-docs": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-p",
"3000:3000",
"-v",
"C:/path/to/google-docs-mcp/credentials:/workspace/credentials",
"-v",
"blob-storage:/mnt/blob-storage",
"-e",
"BLOB_STORAGE_ROOT=/mnt/blob-storage",
"-e",
"BLOB_STORAGE_MAX_SIZE_MB=100",
"-e",
"BLOB_STORAGE_TTL_HOURS=24",
"workspace-google-docs-mcp:latest"
]
}
}
}Configuration notes:
-p 3000:3000- Exposes port 3000 for OAuth token refresh callbacks- The first
-vmount maps your localcredentials/directory (containing bothcredentials.jsonandtoken.json) to/workspace/credentials/in the container - The second
-vmount creates a shared blob storage volume for resource-based file uploads (optional, only needed if using resource-based upload features) - The
-eflags set environment variables for blob storage configuration (optional, defaults shown)
Note: Adjust the path (C:/path/to/google-docs-mcp/credentials) to match your local credentials directory. On Linux/macOS, use Unix-style paths (e.g., /home/user/google-docs-mcp/credentials).
Optional: Remove the blob storage volume mount and environment variables if you don't need resource-based upload features.
If you prefer to keep the container running in the background with docker-compose up -d:
{
"mcpServers": {
"google-docs": {
"command": "docker",
"args": [
"exec",
"-i",
"google-docs-mcp-server",
"uv",
"run",
"google-docs-mcp"
]
}
}
}Note: The container must be running before starting Claude Desktop.
Restart Claude Desktop after updating the configuration.
.
├── Dockerfile # Docker image definition (dev + production)
├── docker-compose.yml # Docker Compose for production
├── docker-compose.devcontainer.yml # Docker Compose for VS Code devcontainer
├── .devcontainer/
│ └── devcontainer.json # VS Code devcontainer configuration
├── src/
│ └── google_docs_mcp/ # Python source code
│ ├── server.py # Main MCP server entry point
│ ├── auth.py # OAuth2 authentication (loopback flow)
│ └── api/ # API modules (documents, comments, drive)
├── tests/ # Test files
├── credentials/ # Your Google OAuth credentials (gitignored)
│ ├── credentials.json # OAuth client credentials
│ └── token.json # OAuth access token (generated after auth)
├── pyproject.toml # Python project configuration
├── .gitignore # Git ignore rules
└── README.md # This file
The project includes a devcontainer configuration for VS Code:
- Open the project in VS Code
- When prompted, click "Reopen in Container" (or use Command Palette: "Dev Containers: Reopen in Container")
- VS Code will build the container and install dependencies automatically
The devcontainer includes:
- Python 3.12 with uv package manager
- Docker CLI (Docker-outside-of-Docker support)
- Node.js 20 and Claude Code CLI
- VS Code extensions: Python, Pylance, debugpy, Ruff, Claude Code
- Port 3000 forwarded for OAuth loopback callback
# Install dependencies
uv sync
# Run the server
uv run google-docs-mcp
# Run tests
uv run pytest| Command | Description |
|---|---|
docker-compose build |
Build the Docker image |
docker-compose up -d |
Start the server in background |
docker-compose down |
Stop the server |
docker-compose logs -f |
View server logs |
docker-compose --profile auth run --rm auth |
Run auth service interactively |
This MCP server integrates with mcp_mapped_resource_lib to support resource-based file uploads. This enables efficient file sharing between multiple MCP servers through a shared Docker volume.
Traditional MCP file transfers require encoding files as base64 and passing them through the MCP protocol, which can be inefficient for large files. With resource-based uploads:
- Other MCP servers upload files to a shared blob storage volume and return a resource identifier (e.g.,
blob://1733437200-a3f9d8c2b1e4f6a7.png) - This server can directly access those files via the resource identifier and upload them to Google Drive
- No file data is transferred through the MCP protocol - only the small resource identifier
upload_image_to_drive_from_resource- Upload an image to Drive using a resource IDupload_file_to_drive_from_resource- Upload any file to Drive using a resource IDinsert_image_from_resource- Insert an image into a document using a resource ID
Add a shared volume for blob storage in your docker-compose.yml:
services:
google-docs-mcp:
# ... existing config ...
volumes:
- ./credentials:/workspace/credentials
- blob-storage:/mnt/blob-storage # Add this line
environment:
- PYTHONUNBUFFERED=1
- BLOB_STORAGE_ROOT=/mnt/blob-storage # Required
- BLOB_STORAGE_MAX_SIZE_MB=100 # Optional: max file size (default: 100)
- BLOB_STORAGE_TTL_HOURS=24 # Optional: time-to-live (default: 24)
volumes:
blob-storage:
driver: localConfiguration Options:
BLOB_STORAGE_ROOT- Required. Path to the blob storage directoryBLOB_STORAGE_MAX_SIZE_MB- Optional. Maximum file size in MB (default: 100)BLOB_STORAGE_TTL_HOURS- Optional. Time-to-live for blobs in hours (default: 24). Blobs older than this will be automatically cleaned up.
When using resource-based uploads, update your claude_desktop_config.json to mount the blob storage volume:
{
"mcpServers": {
"google-docs": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-p",
"3000:3000",
"-v",
"C:/path/to/google-docs-mcp/credentials:/workspace/credentials",
"-v",
"blob-storage:/mnt/blob-storage",
"-e",
"BLOB_STORAGE_ROOT=/mnt/blob-storage",
"-e",
"BLOB_STORAGE_MAX_SIZE_MB=100",
"-e",
"BLOB_STORAGE_TTL_HOURS=24",
"workspace-google-docs-mcp:latest"
]
}
}
}Note: Replace C:/path/to/google-docs-mcp/credentials with your actual credentials path. On Linux/macOS, use Unix-style paths.
Configuration:
- Adjust
BLOB_STORAGE_MAX_SIZE_MBto set the maximum file size (in MB) - Adjust
BLOB_STORAGE_TTL_HOURSto control how long blobs are retained before automatic cleanup
Other MCP servers can use the same volume. Example configuration:
{
"mcpServers": {
"google-docs": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-p", "3000:3000",
"-v", "C:/path/to/google-docs-mcp/credentials:/workspace/credentials",
"-v", "blob-storage:/mnt/blob-storage",
"-e", "BLOB_STORAGE_ROOT=/mnt/blob-storage",
"-e", "BLOB_STORAGE_MAX_SIZE_MB=100",
"-e", "BLOB_STORAGE_TTL_HOURS=24",
"workspace-google-docs-mcp:latest"
]
},
"other-mcp-server": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "blob-storage:/mnt/blob-storage:ro",
"other-mcp-server:latest"
]
}
}
}Important: The volume name (blob-storage) must be the same across all MCP servers that need to share resources.
With another MCP server that has blob upload capabilities:
-
Other MCP server uploads a file to blob storage:
User: Upload this image to blob storage Other Server: Uploaded! Resource ID: blob://1733437200-a3f9d8c2b1e4f6a7.png -
This server uploads to Google Drive using the resource ID:
User: Upload that image to my Google Drive using resource blob://1733437200-a3f9d8c2b1e4f6a7.png Google Docs Server: Successfully uploaded image "photo.png" from resource blob://1733437200-a3f9d8c2b1e4f6a7.png
Resource identifiers follow the pattern: blob://TIMESTAMP-HASH.EXT
TIMESTAMP- Unix timestamp when the file was uploadedHASH- SHA256 hash (truncated) for uniquenessEXT- Original file extension
Example: blob://1733437200-a3f9d8c2b1e4f6a7.png
The bulk_update_google_doc tool allows you to execute multiple document operations in a single batched API call, providing 5-10x performance improvement over individual tool calls.
When making multiple changes to a document (formatting, inserting content, adding tables, etc.), each individual tool call requires a separate network round-trip to Google's API. This creates significant latency:
Before (individual calls):
- 10 formatting operations = 10 API calls = ~5-10 seconds
After (bulk operations):
- 10 formatting operations = 1 batched API call = ~0.5-1 second
The bulk tool supports all document manipulation operations:
- insert_text - Insert text at a specific index
- delete_range - Delete content in a range
- apply_text_style - Apply character-level formatting (bold, italic, colors, etc.)
- apply_paragraph_style - Apply paragraph-level formatting (alignment, headings, spacing, etc.)
- insert_table - Insert a table
- insert_page_break - Insert a page break
- insert_image_from_url - Insert an image from a URL
Here's an example of creating a formatted document with a title, introduction, table, and styled text in a single API call:
{
"document_id": "your-document-id-here",
"operations": [
{
"type": "insert_text",
"text": "Project Status Report\n\n",
"index": 1
},
{
"type": "apply_paragraph_style",
"start_index": 1,
"end_index": 23,
"named_style_type": "HEADING_1",
"alignment": "CENTER"
},
{
"type": "insert_text",
"text": "Executive Summary\n\n",
"index": 23
},
{
"type": "apply_paragraph_style",
"start_index": 23,
"end_index": 42,
"named_style_type": "HEADING_2"
},
{
"type": "insert_text",
"text": "This report provides an overview of project progress and key metrics.\n\n",
"index": 42
},
{
"type": "insert_text",
"text": "Key Metrics\n\n",
"index": 113
},
{
"type": "apply_paragraph_style",
"start_index": 113,
"end_index": 126,
"named_style_type": "HEADING_2"
},
{
"type": "insert_table",
"rows": 4,
"columns": 3,
"index": 126
},
{
"type": "insert_text",
"text": "\n\nConclusion\n",
"index": 127
},
{
"type": "apply_text_style",
"text_to_find": "Conclusion",
"match_instance": 1,
"bold": true,
"font_size": 14
}
]
}Each operation is a dictionary with a type field and operation-specific parameters:
text(string): Text to insertindex(integer): Position to insert at (1-based)tab_id(string, optional): Tab ID for multi-tab documents
start_index(integer): Start of range (1-based, inclusive)end_index(integer): End of range (1-based, exclusive)tab_id(string, optional): Tab ID
Range targeting (choose one):
start_indexandend_index(integers): Direct range specificationtext_to_find(string) andmatch_instance(integer): Find specific text
Style properties:
bold,italic,underline,strikethrough(boolean)font_size(float): Font size in pointsfont_family(string): Font name (e.g., "Arial", "Times New Roman")foreground_color,background_color(string): Hex color (e.g., "#FF0000")link_url(string): URL for hyperlink
Range targeting (choose one):
start_indexandend_index(integers): Direct range specificationtext_to_find(string) andmatch_instance(integer): Find text, format its paragraphindex_within_paragraph(integer): Format paragraph containing this index
Style properties:
alignment(string): "START", "END", "CENTER", "JUSTIFIED"indent_start,indent_end(float): Indentation in pointsspace_above,space_below(float): Spacing in pointsnamed_style_type(string): "NORMAL_TEXT", "HEADING_1" through "HEADING_6", "TITLE", "SUBTITLE"keep_with_next(boolean): Keep paragraph with next
rows(integer): Number of rowscolumns(integer): Number of columnsindex(integer): Position to insert (1-based)
index(integer): Position to insert (1-based)
image_url(string): Publicly accessible image URLindex(integer): Position to insert (1-based)width,height(float, optional): Dimensions in points
- Maximum 500 operations per call (automatically batched into groups of 50 for Google API)
- Operations are executed in the order provided
- All operations must be valid before any are executed (fail-fast validation)
- Group related operations: Combine all changes to a document in a single bulk call
- Use index-based targeting when possible: Text-finding operations require fetching the document first
- Order matters: Structure your operations to account for index changes (e.g., insert text before applying formatting to that text)
- Never commit
credentials.jsonortoken.jsonto version control - The
.gitignorefile is configured to exclude these files - Treat these files like passwords - they grant access to your Google account
- The token.json file allows the server to access your Google account without re-authentication
"credentials.json not found" error:
- Ensure you've placed
credentials.jsonin thecredentials/directory - Check the file is named exactly
credentials.json
Authentication fails:
- Verify you added your email as a Test User in Google Cloud Console
- Ensure you enabled both Google Docs API and Google Drive API
Docker container won't start:
- Check that both
credentials.jsonandtoken.jsonexist incredentials/ - Run
docker-compose logsto see error messages
Claude Desktop shows "Failed to connect":
- Ensure the container is running:
docker-compose ps - Verify the container name is
google-docs-mcp-server - Try restarting Claude Desktop
This Docker configuration is provided under the MIT License. The underlying google-docs-mcp server is licensed separately.