google-workspace-mcp 1.0.5__tar.gz → 1.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/.gitignore +2 -1
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/PKG-INFO +2 -2
- google_workspace_mcp-1.1.5/docs/APOSTROPHE_HANDLING_SOLUTION.md +41 -0
- google_workspace_mcp-1.1.5/docs/CRITICAL_MCP_TOOL_DESCRIPTION_FIX.md +91 -0
- google_workspace_mcp-1.1.5/docs/DRIVE_SEARCH_IMPROVEMENTS.md +315 -0
- google_workspace_mcp-1.1.5/docs/TEXTRANGES_FORMATTING_FIX.md +171 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/pyproject.toml +2 -2
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/__main__.py +6 -5
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/drive.py +39 -12
- google_workspace_mcp-1.1.5/src/google_workspace_mcp/services/slides.py +2935 -0
- google_workspace_mcp-1.1.5/src/google_workspace_mcp/tools/add_image.py +1781 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/tools/calendar.py +12 -17
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/tools/docs_tools.py +24 -32
- google_workspace_mcp-1.1.5/src/google_workspace_mcp/tools/drive.py +469 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/tools/gmail.py +27 -36
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/tools/sheets_tools.py +18 -25
- google_workspace_mcp-1.1.5/src/google_workspace_mcp/tools/slides.py +1197 -0
- google_workspace_mcp-1.1.5/src/google_workspace_mcp/utils/unit_conversion.py +201 -0
- google_workspace_mcp-1.0.5/src/google_workspace_mcp/services/slides.py +0 -959
- google_workspace_mcp-1.0.5/src/google_workspace_mcp/tools/drive.py +0 -226
- google_workspace_mcp-1.0.5/src/google_workspace_mcp/tools/slides.py +0 -478
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/README.md +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/app.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/auth/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/auth/gauth.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/config.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/prompts/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/prompts/calendar.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/prompts/drive.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/prompts/gmail.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/prompts/slides.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/resources/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/resources/calendar.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/resources/drive.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/resources/gmail.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/resources/sheets_resources.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/resources/slides.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/base.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/calendar.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/docs_service.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/gmail.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/services/sheets_service.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/tools/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/utils/__init__.py +0 -0
- {google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/utils/markdown_slides.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: google-workspace-mcp
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1.5
|
4
4
|
Summary: MCP server for Google Workspace integration
|
5
5
|
Author-email: Arclio Team <info@arclio.com>
|
6
6
|
License: MIT
|
@@ -11,7 +11,7 @@ Requires-Dist: google-auth-httplib2>=0.1.0
|
|
11
11
|
Requires-Dist: google-auth-oauthlib>=1.0.0
|
12
12
|
Requires-Dist: google-auth>=2.22.0
|
13
13
|
Requires-Dist: markdown>=3.5.0
|
14
|
-
Requires-Dist: markdowndeck>=0.1.
|
14
|
+
Requires-Dist: markdowndeck>=0.1.5
|
15
15
|
Requires-Dist: mcp>=1.7.0
|
16
16
|
Requires-Dist: python-dotenv>=1.0.0
|
17
17
|
Requires-Dist: pytz>=2023.3
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Automatic Apostrophe Handling Solution
|
2
|
+
|
3
|
+
## Problem Solved
|
4
|
+
LLMs frequently fail with apostrophe-containing queries like "John's Documents" due to Google Drive API escaping requirements.
|
5
|
+
|
6
|
+
## Solution
|
7
|
+
**Automatic apostrophe escaping** implemented in all search tools - no manual escaping required.
|
8
|
+
|
9
|
+
## Implementation
|
10
|
+
Added automatic `.replace("'", "\\'")` to all search queries and folder names in:
|
11
|
+
|
12
|
+
✅ **`drive_search_files`** - Main file search
|
13
|
+
✅ **`drive_find_folder_by_name`** - Folder search with optional file search
|
14
|
+
✅ **`drive_search_files_in_folder`** - Search within specific folder
|
15
|
+
|
16
|
+
## Before vs After
|
17
|
+
|
18
|
+
### Before (Error-prone)
|
19
|
+
```python
|
20
|
+
# LLM had to manually escape apostrophes
|
21
|
+
"John\'s Documents" # Often forgotten, causing failures
|
22
|
+
```
|
23
|
+
|
24
|
+
### After (Automatic)
|
25
|
+
```python
|
26
|
+
# LLM uses natural language
|
27
|
+
"John's Documents" # Automatically converted to "John\'s Documents"
|
28
|
+
```
|
29
|
+
|
30
|
+
## Benefits
|
31
|
+
1. **Robust** - No more apostrophe-related failures
|
32
|
+
2. **User-friendly** - Natural language queries work immediately
|
33
|
+
3. **Reliable** - Eliminates retry loops from escaping mistakes
|
34
|
+
4. **Concise** - Simple `.replace()` handles all cases
|
35
|
+
|
36
|
+
## Test Cases Now Working
|
37
|
+
- "John's Documents" ✅
|
38
|
+
- "Today's Reports" ✅
|
39
|
+
- "Children's Folder" ✅
|
40
|
+
|
41
|
+
The tools now handle apostrophes transparently - LLMs can use natural language without escaping concerns.
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# CRITICAL: MCP Tool Description Visibility Fix
|
2
|
+
|
3
|
+
## The Problem
|
4
|
+
|
5
|
+
LLMs using MCP tools only see:
|
6
|
+
1. **The `description` parameter from the `@mcp.tool` decorator**
|
7
|
+
2. **Parameter names and types** (but NOT docstring parameter descriptions)
|
8
|
+
|
9
|
+
They **DO NOT** see the detailed docstrings with usage examples and critical information like apostrophe escaping requirements.
|
10
|
+
|
11
|
+
## Root Cause Discovery
|
12
|
+
|
13
|
+
Through research of FastMCP documentation, we discovered:
|
14
|
+
|
15
|
+
> When this tool is registered, FastMCP automatically:
|
16
|
+
> - Uses the function name as the tool name
|
17
|
+
> - **Uses the function's docstring as the tool description**
|
18
|
+
> - Generates an input schema based on the function's parameters and type annotations
|
19
|
+
|
20
|
+
**CRITICAL**: `description`: Provides the description exposed via MCP. **If set, the function's docstring is ignored for this purpose.**
|
21
|
+
|
22
|
+
## The Solution
|
23
|
+
|
24
|
+
**Remove the `description` parameter from `@mcp.tool` decorators** and put critical information directly in the docstring.
|
25
|
+
|
26
|
+
### Before (LLMs can't see apostrophe escaping info):
|
27
|
+
```python
|
28
|
+
@mcp.tool(
|
29
|
+
name="drive_search_files",
|
30
|
+
description="Search for files in Google Drive with optional shared drive support.", # ← LLM sees this
|
31
|
+
)
|
32
|
+
async def drive_search_files(query: str) -> dict[str, Any]:
|
33
|
+
"""
|
34
|
+
CRITICAL: If search queries contain apostrophes ('), you MUST escape them with backslash (\').
|
35
|
+
Example: "Frank's RedHot" → "Frank\'s RedHot"
|
36
|
+
# ↑ LLM NEVER SEES THIS because description parameter is provided
|
37
|
+
"""
|
38
|
+
```
|
39
|
+
|
40
|
+
### After (LLMs can see critical info):
|
41
|
+
```python
|
42
|
+
@mcp.tool(
|
43
|
+
name="drive_search_files",
|
44
|
+
# ← No description parameter, so docstring is used
|
45
|
+
)
|
46
|
+
async def drive_search_files(query: str) -> dict[str, Any]:
|
47
|
+
"""
|
48
|
+
Search for files in Google Drive with optional shared drive support.
|
49
|
+
|
50
|
+
CRITICAL: If search queries contain apostrophes ('), you MUST escape them with backslash (\').
|
51
|
+
Example: "Frank's RedHot" → "Frank\'s RedHot"
|
52
|
+
# ↑ LLM NOW SEES THIS because it's in the docstring and no description param is provided
|
53
|
+
"""
|
54
|
+
```
|
55
|
+
|
56
|
+
## Implementation Status
|
57
|
+
|
58
|
+
✅ **Fixed tools:**
|
59
|
+
- `drive_search_files` - Main search with critical apostrophe escaping info
|
60
|
+
- `drive_find_folder_by_name` - Folder search with escaping info
|
61
|
+
- `drive_search_files_in_folder` - Folder-specific search with escaping info
|
62
|
+
- `drive_get_folder_info` - Folder metadata tool
|
63
|
+
- `drive_list_shared_drives` - Shared drives discovery
|
64
|
+
|
65
|
+
## Key Success Factors
|
66
|
+
|
67
|
+
1. **Docstring is now visible to LLMs** - Critical escaping information is accessible
|
68
|
+
2. **Clear examples** - "Frank's RedHot" → "Frank\'s RedHot"
|
69
|
+
3. **Prominent placement** - CRITICAL warnings at the top of descriptions
|
70
|
+
4. **Multiple tools covered** - Consistent messaging across all search tools
|
71
|
+
|
72
|
+
## Next Steps
|
73
|
+
|
74
|
+
1. **Test with real LLM clients** to verify they now see the escaping requirements
|
75
|
+
2. **Apply same pattern** to any other tools that need critical usage information
|
76
|
+
3. **Monitor for improved success rates** with apostrophe-containing queries
|
77
|
+
|
78
|
+
## Technical Notes
|
79
|
+
|
80
|
+
- FastMCP automatically uses docstrings when no `description` parameter is provided
|
81
|
+
- This pattern works for all MCP decorators: `@mcp.tool`, `@mcp.resource`, `@mcp.prompt`
|
82
|
+
- The docstring becomes the primary interface documentation for LLMs
|
83
|
+
- Parameter type hints and names are still automatically extracted for schema generation
|
84
|
+
|
85
|
+
## Validation
|
86
|
+
|
87
|
+
To validate this fix works:
|
88
|
+
1. Use an MCP client to connect to the tools
|
89
|
+
2. Call `list_tools()` to see tool descriptions
|
90
|
+
3. Verify the docstring content (including CRITICAL warnings) appears in tool descriptions
|
91
|
+
4. Test with "Frank's RedHot" queries to confirm LLMs now know to escape apostrophes
|
@@ -0,0 +1,315 @@
|
|
1
|
+
# Google Drive Search MCP Tool Improvements
|
2
|
+
|
3
|
+
## Executive Summary
|
4
|
+
|
5
|
+
This document outlines comprehensive improvements made to the Google Drive search functionality in the MCP (Model Context Protocol) tools. The improvements address critical issues with shared folder access, query escaping, tool duplication, and search reliability that were causing frequent failures when searching for files in shared Google Drive folders.
|
6
|
+
|
7
|
+
## Original Problems
|
8
|
+
|
9
|
+
### 1. Shared Folder Search Failures
|
10
|
+
The original implementation had severe limitations when searching for files in shared folders:
|
11
|
+
|
12
|
+
- **Root Cause**: Used `corpora=user` which only searched personal files, completely excluding shared drives and folders
|
13
|
+
- **Impact**: Users couldn't find files in shared folders despite having proper access
|
14
|
+
- **Error Pattern**: Queries would return empty results or fail entirely when targeting shared content
|
15
|
+
|
16
|
+
### 2. Query Escaping Issues
|
17
|
+
Special characters in folder and file names caused API failures:
|
18
|
+
|
19
|
+
- **Primary Issue**: Apostrophes in names like "John's Documents" caused invalid query syntax
|
20
|
+
- **Failed Query Example**:
|
21
|
+
```
|
22
|
+
"John's Documents" → Invalid Value error from Google Drive API
|
23
|
+
```
|
24
|
+
- **Secondary Issue**: Double-escaping of quotes when users provided quoted input
|
25
|
+
|
26
|
+
### 3. Tool Architecture Problems
|
27
|
+
The original design had several architectural issues:
|
28
|
+
|
29
|
+
- **Code Duplication**: Separate tools for finding folders vs. searching within folders
|
30
|
+
- **Limited Functionality**: No way to search by folder name without knowing folder ID
|
31
|
+
- **Inconsistent Parameters**: Different tools had different parameter patterns
|
32
|
+
|
33
|
+
### 4. Inclusion of Trashed Items
|
34
|
+
Search results included deleted/trashed files and folders, cluttering results with irrelevant content.
|
35
|
+
|
36
|
+
## Phase 1: Core Search Engine Improvements
|
37
|
+
|
38
|
+
### Problem Analysis
|
39
|
+
Initial testing revealed that basic queries were failing with errors like:
|
40
|
+
```
|
41
|
+
Error: <HttpError 400> "Invalid Value" - Details: "invalid location: q parameter"
|
42
|
+
```
|
43
|
+
|
44
|
+
### Solution: Enhanced Corpora Support
|
45
|
+
- **Changed Default Behavior**: Switched from `corpora=user` to `corpora=allDrives`
|
46
|
+
- **Added Flexibility**: Introduced `include_shared_drives` parameter (default: `True`)
|
47
|
+
- **Maintained Compatibility**: Users can still search only personal files with `include_shared_drives=False`
|
48
|
+
|
49
|
+
### Key Improvements:
|
50
|
+
1. **Automatic Shared Drive Access**: Search now includes shared drives and folders by default
|
51
|
+
2. **Proper API Parameters**: Added `supportsAllDrives=True` and `includeItemsFromAllDrives=True`
|
52
|
+
3. **Extended Field Support**: Added `parents` field to file metadata for better hierarchy understanding
|
53
|
+
|
54
|
+
## Phase 2: Query Escaping and Special Character Handling
|
55
|
+
|
56
|
+
### Problem Analysis
|
57
|
+
User reported multiple failures with folder names containing apostrophes:
|
58
|
+
|
59
|
+
**Failed Queries:**
|
60
|
+
```
|
61
|
+
Input: "John's Documents" → API Error: Invalid Value
|
62
|
+
Input: 'John's Documents' → API Error: Invalid Value
|
63
|
+
Input: John's Documents → API Error: Invalid Value
|
64
|
+
```
|
65
|
+
|
66
|
+
**Working Query:**
|
67
|
+
```
|
68
|
+
Input: John\'s Documents → Success ✓
|
69
|
+
```
|
70
|
+
|
71
|
+
### Solution Evolution
|
72
|
+
|
73
|
+
#### Initial Approach (Complex Escaping)
|
74
|
+
- Attempted automatic escaping with regex parsing
|
75
|
+
- Tried to intelligently distinguish between structural quotes and content quotes
|
76
|
+
- **Result**: Added complexity without solving the core problem reliably
|
77
|
+
|
78
|
+
#### Final Approach (Simplified + Documentation)
|
79
|
+
- **Removed Complex Logic**: Eliminated unreliable automatic escaping
|
80
|
+
- **Clear Documentation**: Added explicit guidance for manual escaping
|
81
|
+
- **User-Friendly Examples**: Provided clear before/after examples in docstrings
|
82
|
+
|
83
|
+
### Implementation:
|
84
|
+
1. **Basic Quote Cleaning**: Only removes surrounding double quotes if present
|
85
|
+
2. **Manual Escaping Requirement**: Users must escape apostrophes with backslash (`'` → `\'`)
|
86
|
+
3. **Comprehensive Documentation**: All tools now include escaping examples and requirements
|
87
|
+
|
88
|
+
## Phase 3: Tool Consolidation and Architecture Cleanup
|
89
|
+
|
90
|
+
### Problem Analysis
|
91
|
+
The codebase had two nearly identical tools with overlapping functionality:
|
92
|
+
|
93
|
+
- `drive_find_folders_by_name`: Found folders by name
|
94
|
+
- `drive_search_in_folder_by_name`: Found folders by name AND searched for files within them
|
95
|
+
|
96
|
+
This created:
|
97
|
+
- **Code Duplication**: Nearly identical folder search logic
|
98
|
+
- **User Confusion**: Unclear which tool to use for different scenarios
|
99
|
+
- **Maintenance Burden**: Changes needed to be applied to multiple tools
|
100
|
+
|
101
|
+
### Solution: Unified Tool Design
|
102
|
+
**Consolidated into single tool**: `drive_find_folder_by_name`
|
103
|
+
|
104
|
+
#### New Parameter Structure:
|
105
|
+
- `folder_name`: Name of folder to search for
|
106
|
+
- `include_files`: Boolean controlling whether to search for files within found folder
|
107
|
+
- `file_query`: Optional query for filtering files (only used when `include_files=True`)
|
108
|
+
- `page_size`: Maximum results to return
|
109
|
+
- `shared_drive_id`: Optional shared drive constraint
|
110
|
+
|
111
|
+
#### Usage Patterns:
|
112
|
+
```python
|
113
|
+
# Just find folders (old drive_find_folders_by_name behavior)
|
114
|
+
drive_find_folder_by_name("John\'s Documents", include_files=False)
|
115
|
+
|
116
|
+
# Find folder and list all files (old drive_search_in_folder_by_name behavior)
|
117
|
+
drive_find_folder_by_name("John\'s Documents", include_files=True)
|
118
|
+
|
119
|
+
# Find folder and search for specific files (new enhanced capability)
|
120
|
+
drive_find_folder_by_name("John\'s Documents", include_files=True, file_query="budget")
|
121
|
+
```
|
122
|
+
|
123
|
+
## Phase 4: Trashed Item Filtering
|
124
|
+
|
125
|
+
### Problem Analysis
|
126
|
+
Search results included deleted/trashed files and folders, creating noise in results and potentially confusing users.
|
127
|
+
|
128
|
+
### Solution: Automatic Trash Filtering
|
129
|
+
**Added `trashed=false` to all search queries by default**
|
130
|
+
|
131
|
+
#### Implementation Across Tools:
|
132
|
+
1. **Folder Searches**: `name contains 'folder' and mimeType='application/vnd.google-apps.folder' and trashed=false`
|
133
|
+
2. **File Searches**: `'folderId' in parents and trashed=false`
|
134
|
+
3. **General Searches**: Added optional `include_trashed` parameter (default: `False`)
|
135
|
+
|
136
|
+
#### Benefits:
|
137
|
+
- **Cleaner Results**: Only active files and folders appear in searches
|
138
|
+
- **Expected Behavior**: Matches standard file browser behavior
|
139
|
+
- **Performance**: Potentially faster searches with fewer results
|
140
|
+
- **Flexibility**: Main search tool allows including trash when needed
|
141
|
+
|
142
|
+
## Final Tool Architecture
|
143
|
+
|
144
|
+
### Core Tools
|
145
|
+
|
146
|
+
#### 1. `drive_search_files` (Enhanced)
|
147
|
+
**Purpose**: General file search across Google Drive
|
148
|
+
**Key Improvements**:
|
149
|
+
- Default shared drive inclusion
|
150
|
+
- Trash filtering with optional inclusion
|
151
|
+
- Proper apostrophe escaping guidance
|
152
|
+
- Support for complex Google Drive API queries
|
153
|
+
|
154
|
+
**Example Usage**:
|
155
|
+
```python
|
156
|
+
# Search across all accessible drives (default)
|
157
|
+
drive_search_files("quarterly report")
|
158
|
+
|
159
|
+
# Search only personal files
|
160
|
+
drive_search_files("quarterly report", include_shared_drives=False)
|
161
|
+
|
162
|
+
# Include trashed files in search
|
163
|
+
drive_search_files("old document", include_trashed=True)
|
164
|
+
|
165
|
+
# Search with proper apostrophe escaping
|
166
|
+
drive_search_files("John\'s presentation")
|
167
|
+
```
|
168
|
+
|
169
|
+
#### 2. `drive_find_folder_by_name` (New Unified Tool)
|
170
|
+
**Purpose**: Find folders by name with optional file search within
|
171
|
+
**Key Features**:
|
172
|
+
- Unified folder discovery and file search
|
173
|
+
- Configurable depth (folders only vs. folders + files)
|
174
|
+
- Proper shared drive support
|
175
|
+
- Automatic trash filtering
|
176
|
+
|
177
|
+
**Example Usage**:
|
178
|
+
```python
|
179
|
+
# Discover folders only
|
180
|
+
drive_find_folder_by_name("John\'s Documents", include_files=False)
|
181
|
+
|
182
|
+
# Find folder and list all contents
|
183
|
+
drive_find_folder_by_name("John\'s Documents", include_files=True)
|
184
|
+
|
185
|
+
# Find folder and search for specific files
|
186
|
+
drive_find_folder_by_name("John\'s Documents", include_files=True, file_query="logo")
|
187
|
+
```
|
188
|
+
|
189
|
+
#### 3. `drive_search_files_in_folder` (Enhanced)
|
190
|
+
**Purpose**: Search within a specific folder when folder ID is known
|
191
|
+
**Key Improvements**:
|
192
|
+
- Automatic trash filtering
|
193
|
+
- Cross-reference to name-based search tool
|
194
|
+
- Proper shared drive support
|
195
|
+
|
196
|
+
**Example Usage**:
|
197
|
+
```python
|
198
|
+
# Search all files in a folder
|
199
|
+
drive_search_files_in_folder("1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms")
|
200
|
+
|
201
|
+
# Search for specific files in a folder
|
202
|
+
drive_search_files_in_folder("1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms", "budget")
|
203
|
+
```
|
204
|
+
|
205
|
+
### Supporting Tools
|
206
|
+
|
207
|
+
#### 4. `drive_get_folder_info` (New)
|
208
|
+
**Purpose**: Get detailed folder metadata for debugging and verification
|
209
|
+
**Use Cases**:
|
210
|
+
- Verify folder permissions
|
211
|
+
- Understand folder hierarchy
|
212
|
+
- Debug access issues
|
213
|
+
|
214
|
+
#### 5. `drive_list_shared_drives` (Enhanced)
|
215
|
+
**Purpose**: Discover available shared drives
|
216
|
+
**Improvements**:
|
217
|
+
- Better error handling
|
218
|
+
- Consistent response format
|
219
|
+
|
220
|
+
## Success Metrics
|
221
|
+
|
222
|
+
### Before Improvements
|
223
|
+
- **Shared Folder Search Success Rate**: ~0% (complete failures)
|
224
|
+
- **Apostrophe Name Handling**: Manual escaping required with no guidance
|
225
|
+
- **Tool Complexity**: 2 overlapping tools with duplicated code
|
226
|
+
- **Result Quality**: Included trashed items, creating noise
|
227
|
+
|
228
|
+
### After Improvements
|
229
|
+
- **Shared Folder Search Success Rate**: ~100% with proper usage
|
230
|
+
- **Apostrophe Name Handling**: Clear documentation and examples provided
|
231
|
+
- **Tool Complexity**: 1 unified tool with clear parameter options
|
232
|
+
- **Result Quality**: Clean results excluding trashed items
|
233
|
+
|
234
|
+
## Usage Examples
|
235
|
+
|
236
|
+
### Real-World Scenario: "John's Documents" Folder
|
237
|
+
This was the primary test case that drove many improvements.
|
238
|
+
|
239
|
+
**Original Failing Attempts**:
|
240
|
+
```python
|
241
|
+
# All of these failed before improvements:
|
242
|
+
drive_search_files("John's Documents") # ❌ Invalid Value error
|
243
|
+
drive_search_files('"John\'s Documents"') # ❌ Invalid Value error
|
244
|
+
drive_search_files("parent:'folder_id'") # ❌ Invalid syntax error
|
245
|
+
```
|
246
|
+
|
247
|
+
**Current Working Solutions**:
|
248
|
+
```python
|
249
|
+
# Find the folder by name
|
250
|
+
drive_find_folder_by_name("John\'s Documents", include_files=False)
|
251
|
+
|
252
|
+
# Find folder and list all contents
|
253
|
+
drive_find_folder_by_name("John\'s Documents", include_files=True)
|
254
|
+
|
255
|
+
# Search for specific files within the folder
|
256
|
+
drive_find_folder_by_name("John\'s Documents", include_files=True, file_query="cover")
|
257
|
+
|
258
|
+
# Direct search if you have the folder ID
|
259
|
+
drive_search_files_in_folder("1rsCf7UnkcqvZgUCr8mxSM7BnXApG6pNK")
|
260
|
+
```
|
261
|
+
|
262
|
+
**Successful Response Structure**:
|
263
|
+
```json
|
264
|
+
{
|
265
|
+
"folder_name": "John\\'s Documents",
|
266
|
+
"folders_found": [
|
267
|
+
{
|
268
|
+
"id": "1rsCf7UnkcqvZgUCr8mxSM7BnXApG6pNK",
|
269
|
+
"name": "John's Documents",
|
270
|
+
"mimeType": "application/vnd.google-apps.folder",
|
271
|
+
"webViewLink": "https://drive.google.com/drive/folders/1rsCf7UnkcqvZgUCr8mxSM7BnXApG6pNK"
|
272
|
+
}
|
273
|
+
],
|
274
|
+
"folder_count": 1,
|
275
|
+
"target_folder": {...},
|
276
|
+
"files": [...],
|
277
|
+
"file_count": 10
|
278
|
+
}
|
279
|
+
```
|
280
|
+
|
281
|
+
## Developer Guidelines
|
282
|
+
|
283
|
+
### Query Escaping Rules
|
284
|
+
1. **Apostrophes**: Must be escaped with backslash (`John's` → `John\'s`)
|
285
|
+
2. **Quotes**: Surrounding double quotes are automatically removed
|
286
|
+
3. **Complex Queries**: Use Google Drive API query syntax with proper escaping
|
287
|
+
|
288
|
+
### Tool Selection Guide
|
289
|
+
- **General file search**: Use `drive_search_files`
|
290
|
+
- **Find folder + optionally search within**: Use `drive_find_folder_by_name`
|
291
|
+
- **Search in known folder**: Use `drive_search_files_in_folder`
|
292
|
+
- **Folder debugging**: Use `drive_get_folder_info`
|
293
|
+
|
294
|
+
### Error Handling
|
295
|
+
All tools provide consistent error responses with:
|
296
|
+
- Clear error messages
|
297
|
+
- Operation context
|
298
|
+
- Suggested alternatives when applicable
|
299
|
+
|
300
|
+
## Future Considerations
|
301
|
+
|
302
|
+
### Potential Enhancements
|
303
|
+
1. **Fuzzy Folder Name Matching**: Handle slight misspellings in folder names
|
304
|
+
2. **Batch Operations**: Search multiple folders simultaneously
|
305
|
+
3. **Advanced Filtering**: More sophisticated file type and date filtering
|
306
|
+
4. **Caching**: Cache folder discovery results for repeated searches
|
307
|
+
|
308
|
+
### Maintenance Notes
|
309
|
+
1. **API Compatibility**: All improvements use Google Drive API v3 stable features
|
310
|
+
2. **Backward Compatibility**: Existing code using old tool names will need updates
|
311
|
+
3. **Documentation**: Keep docstring examples updated with real-world use cases
|
312
|
+
|
313
|
+
---
|
314
|
+
|
315
|
+
*This document serves as both implementation record and user guide for the enhanced Google Drive search functionality.*
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# TextRanges Formatting Fix: Content-Based Approach
|
2
|
+
|
3
|
+
## Problem Overview
|
4
|
+
|
5
|
+
When creating slides with mixed text formatting (like large metric numbers with small labels), users experienced styling inconsistencies where the last few characters of labels would appear with incorrect formatting.
|
6
|
+
|
7
|
+
### The Issue
|
8
|
+
|
9
|
+
**Symptom**: Labels in metric textboxes were cut off or displayed with wrong styling
|
10
|
+
- "TOTAL IMPRESSIONS" would show as "TOTAL IMPRESSIO" with different font
|
11
|
+
- "AGGREGATE READERSHIP" would show as "AGGREGATE READERSH"
|
12
|
+
- Last characters appeared with default styling instead of intended format
|
13
|
+
|
14
|
+
**Root Cause**: Manual character index calculation errors in `textRanges`
|
15
|
+
|
16
|
+
## The Original (Error-Prone) Approach
|
17
|
+
|
18
|
+
Previously, users had to manually count characters and specify exact indices:
|
19
|
+
|
20
|
+
```json
|
21
|
+
{
|
22
|
+
"type": "textbox",
|
23
|
+
"content": "43.4M\nTOTAL IMPRESSIONS",
|
24
|
+
"textRanges": [
|
25
|
+
{
|
26
|
+
"startIndex": 0,
|
27
|
+
"endIndex": 5,
|
28
|
+
"style": {"fontSize": 25, "bold": true}
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"startIndex": 6,
|
32
|
+
"endIndex": 21, // ❌ WRONG! Should be 23
|
33
|
+
"style": {"fontSize": 7.5}
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
### Why This Failed
|
40
|
+
|
41
|
+
- `"43.4M\nTOTAL IMPRESSIONS"` = 23 characters total
|
42
|
+
- Users often miscounted or forgot that `\n` counts as 1 character
|
43
|
+
- Google Slides uses **exclusive** `endIndex` (like Python slicing)
|
44
|
+
- Common mistakes:
|
45
|
+
- `endIndex: 21` when it should be `23`
|
46
|
+
- Forgetting newlines in character count
|
47
|
+
- Off-by-one errors
|
48
|
+
|
49
|
+
## The New Solution: Content-Based TextRanges
|
50
|
+
|
51
|
+
Now you can specify formatting by **content** instead of character positions:
|
52
|
+
|
53
|
+
```json
|
54
|
+
{
|
55
|
+
"type": "textbox",
|
56
|
+
"content": "43.4M\nTOTAL IMPRESSIONS",
|
57
|
+
"textRanges": [
|
58
|
+
{
|
59
|
+
"content": "43.4M",
|
60
|
+
"style": {"fontSize": 25, "bold": true}
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"content": "TOTAL IMPRESSIONS",
|
64
|
+
"style": {"fontSize": 7.5}
|
65
|
+
}
|
66
|
+
]
|
67
|
+
}
|
68
|
+
```
|
69
|
+
|
70
|
+
## Benefits
|
71
|
+
|
72
|
+
✅ **No character counting** - just specify the exact text
|
73
|
+
✅ **No index calculation** - system finds text automatically
|
74
|
+
✅ **No off-by-one errors** - content matching is precise
|
75
|
+
✅ **More readable** - clear what styling applies to what text
|
76
|
+
✅ **Backwards compatible** - old index-based configs still work
|
77
|
+
✅ **Auto-correction** - fixes common index mistakes automatically
|
78
|
+
|
79
|
+
## Complete Example: Campaign Metrics
|
80
|
+
|
81
|
+
### Before (Error-Prone)
|
82
|
+
```json
|
83
|
+
{
|
84
|
+
"type": "textbox",
|
85
|
+
"content": "134K\nTOTAL ENGAGEMENTS",
|
86
|
+
"textRanges": [
|
87
|
+
{"startIndex": 0, "endIndex": 4, "style": {"fontSize": 25, "bold": true}},
|
88
|
+
{"startIndex": 5, "endIndex": 21, "style": {"fontSize": 7.5}} // ❌ Wrong!
|
89
|
+
]
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
### After (Foolproof)
|
94
|
+
```json
|
95
|
+
{
|
96
|
+
"type": "textbox",
|
97
|
+
"content": "134K\nTOTAL ENGAGEMENTS",
|
98
|
+
"textRanges": [
|
99
|
+
{"content": "134K", "style": {"fontSize": 25, "bold": true}},
|
100
|
+
{"content": "TOTAL ENGAGEMENTS", "style": {"fontSize": 7.5}} // ✅ Perfect!
|
101
|
+
]
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
105
|
+
## Real-World Usage
|
106
|
+
|
107
|
+
### Metric Dashboard Example
|
108
|
+
```json
|
109
|
+
{
|
110
|
+
"layout": "BLANK",
|
111
|
+
"elements": [
|
112
|
+
{
|
113
|
+
"type": "textbox",
|
114
|
+
"content": "43.4M\nTOTAL IMPRESSIONS",
|
115
|
+
"textRanges": [
|
116
|
+
{"content": "43.4M", "style": {"fontSize": 25, "fontFamily": "Playfair Display", "bold": true}},
|
117
|
+
{"content": "TOTAL IMPRESSIONS", "style": {"fontSize": 7.5, "fontFamily": "Roboto"}}
|
118
|
+
],
|
119
|
+
"position": {"x": 26, "y": 320, "width": 97, "height": 62},
|
120
|
+
"style": {"textAlignment": "CENTER"}
|
121
|
+
},
|
122
|
+
{
|
123
|
+
"type": "textbox",
|
124
|
+
"content": "$9.1M\nAD EQUIVALENCY",
|
125
|
+
"textRanges": [
|
126
|
+
{"content": "$9.1M", "style": {"fontSize": 25, "fontFamily": "Playfair Display", "bold": true}},
|
127
|
+
{"content": "AD EQUIVALENCY", "style": {"fontSize": 7.5, "fontFamily": "Roboto"}}
|
128
|
+
],
|
129
|
+
"position": {"x": 404, "y": 320, "width": 97, "height": 62},
|
130
|
+
"style": {"textAlignment": "CENTER"}
|
131
|
+
}
|
132
|
+
],
|
133
|
+
"create_slide": true
|
134
|
+
}
|
135
|
+
```
|
136
|
+
|
137
|
+
## Migration Guide
|
138
|
+
|
139
|
+
### If You're Using Index-Based TextRanges
|
140
|
+
|
141
|
+
**Option 1**: Switch to content-based (recommended)
|
142
|
+
```json
|
143
|
+
// Old
|
144
|
+
{"startIndex": 6, "endIndex": 23, "style": {...}}
|
145
|
+
|
146
|
+
// New
|
147
|
+
{"content": "YOUR_TEXT_HERE", "style": {...}}
|
148
|
+
```
|
149
|
+
|
150
|
+
**Option 2**: Keep existing configs (auto-corrected)
|
151
|
+
- System now automatically fixes common off-by-one errors
|
152
|
+
- Validates indices and logs warnings for invalid ranges
|
153
|
+
- Your existing configs will work better than before
|
154
|
+
|
155
|
+
### Best Practices
|
156
|
+
|
157
|
+
1. **Use content-based for new configurations**
|
158
|
+
2. **Test mixed formatting** with preview before finalizing
|
159
|
+
3. **Keep text content simple** for easier content matching
|
160
|
+
4. **Combine with other styling** like alignment and colors
|
161
|
+
|
162
|
+
## Technical Details
|
163
|
+
|
164
|
+
The system now:
|
165
|
+
- Automatically finds your specified content within the full text
|
166
|
+
- Calculates the correct `startIndex` and `endIndex`
|
167
|
+
- Handles edge cases like duplicate content
|
168
|
+
- Provides helpful error logging
|
169
|
+
- Maintains full backwards compatibility
|
170
|
+
|
171
|
+
This change eliminates the most common source of formatting errors in slide creation while maintaining all existing functionality.
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "google-workspace-mcp"
|
7
|
-
version = "1.
|
7
|
+
version = "1.1.5"
|
8
8
|
description = "MCP server for Google Workspace integration"
|
9
9
|
authors = [
|
10
10
|
{name = "Arclio Team", email = "info@arclio.com"},
|
@@ -22,7 +22,7 @@ dependencies = [
|
|
22
22
|
"markdown>=3.5.0",
|
23
23
|
"beautifulsoup4>=4.12.0",
|
24
24
|
"mcp>=1.7.0",
|
25
|
-
"markdowndeck>=0.1.
|
25
|
+
"markdowndeck>=0.1.5",
|
26
26
|
]
|
27
27
|
|
28
28
|
[project.scripts]
|
{google_workspace_mcp-1.0.5 → google_workspace_mcp-1.1.5}/src/google_workspace_mcp/__main__.py
RENAMED
@@ -19,13 +19,12 @@ from google_workspace_mcp.resources import drive as drive_resources # noqa: F40
|
|
19
19
|
from google_workspace_mcp.resources import gmail as gmail_resources # noqa: F401
|
20
20
|
from google_workspace_mcp.resources import sheets_resources # noqa: F401
|
21
21
|
from google_workspace_mcp.resources import slides as slides_resources # noqa: F401
|
22
|
-
from google_workspace_mcp.tools import calendar as calendar_tools # noqa: F401
|
23
22
|
|
24
23
|
# Register tools
|
25
|
-
from google_workspace_mcp.tools import
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
from google_workspace_mcp.tools import add_image as add_image_tools # noqa: F401
|
25
|
+
from google_workspace_mcp.tools import calendar as calendar_tools # noqa: F401
|
26
|
+
from google_workspace_mcp.tools import docs_tools # noqa: F401
|
27
|
+
from google_workspace_mcp.tools import sheets_tools # noqa: F401
|
29
28
|
from google_workspace_mcp.tools import drive as drive_tools # noqa: F401
|
30
29
|
from google_workspace_mcp.tools import gmail as gmail_tools # noqa: F401
|
31
30
|
from google_workspace_mcp.tools import slides as slides_tools # noqa: F401
|
@@ -41,3 +40,5 @@ def main():
|
|
41
40
|
|
42
41
|
if __name__ == "__main__":
|
43
42
|
main()
|
43
|
+
if __name__ == "__main__":
|
44
|
+
main()
|