This is page 1 of 2. Use http://codebase.md/markuspfundstein/mcp-gsuite?page={x} to view the full context.
# Directory Structure
```
├── .gitignore
├── .python-version
├── Dockerfile
├── gmail-api-openapi-spec.yaml
├── gmail.v1.json
├── google-calendar-api-openapi-spec.yaml
├── LICENSE
├── pyproject.toml
├── README.md
├── smithery.yaml
├── src
│ └── mcp_gsuite
│ ├── __init__.py
│ ├── calendar.py
│ ├── gauth.py
│ ├── gmail.py
│ ├── server.py
│ ├── toolhandler.py
│ ├── tools_calendar.py
│ └── tools_gmail.py
└── uv.lock
```
# Files
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
```
3.13
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
.env
.gauth.json
oauth2creds.json
.accounts.json
.oauth2.*.json
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# mcp-gsuite MCP server
[](https://smithery.ai/server/mcp-gsuite)
MCP server to interact with Google products.
## Example prompts
Right now, this MCP server supports Gmail and Calendar integration with the following capabilities:
1. General
* Multiple google accounts
2. Gmail
* Get your Gmail user information
* Query emails with flexible search (e.g., unread, from specific senders, date ranges, with attachments)
* Retrieve complete email content by ID
* Create new draft emails with recipients, subject, body and CC options
* Delete draft emails
* Reply to existing emails (can either send immediately or save as draft)
* Retrieve multiple emails at once by their IDs.
* Save multiple attachments from emails to your local system.
3. Calendar
* Manage multiple calendars
* Get calendar events within specified time ranges
* Create calendar events with:
+ Title, start/end times
+ Optional location and description
+ Optional attendees
+ Custom timezone support
+ Notification preferences
* Delete calendar events
Example prompts you can try:
* Retrieve my latest unread messages
* Search my emails from the Scrum Master
* Retrieve all emails from accounting
* Take the email about ABC and summarize it
* Write a nice response to Alice's last email and upload a draft.
* Reply to Bob's email with a Thank you note. Store it as draft
* What do I have on my agenda tomorrow?
* Check my private account's Family agenda for next week
* I need to plan an event with Tim for 2hrs next week. Suggest some time slots.
## Quickstart
### Install
### Installing via Smithery
To install mcp-gsuite for Claude Desktop automatically via [Smithery](https://smithery.ai/server/mcp-gsuite):
```bash
npx -y @smithery/cli install mcp-gsuite --client claude
```
#### Oauth 2
Google Workspace (G Suite) APIs require OAuth2 authorization. Follow these steps to set up authentication:
1. Create OAuth2 Credentials:
- Go to the [Google Cloud Console](https://console.cloud.google.com/)
- Create a new project or select an existing one
- Enable the Gmail API and Google Calendar API for your project
- Go to "Credentials" → "Create Credentials" → "OAuth client ID"
- Select "Desktop app" or "Web application" as the application type
- Configure the OAuth consent screen with required information
- Add authorized redirect URIs (include `http://localhost:4100/code` for local development)
2. Required OAuth2 Scopes:
```json
[
"openid",
"https://mail.google.com/",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/userinfo.email"
]
```
3. Then create a `.gauth.json` in your working directory with client
```json
{
"web": {
"client_id": "$your_client_id",
"client_secret": "$your_client_secret",
"redirect_uris": ["http://localhost:4100/code"],
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}
}
```
4. Create a `.accounts.json` file with account information
```json
{
"accounts": [
{
"email": "[email protected]",
"account_type": "personal",
"extra_info": "Additional info that you want to tell Claude: E.g. 'Contains Family Calendar'"
}
]
}
```
You can specifiy multiple accounts. Make sure they have access in your Google Auth app. The `extra_info` field is especially interesting as you can add info here that you want to tell the AI about the account (e.g. whether it has a specific agenda)
Note: When you first execute one of the tools for a specific account, a browser will open, redirect you to Google and ask for your credentials, scope, etc. After a successful login, it stores the credentials in a local file called `.oauth.{email}.json` . Once you are authorized, the refresh token will be used.
#### Claude Desktop
On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
<details>
<summary>Development/Unpublished Servers Configuration</summary>
```json
{
"mcpServers": {
"mcp-gsuite": {
"command": "uv",
"args": [
"--directory",
"<dir_to>/mcp-gsuite",
"run",
"mcp-gsuite"
]
}
}
}
```
Note: You can also use the `uv run mcp-gsuite --accounts-file /path/to/custom/.accounts.json` to specify a different accounts file or `--credentials-dir /path/to/custom/credentials` to specify a different credentials directory.
```json
{
"mcpServers": {
"mcp-gsuite": {
"command": "uv",
"args": [
"--directory",
"<dir_to>/mcp-gsuite",
"run",
"mcp-gsuite",
"--accounts-file",
"/path/to/custom/.accounts.json",
"--credentials-dir",
"/path/to/custom/credentials"
]
}
}
}
```
</details>
<details>
<summary>Published Servers Configuration</summary>
```json
{
"mcpServers": {
"mcp-gsuite": {
"command": "uvx",
"args": [
"mcp-gsuite",
"--accounts-file",
"/path/to/custom/.accounts.json",
"--credentials-dir",
"/path/to/custom/credentials"
]
}
}
}
```
</details>
### Configuration Options
The MCP server can be configured with several command-line options to specify custom paths for authentication and account information:
* `--gauth-file`: Specifies the path to the `.gauth.json` file containing OAuth2 client configuration. Default is `./.gauth.json`.
* `--accounts-file`: Specifies the path to the `.accounts.json` file containing information about the Google accounts. Default is `./.accounts.json`.
* `--credentials-dir`: Specifies the directory where OAuth credentials are stored after successful authentication. Default is the current working directory with a subdirectory for each account as `.oauth.{email}.json`.
These options allow for flexibility in managing different environments or multiple sets of credentials and accounts, especially useful in development and testing scenarios.
Example usage:
```bash
uv run mcp-gsuite --gauth-file /path/to/custom/.gauth.json --accounts-file /path/to/custom/.accounts.json --credentials-dir /path/to/custom/credentials
```
This configuration is particularly useful when you have multiple instances of the server running with different configurations or when deploying to environments where the default paths are not suitable.
## Development
### Building and Publishing
To prepare the package for distribution:
1. Sync dependencies and update lockfile:
```bash
uv sync
```
2. Build package distributions:
```bash
uv build
```
This will create source and wheel distributions in the `dist/` directory.
3. Publish to PyPI:
```bash
uv publish
```
Note: You'll need to set PyPI credentials via environment variables or command flags:
* Token: `--token` or `UV_PUBLISH_TOKEN`
* Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`
### Debugging
Since MCP servers run over stdio, debugging can be challenging. For the best debugging
experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
You can launch the MCP Inspector via [ `npm` ](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
```bash
npx @modelcontextprotocol/inspector uv --directory /path/to/mcp-gsuite run mcp-gsuite
```
Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
You can also watch the server logs with this command:
```bash
tail -n 20 -f ~/Library/Logs/Claude/mcp-server-mcp-gsuite.log
```
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/__init__.py:
--------------------------------------------------------------------------------
```python
from . import server
import asyncio
def main():
"""Main entry point for the package."""
asyncio.run(server.main())
# Optionally expose other important items at package level
__all__ = ['main', 'server']
```
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
```toml
[project]
name = "mcp-gsuite"
version = "0.4.1"
description = "MCP Server to connect to Google G-Suite"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"beautifulsoup4>=4.12.3",
"google-api-python-client>=2.154.0",
"httplib2>=0.22.0",
"mcp>=1.3.0",
"oauth2client==4.1.3",
"python-dotenv>=1.0.1",
"pytz>=2024.2",
"requests>=2.32.3",
]
[[project.authors]]
name = "Markus Pfundstein"
email = "[email protected]"
[build-system]
requires = [ "hatchling",]
build-backend = "hatchling.build"
[dependency-groups]
dev = [
"pyright>=1.1.389",
]
[project.scripts]
mcp-gsuite = "mcp_gsuite:main"
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
required:
- gauthFile
- accountsFile
properties:
gauthFile:
type: string
description: Path to the OAuth2 client configuration file.
accountsFile:
type: string
description: Path to the Google accounts configuration file.
credentialsDir:
type: string
description: Directory where OAuth credentials are stored.
commandFunction:
# A function that produces the CLI command to start the MCP on stdio.
|-
(config) => ({command: 'uv', args: ['run', 'mcp-gsuite', '--gauth-file', config.gauthFile, '--accounts-file', config.accountsFile, '--credentials-dir', config.credentialsDir]})
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
# Use a Python image with uv pre-installed
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS uv
# Set the working directory
WORKDIR /app
# Copy necessary configuration files
COPY . .
# Enable bytecode compilation
ENV UV_COMPILE_BYTECODE=1
# Use the copy link mode for mount points
ENV UV_LINK_MODE=copy
# Sync dependencies and build the project
RUN --mount=type=cache,target=/root/.cache/uv --mount=type=bind,source=uv.lock,target=uv.lock --mount=type=bind,source=pyproject.toml,target=pyproject.toml uv sync --frozen --no-install-project --no-dev --no-editable
# Install the project
RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev --no-editable
# Final stage: running the application
FROM python:3.13-slim-bookworm
WORKDIR /app
COPY --from=uv /root/.local /root/.local
COPY --from=uv --chown=app:app /app/.venv /app/.venv
# Place executables in the environment at the front of the path
ENV PATH="/app/.venv/bin:$PATH"
# Expose necessary ports
EXPOSE 4100
# Specify the entrypoint command
ENTRYPOINT ["uv", "run", "mcp-gsuite"]
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/toolhandler.py:
--------------------------------------------------------------------------------
```python
from collections.abc import Sequence
from mcp.types import (
Tool,
TextContent,
ImageContent,
EmbeddedResource,
)
from . import gauth
USER_ID_ARG = "__user_id__"
class ToolHandler():
def __init__(self, tool_name: str):
self.name = tool_name
def get_account_descriptions(self) -> list[str]:
return [a.to_description() for a in gauth.get_account_info()]
# we ingest this information into every tool that requires a specified __user_id__.
# we also add what information actually can be used (account info). This way Claude
# will know what to do.
def get_supported_emails_tool_text(self) -> str:
return f"""This tool requires a authorized Google account email for {USER_ID_ARG} argument. You can choose one of: {', '.join(self.get_account_descriptions())}"""
def get_user_id_arg_schema(self) -> dict:
return {
"type": "string",
"description": f"The EMAIL of the Google account for which you are executing this action. Can be one of: {', '.join(self.get_account_descriptions())}"
}
def get_tool_description(self) -> Tool:
raise NotImplementedError()
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
raise NotImplementedError()
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/server.py:
--------------------------------------------------------------------------------
```python
import logging
from collections.abc import Sequence
from functools import lru_cache
import subprocess
from typing import Any
import traceback
from dotenv import load_dotenv
from mcp.server import Server
import threading
import sys
from mcp.types import (
Tool,
TextContent,
ImageContent,
EmbeddedResource,
)
import json
from . import gauth
from http.server import BaseHTTPRequestHandler,HTTPServer
from urllib.parse import (
urlparse,
parse_qs,
)
class OauthListener(BaseHTTPRequestHandler):
def do_GET(self):
url = urlparse(self.path)
if url.path != "/code":
self.send_response(404)
self.end_headers()
return
query = parse_qs(url.query)
if "code" not in query:
self.send_response(400)
self.end_headers()
return
self.send_response(200)
self.end_headers()
self.wfile.write("Auth successful! You can close the tab!".encode("utf-8"))
self.wfile.flush()
storage = {}
creds = gauth.get_credentials(authorization_code=query["code"][0], state=storage)
t = threading.Thread(target = self.server.shutdown)
t.daemon = True
t.start()
load_dotenv()
from . import tools_gmail
from . import tools_calendar
from . import toolhandler
# Load environment variables
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-gsuite")
def start_auth_flow(user_id: str):
auth_url = gauth.get_authorization_url(user_id, state={})
if sys.platform == "darwin" or sys.platform.startswith("linux"):
subprocess.Popen(['open', auth_url])
else:
import webbrowser
webbrowser.open(auth_url)
# start server for code callback
server_address = ('', 4100)
server = HTTPServer(server_address, OauthListener)
server.serve_forever()
def setup_oauth2(user_id: str):
accounts = gauth.get_account_info()
if len(accounts) == 0:
raise RuntimeError("No accounts specified in .gauth.json")
if user_id not in [a.email for a in accounts]:
raise RuntimeError(f"Account for email: {user_id} not specified in .gauth.json")
credentials = gauth.get_stored_credentials(user_id=user_id)
if not credentials:
start_auth_flow(user_id=user_id)
else:
if credentials.access_token_expired:
logger.error("credentials expired. try refresh")
# this call refreshes access token
user_info = gauth.get_user_info(credentials=credentials)
#logging.error(f"User info: {json.dumps(user_info)}")
gauth.store_credentials(credentials=credentials, user_id=user_id)
app = Server("mcp-gsuite")
tool_handlers = {}
def add_tool_handler(tool_class: toolhandler.ToolHandler):
global tool_handlers
tool_handlers[tool_class.name] = tool_class
def get_tool_handler(name: str) -> toolhandler.ToolHandler | None:
if name not in tool_handlers:
return None
return tool_handlers[name]
add_tool_handler(tools_gmail.QueryEmailsToolHandler())
add_tool_handler(tools_gmail.GetEmailByIdToolHandler())
add_tool_handler(tools_gmail.CreateDraftToolHandler())
add_tool_handler(tools_gmail.DeleteDraftToolHandler())
add_tool_handler(tools_gmail.ReplyEmailToolHandler())
add_tool_handler(tools_gmail.GetAttachmentToolHandler())
add_tool_handler(tools_gmail.BulkGetEmailsByIdsToolHandler())
add_tool_handler(tools_gmail.BulkSaveAttachmentsToolHandler())
add_tool_handler(tools_calendar.ListCalendarsToolHandler())
add_tool_handler(tools_calendar.GetCalendarEventsToolHandler())
add_tool_handler(tools_calendar.CreateCalendarEventToolHandler())
add_tool_handler(tools_calendar.DeleteCalendarEventToolHandler())
@app.list_tools()
async def list_tools() -> list[Tool]:
"""List available tools."""
return [th.get_tool_description() for th in tool_handlers.values()]
@app.call_tool()
async def call_tool(name: str, arguments: Any) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
try:
if not isinstance(arguments, dict):
raise RuntimeError("arguments must be dictionary")
if toolhandler.USER_ID_ARG not in arguments:
raise RuntimeError("user_id argument is missing in dictionary.")
setup_oauth2(user_id=arguments.get(toolhandler.USER_ID_ARG, ""))
tool_handler = get_tool_handler(name)
if not tool_handler:
raise ValueError(f"Unknown tool: {name}")
return tool_handler.run_tool(arguments)
except Exception as e:
logging.error(traceback.format_exc())
logging.error(f"Error during call_tool: str(e)")
raise RuntimeError(f"Caught Exception. Error: {str(e)}")
async def main():
print(sys.platform)
accounts = gauth.get_account_info()
for account in accounts:
creds = gauth.get_stored_credentials(user_id=account.email)
if creds:
logging.info(f"found credentials for {account.email}")
from mcp.server.stdio import stdio_server
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/calendar.py:
--------------------------------------------------------------------------------
```python
from googleapiclient.discovery import build
from . import gauth
import logging
import traceback
from datetime import datetime
import pytz
class CalendarService():
def __init__(self, user_id: str):
credentials = gauth.get_stored_credentials(user_id=user_id)
if not credentials:
raise RuntimeError("No Oauth2 credentials stored")
self.service = build('calendar', 'v3', credentials=credentials) # Note: using v3 for Calendar API
def list_calendars(self) -> list:
"""
Lists all calendars accessible by the user.
Returns:
list: List of calendar objects with their metadata
"""
try:
calendar_list = self.service.calendarList().list().execute()
calendars = []
for calendar in calendar_list.get('items', []):
if calendar.get('kind') == 'calendar#calendarListEntry':
calendars.append({
'id': calendar.get('id'),
'summary': calendar.get('summary'),
'primary': calendar.get('primary', False),
'time_zone': calendar.get('timeZone'),
'etag': calendar.get('etag'),
'access_role': calendar.get('accessRole')
})
return calendars
except Exception as e:
logging.error(f"Error retrieving calendars: {str(e)}")
logging.error(traceback.format_exc())
return []
def get_events(self, time_min=None, time_max=None, max_results=250, show_deleted=False, calendar_id: str ='primary'):
"""
Retrieve calendar events within a specified time range.
Args:
time_min (str, optional): Start time in RFC3339 format. Defaults to current time.
time_max (str, optional): End time in RFC3339 format
max_results (int): Maximum number of events to return (1-2500)
show_deleted (bool): Whether to include deleted events
Returns:
list: List of calendar events
"""
try:
# If no time_min specified, use current time
if not time_min:
time_min = datetime.now(pytz.UTC).isoformat()
# Ensure max_results is within limits
max_results = min(max(1, max_results), 2500)
# Prepare parameters
params = {
'calendarId': calendar_id,
'timeMin': time_min,
'maxResults': max_results,
'singleEvents': True,
'orderBy': 'startTime',
'showDeleted': show_deleted
}
# Add optional time_max if specified
if time_max:
params['timeMax'] = time_max
# Execute the events().list() method
events_result = self.service.events().list(**params).execute()
# Extract the events
events = events_result.get('items', [])
# Process and return the events
processed_events = []
for event in events:
processed_event = {
'id': event.get('id'),
'summary': event.get('summary'),
'description': event.get('description'),
'start': event.get('start'),
'end': event.get('end'),
'status': event.get('status'),
'creator': event.get('creator'),
'organizer': event.get('organizer'),
'attendees': event.get('attendees'),
'location': event.get('location'),
'hangoutLink': event.get('hangoutLink'),
'conferenceData': event.get('conferenceData'),
'recurringEventId': event.get('recurringEventId')
}
processed_events.append(processed_event)
return processed_events
except Exception as e:
logging.error(f"Error retrieving calendar events: {str(e)}")
logging.error(traceback.format_exc())
return []
def create_event(self, summary: str, start_time: str, end_time: str,
location: str | None = None, description: str | None = None,
attendees: list | None = None, send_notifications: bool = True,
timezone: str | None = None,
calendar_id : str = 'primary') -> dict | None:
"""
Create a new calendar event.
Args:
summary (str): Title of the event
start_time (str): Start time in RFC3339 format
end_time (str): End time in RFC3339 format
location (str, optional): Location of the event
description (str, optional): Description of the event
attendees (list, optional): List of attendee email addresses
send_notifications (bool): Whether to send notifications to attendees
timezone (str, optional): Timezone for the event (e.g. 'America/New_York')
Returns:
dict: Created event data or None if creation fails
"""
try:
# Prepare event data
event = {
'summary': summary,
'start': {
'dateTime': start_time,
'timeZone': timezone or 'UTC',
},
'end': {
'dateTime': end_time,
'timeZone': timezone or 'UTC',
}
}
# Add optional fields if provided
if location:
event['location'] = location
if description:
event['description'] = description
if attendees:
event['attendees'] = [{'email': email} for email in attendees]
# Create the event
created_event = self.service.events().insert(
calendarId=calendar_id,
body=event,
sendNotifications=send_notifications
).execute()
return created_event
except Exception as e:
logging.error(f"Error creating calendar event: {str(e)}")
logging.error(traceback.format_exc())
return None
def delete_event(self, event_id: str, send_notifications: bool = True, calendar_id: str = 'primary') -> bool:
"""
Delete a calendar event by its ID.
Args:
event_id (str): The ID of the event to delete
send_notifications (bool): Whether to send cancellation notifications to attendees
Returns:
bool: True if deletion was successful, False otherwise
"""
try:
self.service.events().delete(
calendarId=calendar_id,
eventId=event_id,
sendNotifications=send_notifications
).execute()
return True
except Exception as e:
logging.error(f"Error deleting calendar event {event_id}: {str(e)}")
logging.error(traceback.format_exc())
return False
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/gauth.py:
--------------------------------------------------------------------------------
```python
import logging
from oauth2client.client import (
flow_from_clientsecrets,
FlowExchangeError,
OAuth2Credentials,
Credentials,
)
from googleapiclient.discovery import build
import httplib2
from google.auth.transport.requests import Request
import os
import pydantic
import json
import argparse
def get_gauth_file() -> str:
parser = argparse.ArgumentParser()
parser.add_argument(
"--gauth-file",
type=str,
default="./.gauth.json",
help="Path to client secrets file",
)
args, _ = parser.parse_known_args()
return args.gauth_file
CLIENTSECRETS_LOCATION = get_gauth_file()
REDIRECT_URI = 'http://localhost:4100/code'
SCOPES = [
"openid",
"https://www.googleapis.com/auth/userinfo.email",
"https://mail.google.com/",
"https://www.googleapis.com/auth/calendar"
]
class AccountInfo(pydantic.BaseModel):
email: str
account_type: str
extra_info: str
def __init__(self, email: str, account_type: str, extra_info: str = ""):
super().__init__(email=email, account_type=account_type, extra_info=extra_info)
def to_description(self):
return f"""Account for email: {self.email} of type: {self.account_type}. Extra info for: {self.extra_info}"""
def get_accounts_file() -> str:
parser = argparse.ArgumentParser()
parser.add_argument(
"--accounts-file",
type=str,
default="./.accounts.json",
help="Path to accounts configuration file",
)
args, _ = parser.parse_known_args()
return args.accounts_file
def get_account_info() -> list[AccountInfo]:
accounts_file = get_accounts_file()
with open(accounts_file) as f:
data = json.load(f)
accounts = data.get("accounts", [])
return [AccountInfo.model_validate(acc) for acc in accounts]
class GetCredentialsException(Exception):
"""Error raised when an error occurred while retrieving credentials.
Attributes:
authorization_url: Authorization URL to redirect the user to in order to
request offline access.
"""
def __init__(self, authorization_url):
"""Construct a GetCredentialsException."""
self.authorization_url = authorization_url
class CodeExchangeException(GetCredentialsException):
"""Error raised when a code exchange has failed."""
class NoRefreshTokenException(GetCredentialsException):
"""Error raised when no refresh token has been found."""
class NoUserIdException(Exception):
"""Error raised when no user ID could be retrieved."""
def get_credentials_dir() -> str:
parser = argparse.ArgumentParser()
parser.add_argument(
"--credentials-dir",
type=str,
default=".",
help="Directory to store OAuth2 credentials",
)
args, _ = parser.parse_known_args()
return args.credentials_dir
def _get_credential_filename(user_id: str) -> str:
creds_dir = get_credentials_dir()
return os.path.join(creds_dir, f".oauth2.{user_id}.json")
def get_stored_credentials(user_id: str) -> OAuth2Credentials | None:
"""Retrieved stored credentials for the provided user ID.
Args:
user_id: User's ID.
Returns:
Stored oauth2client.client.OAuth2Credentials if found, None otherwise.
"""
try:
cred_file_path = _get_credential_filename(user_id=user_id)
if not os.path.exists(cred_file_path):
logging.warning(f"No stored Oauth2 credentials yet at path: {cred_file_path}")
return None
with open(cred_file_path, 'r') as f:
data = f.read()
return Credentials.new_from_json(data)
except Exception as e:
logging.error(e)
return None
raise None
def store_credentials(credentials: OAuth2Credentials, user_id: str):
"""Store OAuth 2.0 credentials in the specified directory."""
cred_file_path = _get_credential_filename(user_id=user_id)
os.makedirs(os.path.dirname(cred_file_path), exist_ok=True)
data = credentials.to_json()
with open(cred_file_path, "w") as f:
f.write(data)
def exchange_code(authorization_code):
"""Exchange an authorization code for OAuth 2.0 credentials.
Args:
authorization_code: Authorization code to exchange for OAuth 2.0
credentials.
Returns:
oauth2client.client.OAuth2Credentials instance.
Raises:
CodeExchangeException: an error occurred.
"""
flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
flow.redirect_uri = REDIRECT_URI
try:
credentials = flow.step2_exchange(authorization_code)
return credentials
except FlowExchangeError as error:
logging.error('An error occurred: %s', error)
raise CodeExchangeException(None)
def get_user_info(credentials):
"""Send a request to the UserInfo API to retrieve the user's information.
Args:
credentials: oauth2client.client.OAuth2Credentials instance to authorize the
request.
Returns:
User information as a dict.
"""
user_info_service = build(
serviceName='oauth2', version='v2',
http=credentials.authorize(httplib2.Http()))
user_info = None
try:
user_info = user_info_service.userinfo().get().execute()
except Exception as e:
logging.error(f'An error occurred: {e}')
if user_info and user_info.get('id'):
return user_info
else:
raise NoUserIdException()
def get_authorization_url(email_address, state):
"""Retrieve the authorization URL.
Args:
email_address: User's e-mail address.
state: State for the authorization URL.
Returns:
Authorization URL to redirect the user to.
"""
flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES), redirect_uri=REDIRECT_URI)
flow.params['access_type'] = 'offline'
flow.params['approval_prompt'] = 'force'
flow.params['user_id'] = email_address
flow.params['state'] = state
return flow.step1_get_authorize_url(state=state)
def get_credentials(authorization_code, state):
"""Retrieve credentials using the provided authorization code.
This function exchanges the authorization code for an access token and queries
the UserInfo API to retrieve the user's e-mail address.
If a refresh token has been retrieved along with an access token, it is stored
in the application database using the user's e-mail address as key.
If no refresh token has been retrieved, the function checks in the application
database for one and returns it if found or raises a NoRefreshTokenException
with the authorization URL to redirect the user to.
Args:
authorization_code: Authorization code to use to retrieve an access token.
state: State to set to the authorization URL in case of error.
Returns:
oauth2client.client.OAuth2Credentials instance containing an access and
refresh token.
Raises:
CodeExchangeError: Could not exchange the authorization code.
NoRefreshTokenException: No refresh token could be retrieved from the
available sources.
"""
email_address = ''
try:
credentials = exchange_code(authorization_code)
user_info = get_user_info(credentials)
import json
logging.error(f"user_info: {json.dumps(user_info)}")
email_address = user_info.get('email')
if credentials.refresh_token is not None:
store_credentials(credentials, user_id=email_address)
return credentials
else:
credentials = get_stored_credentials(user_id=email_address)
if credentials and credentials.refresh_token is not None:
return credentials
except CodeExchangeException as error:
logging.error('An error occurred during code exchange.')
# Drive apps should try to retrieve the user and credentials for the current
# session.
# If none is available, redirect the user to the authorization URL.
error.authorization_url = get_authorization_url(email_address, state)
raise error
except NoUserIdException:
logging.error('No user ID could be retrieved.')
# No refresh token has been retrieved.
authorization_url = get_authorization_url(email_address, state)
raise NoRefreshTokenException(authorization_url)
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/tools_calendar.py:
--------------------------------------------------------------------------------
```python
from collections.abc import Sequence
from mcp.types import (
Tool,
TextContent,
ImageContent,
EmbeddedResource,
LoggingLevel,
)
from . import gauth
from . import calendar
import json
from . import toolhandler
CALENDAR_ID_ARG="__calendar_id__"
def get_calendar_id_arg_schema() -> dict[str, str]:
return {
"type": "string",
"description": """Optional ID of the specific agenda for which you are executing this action.
If not provided, the default calendar is being used.
If not known, the specific calendar id can be retrieved with the list_calendars tool""",
"default": "primary"
}
class ListCalendarsToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("list_calendars")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="""Lists all calendars accessible by the user.
Call it before any other tool whenever the user specifies a particular agenda (Family, Holidays, etc.).""",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
},
"required": [toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
calendar_service = calendar.CalendarService(user_id=user_id)
calendars = calendar_service.list_calendars()
return [
TextContent(
type="text",
text=json.dumps(calendars, indent=2)
)
]
class GetCalendarEventsToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("get_calendar_events")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Retrieves calendar events from the user's Google Calendar within a specified time range.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"__calendar_id__": get_calendar_id_arg_schema(),
"time_min": {
"type": "string",
"description": "Start time in RFC3339 format (e.g. 2024-12-01T00:00:00Z). Defaults to current time if not specified."
},
"time_max": {
"type": "string",
"description": "End time in RFC3339 format (e.g. 2024-12-31T23:59:59Z). Optional."
},
"max_results": {
"type": "integer",
"description": "Maximum number of events to return (1-2500)",
"minimum": 1,
"maximum": 2500,
"default": 250
},
"show_deleted": {
"type": "boolean",
"description": "Whether to include deleted events",
"default": False
}
},
"required": [toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
calendar_service = calendar.CalendarService(user_id=user_id)
events = calendar_service.get_events(
time_min=args.get('time_min'),
time_max=args.get('time_max'),
max_results=args.get('max_results', 250),
show_deleted=args.get('show_deleted', False),
calendar_id=args.get(CALENDAR_ID_ARG, 'primary'),
)
return [
TextContent(
type="text",
text=json.dumps(events, indent=2)
)
]
class CreateCalendarEventToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("create_calendar_event")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Creates a new event in a specified Google Calendar of the specified user.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"__calendar_id__": get_calendar_id_arg_schema(),
"summary": {
"type": "string",
"description": "Title of the event"
},
"location": {
"type": "string",
"description": "Location of the event (optional)"
},
"description": {
"type": "string",
"description": "Description or notes for the event (optional)"
},
"start_time": {
"type": "string",
"description": "Start time in RFC3339 format (e.g. 2024-12-01T10:00:00Z)"
},
"end_time": {
"type": "string",
"description": "End time in RFC3339 format (e.g. 2024-12-01T11:00:00Z)"
},
"attendees": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of attendee email addresses (optional)"
},
"send_notifications": {
"type": "boolean",
"description": "Whether to send notifications to attendees",
"default": True
},
"timezone": {
"type": "string",
"description": "Timezone for the event (e.g. 'America/New_York'). Defaults to UTC if not specified."
}
},
"required": [toolhandler.USER_ID_ARG, "summary", "start_time", "end_time"]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
# Validate required arguments
required = ["summary", "start_time", "end_time"]
if not all(key in args for key in required):
raise RuntimeError(f"Missing required arguments: {', '.join(required)}")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
calendar_service = calendar.CalendarService(user_id=user_id)
event = calendar_service.create_event(
summary=args["summary"],
start_time=args["start_time"],
end_time=args["end_time"],
location=args.get("location"),
description=args.get("description"),
attendees=args.get("attendees", []),
send_notifications=args.get("send_notifications", True),
timezone=args.get("timezone"),
calendar_id=args.get(CALENDAR_ID_ARG, 'primary'),
)
return [
TextContent(
type="text",
text=json.dumps(event, indent=2)
)
]
class DeleteCalendarEventToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("delete_calendar_event")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Deletes an event from the user's Google Calendar by its event ID.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"__calendar_id__": get_calendar_id_arg_schema(),
"event_id": {
"type": "string",
"description": "The ID of the calendar event to delete"
},
"send_notifications": {
"type": "boolean",
"description": "Whether to send cancellation notifications to attendees",
"default": True
}
},
"required": [toolhandler.USER_ID_ARG, "event_id"]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if "event_id" not in args:
raise RuntimeError("Missing required argument: event_id")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
calendar_service = calendar.CalendarService(user_id=user_id)
success = calendar_service.delete_event(
event_id=args["event_id"],
send_notifications=args.get("send_notifications", True),
calendar_id=args.get(CALENDAR_ID_ARG, 'primary'),
)
return [
TextContent(
type="text",
text=json.dumps({
"success": success,
"message": "Event successfully deleted" if success else "Failed to delete event"
}, indent=2)
)
]
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/gmail.py:
--------------------------------------------------------------------------------
```python
from googleapiclient.discovery import build
from . import gauth
import logging
import base64
import traceback
from email.mime.text import MIMEText
from typing import Tuple
class GmailService():
def __init__(self, user_id: str):
credentials = gauth.get_stored_credentials(user_id=user_id)
if not credentials:
raise RuntimeError("No Oauth2 credentials stored")
self.service = build('gmail', 'v1', credentials=credentials)
def _parse_message(self, txt, parse_body=False) -> dict | None:
"""
Parse a Gmail message into a structured format.
Args:
txt (dict): Raw message from Gmail API
parse_body (bool): Whether to parse and include the message body (default: False)
Returns:
dict: Parsed message containing comprehensive metadata
None: If parsing fails
"""
try:
message_id = txt.get('id')
thread_id = txt.get('threadId')
payload = txt.get('payload', {})
headers = payload.get('headers', [])
metadata = {
'id': message_id,
'threadId': thread_id,
'historyId': txt.get('historyId'),
'internalDate': txt.get('internalDate'),
'sizeEstimate': txt.get('sizeEstimate'),
'labelIds': txt.get('labelIds', []),
'snippet': txt.get('snippet'),
}
for header in headers:
name = header.get('name', '').lower()
value = header.get('value', '')
if name == 'subject':
metadata['subject'] = value
elif name == 'from':
metadata['from'] = value
elif name == 'to':
metadata['to'] = value
elif name == 'date':
metadata['date'] = value
elif name == 'cc':
metadata['cc'] = value
elif name == 'bcc':
metadata['bcc'] = value
elif name == 'message-id':
metadata['message_id'] = value
elif name == 'in-reply-to':
metadata['in_reply_to'] = value
elif name == 'references':
metadata['references'] = value
elif name == 'delivered-to':
metadata['delivered_to'] = value
if parse_body:
body = self._extract_body(payload)
if body:
metadata['body'] = body
metadata['mimeType'] = payload.get('mimeType')
return metadata
except Exception as e:
logging.error(f"Error parsing message: {str(e)}")
logging.error(traceback.format_exc())
return None
def _extract_body(self, payload) -> str | None:
"""
Extract the email body from the payload.
Handles both multipart and single part messages, including nested multiparts.
"""
try:
# For single part text/plain messages
if payload.get('mimeType') == 'text/plain':
data = payload.get('body', {}).get('data')
if data:
return base64.urlsafe_b64decode(data).decode('utf-8')
# For single part text/html messages
if payload.get('mimeType') == 'text/html':
data = payload.get('body', {}).get('data')
if data:
return base64.urlsafe_b64decode(data).decode('utf-8')
# For multipart messages (both alternative and related)
if payload.get('mimeType', '').startswith('multipart/'):
parts = payload.get('parts', [])
# First try to find a direct text/plain part
for part in parts:
if part.get('mimeType') == 'text/plain':
data = part.get('body', {}).get('data')
if data:
return base64.urlsafe_b64decode(data).decode('utf-8')
# If no direct text/plain, recursively check nested multipart structures
for part in parts:
if part.get('mimeType', '').startswith('multipart/'):
nested_body = self._extract_body(part)
if nested_body:
return nested_body
# If still no body found, try the first part as fallback
if parts and 'body' in parts[0] and 'data' in parts[0]['body']:
data = parts[0]['body']['data']
return base64.urlsafe_b64decode(data).decode('utf-8')
return None
except Exception as e:
logging.error(f"Error extracting body: {str(e)}")
return None
def query_emails(self, query=None, max_results=100):
"""
Query emails from Gmail based on a search query.
Args:
query (str, optional): Gmail search query (e.g., 'is:unread', 'from:[email protected]')
If None, returns all emails
max_results (int): Maximum number of emails to retrieve (1-500, default: 100)
Returns:
list: List of parsed email messages, newest first
"""
try:
# Ensure max_results is within API limits
max_results = min(max(1, max_results), 500)
# Get the list of messages
result = self.service.users().messages().list(
userId='me',
maxResults=max_results,
q=query if query else ''
).execute()
messages = result.get('messages', [])
parsed = []
# Fetch full message details for each message
for msg in messages:
txt = self.service.users().messages().get(
userId='me',
id=msg['id']
).execute()
parsed_message = self._parse_message(txt=txt, parse_body=False)
if parsed_message:
parsed.append(parsed_message)
return parsed
except Exception as e:
logging.error(f"Error reading emails: {str(e)}")
logging.error(traceback.format_exc())
return []
def get_email_by_id_with_attachments(self, email_id: str) -> Tuple[dict, dict] | Tuple[None, dict]:
"""
Fetch and parse a complete email message by its ID including attachment IDs.
Args:
email_id (str): The Gmail message ID to retrieve
Returns:
Tuple[dict, list]: Complete parsed email message including body and list of attachment IDs
Tuple[None, list]: If retrieval or parsing fails, returns None for email and empty list for attachment IDs
"""
try:
# Fetch the complete message by ID
message = self.service.users().messages().get(
userId='me',
id=email_id
).execute()
# Parse the message with body included
parsed_email = self._parse_message(txt=message, parse_body=True)
if parsed_email is None:
return None, {}
attachments = {}
# Check if 'parts' exists in payload before trying to access it
if "payload" in message and "parts" in message["payload"]:
for part in message["payload"]["parts"]:
if "body" in part and "attachmentId" in part["body"]:
attachment_id = part["body"]["attachmentId"]
part_id = part["partId"]
attachment = {
"filename": part["filename"],
"mimeType": part["mimeType"],
"attachmentId": attachment_id,
"partId": part_id
}
attachments[part_id] = attachment
else:
# Handle case when there are no parts (single part message)
logging.info(f"Email {email_id} does not have 'parts' in payload (likely single part message)")
if "payload" in message and "body" in message["payload"] and "attachmentId" in message["payload"]["body"]:
# Handle potential attachment in single part message
attachment_id = message["payload"]["body"]["attachmentId"]
attachment = {
"filename": message["payload"].get("filename", "attachment"),
"mimeType": message["payload"].get("mimeType", "application/octet-stream"),
"attachmentId": attachment_id,
"partId": "0"
}
attachments["0"] = attachment
return parsed_email, attachments
except Exception as e:
logging.error(f"Error retrieving email {email_id}: {str(e)}")
logging.error(traceback.format_exc())
return None, {}
def create_draft(self, to: str, subject: str, body: str, cc: list[str] | None = None) -> dict | None:
"""
Create a draft email message.
Args:
to (str): Email address of the recipient
subject (str): Subject line of the email
body (str): Body content of the email
cc (list[str], optional): List of email addresses to CC
Returns:
dict: Draft message data including the draft ID if successful
None: If creation fails
"""
try:
# Create message body
message = {
'to': to,
'subject': subject,
'text': body,
}
if cc:
message['cc'] = ','.join(cc)
# Create the message in MIME format
mime_message = MIMEText(body)
mime_message['to'] = to
mime_message['subject'] = subject
if cc:
mime_message['cc'] = ','.join(cc)
# Encode the message
raw_message = base64.urlsafe_b64encode(mime_message.as_bytes()).decode('utf-8')
# Create the draft
draft = self.service.users().drafts().create(
userId='me',
body={
'message': {
'raw': raw_message
}
}
).execute()
return draft
except Exception as e:
logging.error(f"Error creating draft: {str(e)}")
logging.error(traceback.format_exc())
return None
def delete_draft(self, draft_id: str) -> bool:
"""
Delete a draft email message.
Args:
draft_id (str): The ID of the draft to delete
Returns:
bool: True if deletion was successful, False otherwise
"""
try:
self.service.users().drafts().delete(
userId='me',
id=draft_id
).execute()
return True
except Exception as e:
logging.error(f"Error deleting draft {draft_id}: {str(e)}")
logging.error(traceback.format_exc())
return False
def create_reply(self, original_message: dict, reply_body: str, send: bool = False, cc: list[str] | None = None) -> dict | None:
"""
Create a reply to an email message and either send it or save as draft.
Args:
original_message (dict): The original message data (as returned by get_email_by_id)
reply_body (str): Body content of the reply
send (bool): If True, sends the reply immediately. If False, saves as draft.
cc (list[str], optional): List of email addresses to CC
Returns:
dict: Sent message or draft data if successful
None: If operation fails
"""
try:
to_address = original_message.get('from')
if not to_address:
raise ValueError("Could not determine original sender's address")
subject = original_message.get('subject', '')
if not subject.lower().startswith('re:'):
subject = f"Re: {subject}"
original_date = original_message.get('date', '')
original_from = original_message.get('from', '')
original_body = original_message.get('body', '')
full_reply_body = (
f"{reply_body}\n\n"
f"On {original_date}, {original_from} wrote:\n"
f"> {original_body.replace('\n', '\n> ') if original_body else '[No message body]'}"
)
mime_message = MIMEText(full_reply_body)
mime_message['to'] = to_address
mime_message['subject'] = subject
if cc:
mime_message['cc'] = ','.join(cc)
mime_message['In-Reply-To'] = original_message.get('id', '')
mime_message['References'] = original_message.get('id', '')
raw_message = base64.urlsafe_b64encode(mime_message.as_bytes()).decode('utf-8')
message_body = {
'raw': raw_message,
'threadId': original_message.get('threadId') # Ensure it's added to the same thread
}
if send:
# Send the reply immediately
result = self.service.users().messages().send(
userId='me',
body=message_body
).execute()
else:
# Save as draft
result = self.service.users().drafts().create(
userId='me',
body={
'message': message_body
}
).execute()
return result
except Exception as e:
logging.error(f"Error {'sending' if send else 'drafting'} reply: {str(e)}")
logging.error(traceback.format_exc())
return None
def get_attachment(self, message_id: str, attachment_id: str) -> dict | None:
"""
Retrieves a Gmail attachment by its ID.
Args:
message_id (str): The ID of the Gmail message containing the attachment
attachment_id (str): The ID of the attachment to retrieve
Returns:
dict: Attachment data including filename and base64-encoded content
None: If retrieval fails
"""
try:
attachment = self.service.users().messages().attachments().get(
userId='me',
messageId=message_id,
id=attachment_id
).execute()
return {
"size": attachment.get("size"),
"data": attachment.get("data")
}
except Exception as e:
logging.error(f"Error retrieving attachment {attachment_id} from message {message_id}: {str(e)}")
logging.error(traceback.format_exc())
return None
```
--------------------------------------------------------------------------------
/src/mcp_gsuite/tools_gmail.py:
--------------------------------------------------------------------------------
```python
from collections.abc import Sequence
from mcp.types import (
Tool,
TextContent,
ImageContent,
EmbeddedResource,
LoggingLevel,
)
from . import gmail
import json
from . import toolhandler
import base64
def decode_base64_data(file_data):
standard_base64_data = file_data.replace("-", "+").replace("_", "/")
missing_padding = len(standard_base64_data) % 4
if missing_padding:
standard_base64_data += '=' * (4 - missing_padding)
return base64.b64decode(standard_base64_data, validate=True)
class QueryEmailsToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("query_gmail_emails")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="""Query Gmail emails based on an optional search query.
Returns emails in reverse chronological order (newest first).
Returns metadata such as subject and also a short summary of the content.
""",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"query": {
"type": "string",
"description": """Gmail search query (optional). Examples:
- a $string: Search email body, subject, and sender information for $string
- 'is:unread' for unread emails
- 'from:[email protected]' for emails from a specific sender
- 'newer_than:2d' for emails from last 2 days
- 'has:attachment' for emails with attachments
If not provided, returns recent emails without filtering.""",
"required": False
},
"max_results": {
"type": "integer",
"description": "Maximum number of emails to retrieve (1-500)",
"minimum": 1,
"maximum": 500,
"default": 100
}
},
"required": [toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
query = args.get('query')
max_results = args.get('max_results', 100)
emails = gmail_service.query_emails(query=query, max_results=max_results)
return [
TextContent(
type="text",
text=json.dumps(emails, indent=2)
)
]
class GetEmailByIdToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("get_gmail_email")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Retrieves a complete Gmail email message by its ID, including the full message body and attachment IDs.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"email_id": {
"type": "string",
"description": "The ID of the Gmail message to retrieve"
}
},
"required": ["email_id", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if "email_id" not in args:
raise RuntimeError("Missing required argument: email_id")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
email, attachments = gmail_service.get_email_by_id_with_attachments(args["email_id"])
if email is None:
return [
TextContent(
type="text",
text=f"Failed to retrieve email with ID: {args['email_id']}"
)
]
email["attachments"] = attachments
return [
TextContent(
type="text",
text=json.dumps(email, indent=2)
)
]
class BulkGetEmailsByIdsToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("bulk_get_gmail_emails")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Retrieves multiple Gmail email messages by their IDs in a single request, including the full message bodies and attachment IDs.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"email_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of Gmail message IDs to retrieve"
}
},
"required": ["email_ids", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if "email_ids" not in args:
raise RuntimeError("Missing required argument: email_ids")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
results = []
for email_id in args["email_ids"]:
email, attachments = gmail_service.get_email_by_id_with_attachments(email_id)
if email is not None:
email["attachments"] = attachments
results.append(email)
if not results:
return [
TextContent(
type="text",
text=f"Failed to retrieve any emails from the provided IDs"
)
]
return [
TextContent(
type="text",
text=json.dumps(results, indent=2)
)
]
class CreateDraftToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("create_gmail_draft")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="""Creates a draft email message from scratch in Gmail with specified recipient, subject, body, and optional CC recipients.
Do NOT use this tool when you want to draft or send a REPLY to an existing message. This tool does NOT include any previous message content. Use the reply_gmail_email tool
with send=False instead."
""",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"to": {
"type": "string",
"description": "Email address of the recipient"
},
"subject": {
"type": "string",
"description": "Subject line of the email"
},
"body": {
"type": "string",
"description": "Body content of the email"
},
"cc": {
"type": "array",
"items": {
"type": "string"
},
"description": "Optional list of email addresses to CC"
}
},
"required": ["to", "subject", "body", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
required = ["to", "subject", "body"]
if not all(key in args for key in required):
raise RuntimeError(f"Missing required arguments: {', '.join(required)}")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
draft = gmail_service.create_draft(
to=args["to"],
subject=args["subject"],
body=args["body"],
cc=args.get("cc")
)
if draft is None:
return [
TextContent(
type="text",
text="Failed to create draft email"
)
]
return [
TextContent(
type="text",
text=json.dumps(draft, indent=2)
)
]
class DeleteDraftToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("delete_gmail_draft")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Deletes a Gmail draft message by its ID. This action cannot be undone.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"draft_id": {
"type": "string",
"description": "The ID of the draft to delete"
}
},
"required": ["draft_id", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if "draft_id" not in args:
raise RuntimeError("Missing required argument: draft_id")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
success = gmail_service.delete_draft(args["draft_id"])
return [
TextContent(
type="text",
text="Successfully deleted draft" if success else f"Failed to delete draft with ID: {args['draft_id']}"
)
]
class ReplyEmailToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("reply_gmail_email")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="""Creates a reply to an existing Gmail email message and either sends it or saves as draft.
Use this tool if you want to draft a reply. Use the 'cc' argument if you want to perform a "reply all".
""",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"original_message_id": {
"type": "string",
"description": "The ID of the Gmail message to reply to"
},
"reply_body": {
"type": "string",
"description": "The body content of your reply message"
},
"send": {
"type": "boolean",
"description": "If true, sends the reply immediately. If false, saves as draft.",
"default": False
},
"cc": {
"type": "array",
"items": {
"type": "string"
},
"description": "Optional list of email addresses to CC on the reply"
}
},
"required": ["original_message_id", "reply_body", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if not all(key in args for key in ["original_message_id", "reply_body"]):
raise RuntimeError("Missing required arguments: original_message_id and reply_body")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
# First get the original message to extract necessary information
original_message = gmail_service.get_email_by_id(args["original_message_id"])
if original_message is None:
return [
TextContent(
type="text",
text=f"Failed to retrieve original message with ID: {args['original_message_id']}"
)
]
# Create and send/draft the reply
result = gmail_service.create_reply(
original_message=original_message,
reply_body=args.get("reply_body", ""),
send=args.get("send", False),
cc=args.get("cc")
)
if result is None:
return [
TextContent(
type="text",
text=f"Failed to {'send' if args.get('send', True) else 'draft'} reply email"
)
]
return [
TextContent(
type="text",
text=json.dumps(result, indent=2)
)
]
class GetAttachmentToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("get_gmail_attachment")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Retrieves a Gmail attachment by its ID.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"message_id": {
"type": "string",
"description": "The ID of the Gmail message containing the attachment"
},
"attachment_id": {
"type": "string",
"description": "The ID of the attachment to retrieve"
},
"mime_type": {
"type": "string",
"description": "The MIME type of the attachment"
},
"filename": {
"type": "string",
"description": "The filename of the attachment"
},
"save_to_disk": {
"type": "string",
"description": "The fullpath to save the attachment to disk. If not provided, the attachment is returned as a resource."
}
},
"required": ["message_id", "attachment_id", "mime_type", "filename", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if "message_id" not in args:
raise RuntimeError("Missing required argument: message_id")
if "attachment_id" not in args:
raise RuntimeError("Missing required argument: attachment_id")
if "mime_type" not in args:
raise RuntimeError("Missing required argument: mime_type")
if "filename" not in args:
raise RuntimeError("Missing required argument: filename")
filename = args["filename"]
mime_type = args["mime_type"]
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
attachment_data = gmail_service.get_attachment(args["message_id"], args["attachment_id"])
if attachment_data is None:
return [
TextContent(
type="text",
text=f"Failed to retrieve attachment with ID: {args['attachment_id']} from message: {args['message_id']}"
)
]
file_data = attachment_data["data"]
attachment_url = f"attachment://gmail/{args['message_id']}/{args['attachment_id']}/{filename}"
if args.get("save_to_disk"):
decoded_data = decode_base64_data(file_data)
with open(args["save_to_disk"], "wb") as f:
f.write(decoded_data)
return [
TextContent(
type="text",
text=f"Attachment saved to disk: {args['save_to_disk']}"
)
]
return [
EmbeddedResource(
type="resource",
resource={
"blob": file_data,
"uri": attachment_url,
"mimeType": mime_type,
},
)
]
class BulkSaveAttachmentsToolHandler(toolhandler.ToolHandler):
def __init__(self):
super().__init__("bulk_save_gmail_attachments")
def get_tool_description(self) -> Tool:
return Tool(
name=self.name,
description="Saves multiple Gmail attachments to disk by their message IDs and attachment IDs in a single request.",
inputSchema={
"type": "object",
"properties": {
"__user_id__": self.get_user_id_arg_schema(),
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"message_id": {
"type": "string",
"description": "ID of the Gmail message containing the attachment"
},
"part_id": {
"type": "string",
"description": "ID of the part containing the attachment"
},
"save_path": {
"type": "string",
"description": "Path where the attachment should be saved"
}
},
"required": ["message_id", "part_id", "save_path"]
}
}
},
"required": ["attachments", toolhandler.USER_ID_ARG]
}
)
def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
if "attachments" not in args:
raise RuntimeError("Missing required argument: attachments")
user_id = args.get(toolhandler.USER_ID_ARG)
if not user_id:
raise RuntimeError(f"Missing required argument: {toolhandler.USER_ID_ARG}")
gmail_service = gmail.GmailService(user_id=user_id)
results = []
for attachment_info in args["attachments"]:
# get attachment data from message_id and part_id
message, attachments = gmail_service.get_email_by_id_with_attachments(
attachment_info["message_id"]
)
if message is None:
results.append(
TextContent(
type="text",
text=f"Failed to retrieve message with ID: {attachment_info['message_id']}"
)
)
continue
# get attachment_id from part_id
attachment_id = attachments[attachment_info["part_id"]]["attachmentId"]
attachment_data = gmail_service.get_attachment(
attachment_info["message_id"],
attachment_id
)
if attachment_data is None:
results.append(
TextContent(
type="text",
text=f"Failed to retrieve attachment with ID: {attachment_id} from message: {attachment_info['message_id']}"
)
)
continue
file_data = attachment_data["data"]
try:
decoded_data = decode_base64_data(file_data)
with open(attachment_info["save_path"], "wb") as f:
f.write(decoded_data)
results.append(
TextContent(
type="text",
text=f"Attachment saved to: {attachment_info['save_path']}"
)
)
except Exception as e:
results.append(
TextContent(
type="text",
text=f"Failed to save attachment to {attachment_info['save_path']}: {str(e)}"
)
)
continue
return results
```
--------------------------------------------------------------------------------
/google-calendar-api-openapi-spec.yaml:
--------------------------------------------------------------------------------
```yaml
swagger: "2.0"
info:
title: Calendar
description: Manipulates events and other calendar data.
contact:
name: Google
url: https://google.com
version: v3
host: www.googleapis.com
basePath: /calendar/v3
schemes:
- http
produces:
- application/json
consumes:
- application/json
paths:
/calendars:
post:
summary: Create Calendar
description: Creates a secondary calendar
operationId: calendar.calendars.insert
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
responses:
200:
description: OK
tags:
- Calendar
/calendars/{calendarId}:
delete:
summary: CreaDeletete Calendar
description: Deletes a secondary calendar
operationId: calendar.calendars.delete
parameters:
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Calendar
get:
summary: Get Calendar
description: Returns metadata for a calendar
operationId: calendar.calendars.get
parameters:
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Calendar
patch:
summary: Update Calendar
description: Updates metadata for a calendar
operationId: calendar.calendars.patch
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Calendar
put:
summary: Update Calendar
description: Updates metadata for a calendar
operationId: calendar.calendars.update
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Calendar
/calendars/{calendarId}/acl:
get:
summary: Get Calendar ACL
description: Returns the rules in the access control list for the calendar
operationId: calendar.acl.list
parameters:
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: maxResults
description: Maximum number of entries returned on one result page
- in: query
name: pageToken
description: Token specifying which result page to return
- in: query
name: showDeleted
description: Whether to include deleted ACLs in the result
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
responses:
200:
description: OK
tags:
- Calendar ACL
post:
summary: Create Calendar ACL
description: Creates an access control rule
operationId: calendar.acl.insert
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Calendar ACL
/calendars/{calendarId}/acl/watch:
post:
summary: Watch Calendar ACL
description: Watch for changes to ACL resources
operationId: calendar.acl.watch
parameters:
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: maxResults
description: Maximum number of entries returned on one result page
- in: query
name: pageToken
description: Token specifying which result page to return
- in: body
name: resource
schema:
$ref: '#/definitions/holder'
- in: query
name: showDeleted
description: Whether to include deleted ACLs in the result
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
responses:
200:
description: OK
tags:
- Calendar ACL
/calendars/{calendarId}/acl/{ruleId}:
delete:
summary: Delete Calendar ACL
description: Deletes an access control rule
operationId: calendar.acl.delete
parameters:
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: ruleId
description: ACL rule identifier
responses:
200:
description: OK
tags:
- Calendar ACL
get:
summary: Get Calendar ACL
description: Returns an access control rule
operationId: calendar.acl.get
parameters:
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: ruleId
description: ACL rule identifier
responses:
200:
description: OK
tags:
- Calendar ACL
patch:
summary: Update Calendar ACL
description: Updates an access control rule
operationId: calendar.acl.patch
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: ruleId
description: ACL rule identifier
responses:
200:
description: OK
tags:
- Calendar ACL
put:
summary: Update Calendar ACL
description: Updates an access control rule
operationId: calendar.acl.update
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: ruleId
description: ACL rule identifier
responses:
200:
description: OK
tags:
- Calendar ACL
/calendars/{calendarId}/clear:
post:
summary: Clear Primary Calendar
description: Clears a primary calendar
operationId: calendar.calendars.clear
parameters:
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Calendar
/calendars/{calendarId}/events:
get:
summary: Get Events
description: Returns events on the specified calendar
operationId: calendar.events.list
parameters:
- in: query
name: alwaysIncludeEmail
description: Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available (i
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: iCalUID
description: Specifies event ID in the iCalendar format to be included in
the response
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: maxResults
description: Maximum number of events returned on one result page
- in: query
name: orderBy
description: The order of the events returned in the result
- in: query
name: pageToken
description: Token specifying which result page to return
- in: query
name: privateExtendedProperty
description: Extended properties constraint specified as propertyName=value
- in: query
name: q
description: Free text search terms to find events that match these terms
in any field, except for extended properties
- in: query
name: sharedExtendedProperty
description: Extended properties constraint specified as propertyName=value
- in: query
name: showDeleted
description: Whether to include deleted events (with status equals "cancelled")
in the result
- in: query
name: showHiddenInvitations
description: Whether to include hidden invitations in the result
- in: query
name: singleEvents
description: Whether to expand recurring events into instances and only return
single one-off events and instances of recurring events, but not the underlying
recurring events themselves
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
- in: query
name: timeMax
description: Upper bound (exclusive) for an event's start time to filter by
- in: query
name: timeMin
description: Lower bound (inclusive) for an event's end time to filter by
- in: query
name: timeZone
description: Time zone used in the response
- in: query
name: updatedMin
description: Lower bound for an event's last modification time (as a RFC3339
timestamp) to filter by
responses:
200:
description: OK
tags:
- Event
post:
summary: Create Event
description: Creates an event
operationId: calendar.events.insert
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: sendNotifications
description: Whether to send notifications about the creation of the new event
- in: query
name: supportsAttachments
description: Whether API client performing operation supports event attachments
responses:
200:
description: OK
tags:
- Event
/calendars/{calendarId}/events/import:
post:
summary: Import Event
description: Imports an event
operationId: calendar.events.import
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: supportsAttachments
description: Whether API client performing operation supports event attachments
responses:
200:
description: OK
tags:
- Event
/calendars/{calendarId}/events/quickAdd:
post:
summary: Create Event
description: Creates an event based on a simple text string
operationId: calendar.events.quickAdd
parameters:
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: sendNotifications
description: Whether to send notifications about the creation of the event
- in: query
name: text
description: The text describing the event to be created
responses:
200:
description: OK
tags:
- Event
/calendars/{calendarId}/events/watch:
post:
summary: Watch Event
description: Watch for changes to Events resources
operationId: calendar.events.watch
parameters:
- in: query
name: alwaysIncludeEmail
description: Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available (i
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: iCalUID
description: Specifies event ID in the iCalendar format to be included in
the response
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: maxResults
description: Maximum number of events returned on one result page
- in: query
name: orderBy
description: The order of the events returned in the result
- in: query
name: pageToken
description: Token specifying which result page to return
- in: query
name: privateExtendedProperty
description: Extended properties constraint specified as propertyName=value
- in: query
name: q
description: Free text search terms to find events that match these terms
in any field, except for extended properties
- in: body
name: resource
schema:
$ref: '#/definitions/holder'
- in: query
name: sharedExtendedProperty
description: Extended properties constraint specified as propertyName=value
- in: query
name: showDeleted
description: Whether to include deleted events (with status equals "cancelled")
in the result
- in: query
name: showHiddenInvitations
description: Whether to include hidden invitations in the result
- in: query
name: singleEvents
description: Whether to expand recurring events into instances and only return
single one-off events and instances of recurring events, but not the underlying
recurring events themselves
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
- in: query
name: timeMax
description: Upper bound (exclusive) for an event's start time to filter by
- in: query
name: timeMin
description: Lower bound (inclusive) for an event's end time to filter by
- in: query
name: timeZone
description: Time zone used in the response
- in: query
name: updatedMin
description: Lower bound for an event's last modification time (as a RFC3339
timestamp) to filter by
responses:
200:
description: OK
tags:
- Event
/calendars/{calendarId}/events/{eventId}:
delete:
summary: Delete Event
description: Deletes an event
operationId: calendar.events.delete
parameters:
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: eventId
description: Event identifier
- in: query
name: sendNotifications
description: Whether to send notifications about the deletion of the event
responses:
200:
description: OK
tags:
- Event
get:
summary: Get Event
description: Returns an event
operationId: calendar.events.get
parameters:
- in: query
name: alwaysIncludeEmail
description: Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available (i
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: eventId
description: Event identifier
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: timeZone
description: Time zone used in the response
responses:
200:
description: OK
tags:
- Event
patch:
summary: Update Event
description: Updates an event
operationId: calendar.events.patch
parameters:
- in: query
name: alwaysIncludeEmail
description: Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available (i
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: eventId
description: Event identifier
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: sendNotifications
description: Whether to send notifications about the event update (e
- in: query
name: supportsAttachments
description: Whether API client performing operation supports event attachments
responses:
200:
description: OK
tags:
- Event
put:
summary: Update Event
description: Updates an event
operationId: calendar.events.update
parameters:
- in: query
name: alwaysIncludeEmail
description: Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available (i
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: eventId
description: Event identifier
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: sendNotifications
description: Whether to send notifications about the event update (e
- in: query
name: supportsAttachments
description: Whether API client performing operation supports event attachments
responses:
200:
description: OK
tags:
- Event
/calendars/{calendarId}/events/{eventId}/instances:
get:
summary: Get Event Instance
description: Returns instances of the specified recurring event
operationId: calendar.events.instances
parameters:
- in: query
name: alwaysIncludeEmail
description: Whether to always include a value in the email field for the
organizer, creator and attendees, even if no real email is available (i
- in: path
name: calendarId
description: Calendar identifier
- in: path
name: eventId
description: Recurring event identifier
- in: query
name: maxAttendees
description: The maximum number of attendees to include in the response
- in: query
name: maxResults
description: Maximum number of events returned on one result page
- in: query
name: originalStart
description: The original start time of the instance in the result
- in: query
name: pageToken
description: Token specifying which result page to return
- in: query
name: showDeleted
description: Whether to include deleted events (with status equals "cancelled")
in the result
- in: query
name: timeMax
description: Upper bound (exclusive) for an event's start time to filter by
- in: query
name: timeMin
description: Lower bound (inclusive) for an event's end time to filter by
- in: query
name: timeZone
description: Time zone used in the response
responses:
200:
description: OK
tags:
- Event
/calendars/{calendarId}/events/{eventId}/move:
post:
summary: Move Event
description: Moves an event to another calendar, i
operationId: calendar.events.move
parameters:
- in: path
name: calendarId
description: Calendar identifier of the source calendar where the event currently
is on
- in: query
name: destination
description: Calendar identifier of the target calendar where the event is
to be moved to
- in: path
name: eventId
description: Event identifier
- in: query
name: sendNotifications
description: Whether to send notifications about the change of the event's
organizer
responses:
200:
description: OK
tags:
- Event
/channels/stop:
post:
summary: Stop Watching Resource
description: Stop watching resources through this channel
operationId: calendar.channels.stop
parameters:
- in: body
name: resource
schema:
$ref: '#/definitions/holder'
responses:
200:
description: OK
tags:
- Watch
/colors:
get:
summary: Get Colors
description: Returns the color definitions for calendars and events
operationId: calendar.colors.get
responses:
200:
description: OK
tags:
- Color
/freeBusy:
post:
summary: Return Free/Busy Information
description: Returns free/busy information for a set of calendars
operationId: calendar.freebusy.query
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
responses:
200:
description: OK
tags:
- Free/Busy
/users/me/calendarList:
get:
summary: Return Entries
description: Returns entries on the user's calendar list
operationId: calendar.calendarList.list
parameters:
- in: query
name: maxResults
description: Maximum number of entries returned on one result page
- in: query
name: minAccessRole
description: The minimum access role for the user in the returned entries
- in: query
name: pageToken
description: Token specifying which result page to return
- in: query
name: showDeleted
description: Whether to include deleted calendar list entries in the result
- in: query
name: showHidden
description: Whether to show hidden entries
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
responses:
200:
description: OK
tags:
- Event
post:
summary: Add Entry
description: Adds an entry to the user's calendar list
operationId: calendar.calendarList.insert
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: query
name: colorRgbFormat
description: Whether to use the foregroundColor and backgroundColor fields
to write the calendar colors (RGB)
responses:
200:
description: OK
tags:
- Event
/users/me/calendarList/watch:
post:
summary: Watch Entry
description: Watch for changes to CalendarList resources
operationId: calendar.calendarList.watch
parameters:
- in: query
name: maxResults
description: Maximum number of entries returned on one result page
- in: query
name: minAccessRole
description: The minimum access role for the user in the returned entries
- in: query
name: pageToken
description: Token specifying which result page to return
- in: body
name: resource
schema:
$ref: '#/definitions/holder'
- in: query
name: showDeleted
description: Whether to include deleted calendar list entries in the result
- in: query
name: showHidden
description: Whether to show hidden entries
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
responses:
200:
description: OK
tags:
- Event
/users/me/calendarList/{calendarId}:
delete:
summary: Delete Entry
description: Deletes an entry on the user's calendar list
operationId: calendar.calendarList.delete
parameters:
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Event
get:
summary: Get Entry
description: Returns an entry on the user's calendar list
operationId: calendar.calendarList.get
parameters:
- in: path
name: calendarId
description: Calendar identifier
responses:
200:
description: OK
tags:
- Event
patch:
summary: Update Entry
description: Updates an entry on the user's calendar list
operationId: calendar.calendarList.patch
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: colorRgbFormat
description: Whether to use the foregroundColor and backgroundColor fields
to write the calendar colors (RGB)
responses:
200:
description: OK
tags:
- Event
put:
summary: Update Entry
description: Updates an entry on the user's calendar list
operationId: calendar.calendarList.update
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: calendarId
description: Calendar identifier
- in: query
name: colorRgbFormat
description: Whether to use the foregroundColor and backgroundColor fields
to write the calendar colors (RGB)
responses:
200:
description: OK
tags:
- Event
/users/me/settings:
get:
summary: Get Settings
description: Returns all user settings for the authenticated user
operationId: calendar.settings.list
parameters:
- in: query
name: maxResults
description: Maximum number of entries returned on one result page
- in: query
name: pageToken
description: Token specifying which result page to return
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
responses:
200:
description: OK
tags:
- Setting
/users/me/settings/watch:
post:
summary: Watch Settings
description: Watch for changes to Settings resources
operationId: calendar.settings.watch
parameters:
- in: query
name: maxResults
description: Maximum number of entries returned on one result page
- in: query
name: pageToken
description: Token specifying which result page to return
- in: body
name: resource
schema:
$ref: '#/definitions/holder'
- in: query
name: syncToken
description: Token obtained from the nextSyncToken field returned on the last
page of results from the previous list request
responses:
200:
description: OK
tags:
- Setting
/users/me/settings/{setting}:
get:
summary: Get Setting
description: Returns a single user setting
operationId: calendar.settings.get
parameters:
- in: path
name: setting
description: The id of the user setting
responses:
200:
description: OK
tags:
- Setting
definitions:
Acl:
properties:
etag:
description: This is a default description.
type: parameters
items:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
nextPageToken:
description: This is a default description.
type: parameters
nextSyncToken:
description: This is a default description.
type: parameters
AclRule:
properties:
etag:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
role:
description: This is a default description.
type: parameters
scope:
description: This is a default description.
type: parameters
Calendar:
properties:
description:
description: This is a default description.
type: parameters
etag:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
location:
description: This is a default description.
type: parameters
summary:
description: This is a default description.
type: parameters
timeZone:
description: This is a default description.
type: parameters
CalendarList:
properties:
etag:
description: This is a default description.
type: parameters
items:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
nextPageToken:
description: This is a default description.
type: parameters
nextSyncToken:
description: This is a default description.
type: parameters
CalendarListEntry:
properties:
accessRole:
description: This is a default description.
type: parameters
backgroundColor:
description: This is a default description.
type: parameters
colorId:
description: This is a default description.
type: parameters
defaultReminders:
description: This is a default description.
type: parameters
deleted:
description: This is a default description.
type: parameters
description:
description: This is a default description.
type: parameters
etag:
description: This is a default description.
type: parameters
foregroundColor:
description: This is a default description.
type: parameters
hidden:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
location:
description: This is a default description.
type: parameters
notificationSettings:
description: This is a default description.
type: parameters
primary:
description: This is a default description.
type: parameters
selected:
description: This is a default description.
type: parameters
summary:
description: This is a default description.
type: parameters
summaryOverride:
description: This is a default description.
type: parameters
timeZone:
description: This is a default description.
type: parameters
CalendarNotification:
properties:
method:
description: This is a default description.
type: parameters
type:
description: This is a default description.
type: parameters
Channel:
properties:
address:
description: This is a default description.
type: parameters
expiration:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
params:
description: This is a default description.
type: parameters
payload:
description: This is a default description.
type: parameters
resourceId:
description: This is a default description.
type: parameters
resourceUri:
description: This is a default description.
type: parameters
token:
description: This is a default description.
type: parameters
type:
description: This is a default description.
type: parameters
ColorDefinition:
properties:
background:
description: This is a default description.
type: parameters
foreground:
description: This is a default description.
type: parameters
Colors:
properties:
calendar:
description: This is a default description.
type: parameters
event:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
updated:
description: This is a default description.
type: parameters
Error:
properties:
domain:
description: This is a default description.
type: parameters
reason:
description: This is a default description.
type: parameters
Event:
properties:
anyoneCanAddSelf:
description: This is a default description.
type: parameters
attachments:
description: This is a default description.
type: parameters
attendees:
description: This is a default description.
type: parameters
attendeesOmitted:
description: This is a default description.
type: parameters
colorId:
description: This is a default description.
type: parameters
created:
description: This is a default description.
type: parameters
creator:
description: This is a default description.
type: parameters
description:
description: This is a default description.
type: parameters
endTimeUnspecified:
description: This is a default description.
type: parameters
etag:
description: This is a default description.
type: parameters
extendedProperties:
description: This is a default description.
type: parameters
gadget:
description: This is a default description.
type: parameters
guestsCanInviteOthers:
description: This is a default description.
type: parameters
guestsCanModify:
description: This is a default description.
type: parameters
guestsCanSeeOtherGuests:
description: This is a default description.
type: parameters
hangoutLink:
description: This is a default description.
type: parameters
htmlLink:
description: This is a default description.
type: parameters
iCalUID:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
location:
description: This is a default description.
type: parameters
locked:
description: This is a default description.
type: parameters
organizer:
description: This is a default description.
type: parameters
privateCopy:
description: This is a default description.
type: parameters
recurrence:
description: This is a default description.
type: parameters
recurringEventId:
description: This is a default description.
type: parameters
reminders:
description: This is a default description.
type: parameters
sequence:
description: This is a default description.
type: parameters
source:
description: This is a default description.
type: parameters
status:
description: This is a default description.
type: parameters
summary:
description: This is a default description.
type: parameters
transparency:
description: This is a default description.
type: parameters
updated:
description: This is a default description.
type: parameters
visibility:
description: This is a default description.
type: parameters
EventAttachment:
properties:
fileId:
description: This is a default description.
type: parameters
fileUrl:
description: This is a default description.
type: parameters
iconLink:
description: This is a default description.
type: parameters
mimeType:
description: This is a default description.
type: parameters
title:
description: This is a default description.
type: parameters
EventAttendee:
properties:
additionalGuests:
description: This is a default description.
type: parameters
comment:
description: This is a default description.
type: parameters
displayName:
description: This is a default description.
type: parameters
email:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
optional:
description: This is a default description.
type: parameters
organizer:
description: This is a default description.
type: parameters
resource:
description: This is a default description.
type: parameters
responseStatus:
description: This is a default description.
type: parameters
self:
description: This is a default description.
type: parameters
EventDateTime:
properties:
date:
description: This is a default description.
type: parameters
dateTime:
description: This is a default description.
type: parameters
timeZone:
description: This is a default description.
type: parameters
EventReminder:
properties:
method:
description: This is a default description.
type: parameters
minutes:
description: This is a default description.
type: parameters
Events:
properties:
accessRole:
description: This is a default description.
type: parameters
defaultReminders:
description: This is a default description.
type: parameters
description:
description: This is a default description.
type: parameters
etag:
description: This is a default description.
type: parameters
items:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
nextPageToken:
description: This is a default description.
type: parameters
nextSyncToken:
description: This is a default description.
type: parameters
summary:
description: This is a default description.
type: parameters
timeZone:
description: This is a default description.
type: parameters
updated:
description: This is a default description.
type: parameters
FreeBusyCalendar:
properties:
busy:
description: This is a default description.
type: parameters
errors:
description: This is a default description.
type: parameters
FreeBusyGroup:
properties:
calendars:
description: This is a default description.
type: parameters
errors:
description: This is a default description.
type: parameters
FreeBusyRequest:
properties:
calendarExpansionMax:
description: This is a default description.
type: parameters
groupExpansionMax:
description: This is a default description.
type: parameters
items:
description: This is a default description.
type: parameters
timeMax:
description: This is a default description.
type: parameters
timeMin:
description: This is a default description.
type: parameters
timeZone:
description: This is a default description.
type: parameters
FreeBusyRequestItem:
properties:
id:
description: This is a default description.
type: parameters
FreeBusyResponse:
properties:
calendars:
description: This is a default description.
type: parameters
groups:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
timeMax:
description: This is a default description.
type: parameters
timeMin:
description: This is a default description.
type: parameters
Setting:
properties:
etag:
description: This is a default description.
type: parameters
id:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
value:
description: This is a default description.
type: parameters
Settings:
properties:
etag:
description: This is a default description.
type: parameters
items:
description: This is a default description.
type: parameters
kind:
description: This is a default description.
type: parameters
nextPageToken:
description: This is a default description.
type: parameters
nextSyncToken:
description: This is a default description.
type: parameters
TimePeriod:
properties:
end:
description: This is a default description.
type: parameters
start:
description: This is a default description.
type: parameters
```
--------------------------------------------------------------------------------
/gmail-api-openapi-spec.yaml:
--------------------------------------------------------------------------------
```yaml
agger: "2.0"
info:
title: Gmail
description: Access Gmail mailboxes including sending user email.
contact:
name: Google
url: https://google.com
version: v1
host: www.googleapis.com
basePath: /gmail/v1/users
schemes:
- http
produces:
- application/json
consumes:
- application/json
paths:
/{userId}/drafts:
get:
summary: Get Drafts
description: Lists the drafts in the user's mailbox
operationId: gmail.users.drafts.list
parameters:
- in: query
name: includeSpamTrash
description: Include drafts from SPAM and TRASH in the results
- in: query
name: maxResults
description: Maximum number of drafts to return
- in: query
name: pageToken
description: Page token to retrieve a specific page of results in the list
- in: query
name: q
description: Only return draft messages matching the specified query
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
post:
summary: Update Draft
description: Creates a new draft with the DRAFT label
operationId: gmail.users.drafts.create
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/drafts/send:
post:
summary: Send Draft
description: Sends the specified, existing draft to the recipients in the To,
Cc, and Bcc headers
operationId: gmail.users.drafts.send
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/drafts/{id}:
delete:
summary: Delete Draft
description: Immediately and permanently deletes the specified draft
operationId: gmail.users.drafts.delete
parameters:
- in: path
name: id
description: The ID of the draft to delete
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
get:
summary: Get Draft
description: Gets the specified draft
operationId: gmail.users.drafts.get
parameters:
- in: query
name: format
description: The format to return the draft in
- in: path
name: id
description: The ID of the draft to retrieve
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
put:
summary: Update Draft
description: Replaces a draft's content
operationId: gmail.users.drafts.update
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: id
description: The ID of the draft to update
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/history:
get:
summary: Get History
description: Lists the history of all changes to the given mailbox
operationId: gmail.users.history.list
parameters:
- in: query
name: historyTypes
description: History types to be returned by the function
- in: query
name: labelId
description: Only return messages with a label matching the ID
- in: query
name: maxResults
description: The maximum number of history records to return
- in: query
name: pageToken
description: Page token to retrieve a specific page of results in the list
- in: query
name: startHistoryId
description: Required
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- History
/{userId}/labels:
get:
summary: Get Labels
description: Lists all labels in the user's mailbox
operationId: gmail.users.labels.list
parameters:
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Label
post:
summary: Create Label
description: Creates a new label
operationId: gmail.users.labels.create
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Label
/{userId}/labels/{id}:
delete:
summary: Delete Lbel
description: Immediately and permanently deletes the specified label and removes
it from any messages and threads that it is applied to
operationId: gmail.users.labels.delete
parameters:
- in: path
name: id
description: The ID of the label to delete
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Label
get:
summary: Get Label
description: Gets the specified label
operationId: gmail.users.labels.get
parameters:
- in: path
name: id
description: The ID of the label to retrieve
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Label
patch:
summary: Update Label
description: Updates the specified label
operationId: gmail.users.labels.patch
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: id
description: The ID of the label to update
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Label
put:
summary: Update Label
description: Updates the specified label
operationId: gmail.users.labels.update
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: id
description: The ID of the label to update
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Label
/{userId}/messages:
get:
summary: Get Message
description: Lists the messages in the user's mailbox
operationId: gmail.users.messages.list
parameters:
- in: query
name: includeSpamTrash
description: Include messages from SPAM and TRASH in the results
- in: query
name: labelIds
description: Only return messages with labels that match all of the specified
label IDs
- in: query
name: maxResults
description: Maximum number of messages to return
- in: query
name: pageToken
description: Page token to retrieve a specific page of results in the list
- in: query
name: q
description: Only return messages matching the specified query
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
post:
summary: Create Message
description: Directly inserts a message into only this user's mailbox similar
to IMAP APPEND, bypassing most scanning and classification
operationId: gmail.users.messages.insert
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: query
name: deleted
description: Mark the email as permanently deleted (not TRASH) and only visible
in Google Vault to a Vault administrator
- in: query
name: internalDateSource
description: Source for Gmail's internal date of the message
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/batchDelete:
post:
summary: Delete Messages
description: Deletes many messages by message ID
operationId: gmail.users.messages.batchDelete
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/batchModify:
post:
summary: Update Label
description: Modifies the labels on the specified messages
operationId: gmail.users.messages.batchModify
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/import:
post:
summary: Import Message
description: Imports a message into only this user's mailbox, with standard
email delivery scanning and classification similar to receiving via SMTP
operationId: gmail.users.messages.import
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: query
name: deleted
description: Mark the email as permanently deleted (not TRASH) and only visible
in Google Vault to a Vault administrator
- in: query
name: internalDateSource
description: Source for Gmail's internal date of the message
- in: query
name: neverMarkSpam
description: Ignore the Gmail spam classifier decision and never mark this
email as SPAM in the mailbox
- in: query
name: processForCalendar
description: Process calendar invites in the email and add any extracted meetings
to the Google Calendar for this user
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/send:
post:
summary: Send Message
description: Sends the specified message to the recipients in the To, Cc, and
Bcc headers
operationId: gmail.users.messages.send
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/{id}:
delete:
summary: Delete Message
description: Immediately and permanently deletes the specified message
operationId: gmail.users.messages.delete
parameters:
- in: path
name: id
description: The ID of the message to delete
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
get:
summary: Get Message
description: Gets the specified message
operationId: gmail.users.messages.get
parameters:
- in: query
name: format
description: The format to return the message in
- in: path
name: id
description: The ID of the message to retrieve
- in: query
name: metadataHeaders
description: When given and format is METADATA, only include headers specified
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/{id}/modify:
post:
summary: Modify message
description: Modifies the labels on the specified message
operationId: gmail.users.messages.modify
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: id
description: The ID of the message to modify
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/{id}/trash:
post:
summary: Trash Message
description: Moves the specified message to the trash
operationId: gmail.users.messages.trash
parameters:
- in: path
name: id
description: The ID of the message to Trash
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/{id}/untrash:
post:
summary: UnTrash Message
description: Removes the specified message from the trash
operationId: gmail.users.messages.untrash
parameters:
- in: path
name: id
description: The ID of the message to remove from Trash
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/messages/{messageId}/attachments/{id}:
get:
summary: Get Attachments
description: Gets the specified message attachment
operationId: gmail.users.messages.attachments.get
parameters:
- in: path
name: id
description: The ID of the attachment
- in: path
name: messageId
description: The ID of the message containing the attachment
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email
/{userId}/profile:
get:
summary: Get Profile
description: Gets the current user's Gmail profile
operationId: gmail.users.getProfile
parameters:
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- User
/{userId}/settings/autoForwarding:
get:
summary: Get Auto-Forwarding Settings
description: Gets the auto-forwarding setting for the specified account
operationId: gmail.users.settings.getAutoForwarding
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Settings
put:
summary: Update Auto-Forwarding Settings
description: Updates the auto-forwarding setting for the specified account
operationId: gmail.users.settings.updateAutoForwarding
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Settings
/{userId}/settings/filters:
get:
summary: Get Message Filters
description: Lists the message filters of a Gmail user
operationId: gmail.users.settings.filters.list
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Filters
post:
summary: Create Message Filters
description: Creates a filter
operationId: gmail.users.settings.filters.create
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Filters
/{userId}/settings/filters/{id}:
delete:
summary: Delete Message Filter
description: Deletes a filter
operationId: gmail.users.settings.filters.delete
parameters:
- in: path
name: id
description: The ID of the filter to be deleted
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Filters
get:
summary: Get Message Filter
description: Gets a filter
operationId: gmail.users.settings.filters.get
parameters:
- in: path
name: id
description: The ID of the filter to be fetched
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Filters
/{userId}/settings/forwardingAddresses:
get:
summary: Get Forward Addresses
description: Lists the forwarding addresses for the specified account
operationId: gmail.users.settings.forwardingAddresses.list
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Forwarding Address
post:
summary: Create Forward Addresse
description: Creates a forwarding address
operationId: gmail.users.settings.forwardingAddresses.create
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Forwarding Address
/{userId}/settings/forwardingAddresses/{forwardingEmail}:
delete:
summary: Delete Forward Address
description: Deletes the specified forwarding address and revokes any verification
that may have been required
operationId: gmail.users.settings.forwardingAddresses.delete
parameters:
- in: path
name: forwardingEmail
description: The forwarding address to be deleted
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Forwarding Address
get:
summary: GGetet Forward Address
description: Gets the specified forwarding address
operationId: gmail.users.settings.forwardingAddresses.get
parameters:
- in: path
name: forwardingEmail
description: The forwarding address to be retrieved
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Forwarding Address
/{userId}/settings/imap:
get:
summary: Gets IMAP Settings
description: Gets IMAP settings
operationId: gmail.users.settings.getImap
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- IMAP Settings
put:
summary: Update IMAP Setting
description: Updates IMAP settings
operationId: gmail.users.settings.updateImap
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- IMAP Settings
/{userId}/settings/pop:
get:
summary: Gets POP Settings
description: Gets POP settings
operationId: gmail.users.settings.getPop
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- ""
put:
summary: Update IMAP Setting
description: Updates POP settings
operationId: gmail.users.settings.updatePop
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- POP Settings
/{userId}/settings/sendAs:
get:
summary: Send As Alias
description: Lists the send-as aliases for the specified account
operationId: gmail.users.settings.sendAs.list
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Alias
post:
summary: Create Alias
description: Creates a custom "from" send-as alias
operationId: gmail.users.settings.sendAs.create
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Alias
/{userId}/settings/sendAs/{sendAsEmail}:
delete:
summary: Delete Alias
description: Deletes the specified send-as alias
operationId: gmail.users.settings.sendAs.delete
parameters:
- in: path
name: sendAsEmail
description: The send-as alias to be deleted
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Alias
get:
summary: Get Alias
description: Gets the specified send-as alias
operationId: gmail.users.settings.sendAs.get
parameters:
- in: path
name: sendAsEmail
description: The send-as alias to be retrieved
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Alias
patch:
summary: Update Alias
description: Updates a send-as alias
operationId: gmail.users.settings.sendAs.patch
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: sendAsEmail
description: The send-as alias to be updated
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Alias
put:
summary: Update Alias
description: Updates a send-as alias
operationId: gmail.users.settings.sendAs.update
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: sendAsEmail
description: The send-as alias to be updated
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Alias
/{userId}/settings/sendAs/{sendAsEmail}/smimeInfo:
get:
summary: Get S/MIME Configurations
description: Lists S/MIME configs for the specified send-as alias
operationId: gmail.users.settings.sendAs.smimeInfo.list
parameters:
- in: path
name: sendAsEmail
description: The email address that appears in the "From:" header for mail
sent using this alias
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- S/MIME Configuration
post:
summary: Create S/MIME Configurations
description: Insert (upload) the given S/MIME config for the specified send-as
alias
operationId: gmail.users.settings.sendAs.smimeInfo.insert
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: sendAsEmail
description: The email address that appears in the "From:" header for mail
sent using this alias
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- S/MIME Configuration
/{userId}/settings/sendAs/{sendAsEmail}/smimeInfo/{id}:
delete:
summary: Delete S/MIME Configurations
description: Deletes the specified S/MIME config for the specified send-as alias
operationId: gmail.users.settings.sendAs.smimeInfo.delete
parameters:
- in: path
name: id
description: The immutable ID for the SmimeInfo
- in: path
name: sendAsEmail
description: The email address that appears in the "From:" header for mail
sent using this alias
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- S/MIME Configuration
get:
summary: Get S/MIME Configuration
description: Gets the specified S/MIME config for the specified send-as alias
operationId: gmail.users.settings.sendAs.smimeInfo.get
parameters:
- in: path
name: id
description: The immutable ID for the SmimeInfo
- in: path
name: sendAsEmail
description: The email address that appears in the "From:" header for mail
sent using this alias
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- S/MIME Configuration
/{userId}/settings/sendAs/{sendAsEmail}/smimeInfo/{id}/setDefault:
post:
summary: Create Default S/MIME Configurations
description: Sets the default S/MIME config for the specified send-as alias
operationId: gmail.users.settings.sendAs.smimeInfo.setDefault
parameters:
- in: path
name: id
description: The immutable ID for the SmimeInfo
- in: path
name: sendAsEmail
description: The email address that appears in the "From:" header for mail
sent using this alias
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- S/MIME Configuration
/{userId}/settings/sendAs/{sendAsEmail}/verify:
post:
summary: Send Verification Email
description: Sends a verification email to the specified send-as alias address
operationId: gmail.users.settings.sendAs.verify
parameters:
- in: path
name: sendAsEmail
description: The send-as alias to be verified
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Verification
/{userId}/settings/vacation:
get:
summary: Get Vacation Settings
description: Gets vacation responder settings
operationId: gmail.users.settings.getVacation
parameters:
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Vacation Settings
put:
summary: Update Vacation Settings
description: Updates vacation responder settings
operationId: gmail.users.settings.updateVacation
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: User's email address
responses:
200:
description: OK
tags:
- Vacation Settings
/{userId}/stop:
post:
summary: Stop Push Notifications
description: Stop receiving push notifications for the given user mailbox
operationId: gmail.users.stop
parameters:
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Push Notification
/{userId}/threads:
get:
summary: Get Threads
description: Lists the threads in the user's mailbox
operationId: gmail.users.threads.list
parameters:
- in: query
name: includeSpamTrash
description: Include threads from SPAM and TRASH in the results
- in: query
name: labelIds
description: Only return threads with labels that match all of the specified
label IDs
- in: query
name: maxResults
description: Maximum number of threads to return
- in: query
name: pageToken
description: Page token to retrieve a specific page of results in the list
- in: query
name: q
description: Only return threads matching the specified query
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email Thread
/{userId}/threads/{id}:
delete:
summary: Delete Threads
description: Immediately and permanently deletes the specified thread
operationId: gmail.users.threads.delete
parameters:
- in: path
name: id
description: ID of the Thread to delete
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email Thread
get:
summary: Get Threads
description: Gets the specified thread
operationId: gmail.users.threads.get
parameters:
- in: query
name: format
description: The format to return the messages in
- in: path
name: id
description: The ID of the thread to retrieve
- in: query
name: metadataHeaders
description: When given and format is METADATA, only include headers specified
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email Thread
/{userId}/threads/{id}/modify:
post:
summary: Modify Thread labels
description: Modifies the labels applied to the thread
operationId: gmail.users.threads.modify
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: id
description: The ID of the thread to modify
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email Thread
/{userId}/threads/{id}/trash:
post:
summary: Trash Thread
description: Moves the specified thread to the trash
operationId: gmail.users.threads.trash
parameters:
- in: path
name: id
description: The ID of the thread to Trash
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email Thread
/{userId}/threads/{id}/untrash:
post:
summary: UnTrash Threat
description: Removes the specified thread from the trash
operationId: gmail.users.threads.untrash
parameters:
- in: path
name: id
description: The ID of the thread to remove from Trash
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Email Thread
/{userId}/watch:
post:
summary: Send Push Notification
description: Set up or update a push notification watch on the given user mailbox
operationId: gmail.users.watch
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/holder'
- in: path
name: userId
description: The user's email address
responses:
200:
description: OK
tags:
- Push Notification
definitions:
AutoForwarding:
properties:
disposition:
description: This is a default description.
type: post
emailAddress:
description: This is a default description.
type: post
enabled:
description: This is a default description.
type: post
BatchDeleteMessagesRequest:
properties:
ids:
description: This is a default description.
type: post
BatchModifyMessagesRequest:
properties:
addLabelIds:
description: This is a default description.
type: post
ids:
description: This is a default description.
type: post
removeLabelIds:
description: This is a default description.
type: post
Draft:
properties:
id:
description: This is a default description.
type: post
Filter:
properties:
id:
description: This is a default description.
type: post
FilterAction:
properties:
addLabelIds:
description: This is a default description.
type: post
forward:
description: This is a default description.
type: post
removeLabelIds:
description: This is a default description.
type: post
FilterCriteria:
properties:
excludeChats:
description: This is a default description.
type: post
from:
description: This is a default description.
type: post
hasAttachment:
description: This is a default description.
type: post
negatedQuery:
description: This is a default description.
type: post
query:
description: This is a default description.
type: post
size:
description: This is a default description.
type: post
sizeComparison:
description: This is a default description.
type: post
subject:
description: This is a default description.
type: post
to:
description: This is a default description.
type: post
ForwardingAddress:
properties:
forwardingEmail:
description: This is a default description.
type: post
verificationStatus:
description: This is a default description.
type: post
History:
properties:
id:
description: This is a default description.
type: post
labelsAdded:
description: This is a default description.
type: post
labelsRemoved:
description: This is a default description.
type: post
messages:
description: This is a default description.
type: post
messagesAdded:
description: This is a default description.
type: post
messagesDeleted:
description: This is a default description.
type: post
HistoryLabelAdded:
properties:
labelIds:
description: This is a default description.
type: post
HistoryLabelRemoved:
properties:
labelIds:
description: This is a default description.
type: post
HistoryMessageAdded:
properties: []
HistoryMessageDeleted:
properties: []
ImapSettings:
properties:
autoExpunge:
description: This is a default description.
type: post
enabled:
description: This is a default description.
type: post
expungeBehavior:
description: This is a default description.
type: post
maxFolderSize:
description: This is a default description.
type: post
Label:
properties:
id:
description: This is a default description.
type: post
labelListVisibility:
description: This is a default description.
type: post
messageListVisibility:
description: This is a default description.
type: post
messagesTotal:
description: This is a default description.
type: post
messagesUnread:
description: This is a default description.
type: post
name:
description: This is a default description.
type: post
threadsTotal:
description: This is a default description.
type: post
threadsUnread:
description: This is a default description.
type: post
type:
description: This is a default description.
type: post
ListDraftsResponse:
properties:
drafts:
description: This is a default description.
type: post
nextPageToken:
description: This is a default description.
type: post
resultSizeEstimate:
description: This is a default description.
type: post
ListFiltersResponse:
properties:
filter:
description: This is a default description.
type: post
ListForwardingAddressesResponse:
properties:
forwardingAddresses:
description: This is a default description.
type: post
ListHistoryResponse:
properties:
history:
description: This is a default description.
type: post
historyId:
description: This is a default description.
type: post
nextPageToken:
description: This is a default description.
type: post
ListLabelsResponse:
properties:
labels:
description: This is a default description.
type: post
ListMessagesResponse:
properties:
messages:
description: This is a default description.
type: post
nextPageToken:
description: This is a default description.
type: post
resultSizeEstimate:
description: This is a default description.
type: post
ListSendAsResponse:
properties:
sendAs:
description: This is a default description.
type: post
ListSmimeInfoResponse:
properties:
smimeInfo:
description: This is a default description.
type: post
ListThreadsResponse:
properties:
nextPageToken:
description: This is a default description.
type: post
resultSizeEstimate:
description: This is a default description.
type: post
threads:
description: This is a default description.
type: post
Message:
properties:
historyId:
description: This is a default description.
type: post
id:
description: This is a default description.
type: post
internalDate:
description: This is a default description.
type: post
labelIds:
description: This is a default description.
type: post
raw:
description: This is a default description.
type: post
sizeEstimate:
description: This is a default description.
type: post
snippet:
description: This is a default description.
type: post
threadId:
description: This is a default description.
type: post
MessagePart:
properties:
filename:
description: This is a default description.
type: post
headers:
description: This is a default description.
type: post
mimeType:
description: This is a default description.
type: post
partId:
description: This is a default description.
type: post
parts:
description: This is a default description.
type: post
MessagePartBody:
properties:
attachmentId:
description: This is a default description.
type: post
data:
description: This is a default description.
type: post
size:
description: This is a default description.
type: post
MessagePartHeader:
properties:
name:
description: This is a default description.
type: post
value:
description: This is a default description.
type: post
ModifyMessageRequest:
properties:
addLabelIds:
description: This is a default description.
type: post
removeLabelIds:
description: This is a default description.
type: post
ModifyThreadRequest:
properties:
addLabelIds:
description: This is a default description.
type: post
removeLabelIds:
description: This is a default description.
type: post
PopSettings:
properties:
accessWindow:
description: This is a default description.
type: post
disposition:
description: This is a default description.
type: post
Profile:
properties:
emailAddress:
description: This is a default description.
type: post
historyId:
description: This is a default description.
type: post
messagesTotal:
description: This is a default description.
type: post
threadsTotal:
description: This is a default description.
type: post
SendAs:
properties:
displayName:
description: This is a default description.
type: post
isDefault:
description: This is a default description.
type: post
isPrimary:
description: This is a default description.
type: post
replyToAddress:
description: This is a default description.
type: post
sendAsEmail:
description: This is a default description.
type: post
signature:
description: This is a default description.
type: post
treatAsAlias:
description: This is a default description.
type: post
verificationStatus:
description: This is a default description.
type: post
SmimeInfo:
properties:
encryptedKeyPassword:
description: This is a default description.
type: post
expiration:
description: This is a default description.
type: post
id:
description: This is a default description.
type: post
isDefault:
description: This is a default description.
type: post
issuerCn:
description: This is a default description.
type: post
pem:
description: This is a default description.
type: post
pkcs12:
description: This is a default description.
type: post
SmtpMsa:
properties:
host:
description: This is a default description.
type: post
password:
description: This is a default description.
type: post
port:
description: This is a default description.
type: post
securityMode:
description: This is a default description.
type: post
username:
description: This is a default description.
type: post
Thread:
properties:
historyId:
description: This is a default description.
type: post
id:
description: This is a default description.
type: post
messages:
description: This is a default description.
type: post
snippet:
description: This is a default description.
type: post
VacationSettings:
properties:
enableAutoReply:
description: This is a default description.
type: post
endTime:
description: This is a default description.
type: post
responseBodyHtml:
description: This is a default description.
type: post
responseBodyPlainText:
description: This is a default description.
type: post
responseSubject:
description: This is a default description.
type: post
restrictToContacts:
description: This is a default description.
type: post
restrictToDomain:
description: This is a default description.
type: post
startTime:
description: This is a default description.
type: post
WatchRequest:
properties:
labelFilterAction:
description: This is a default description.
type: post
labelIds:
description: This is a default description.
type: post
topicName:
description: This is a default description.
type: post
WatchResponse:
properties:
expiration:
description: This is a default description.
type: post
historyId:
description: This is a default description.
type: post
```