Skip to content

Google Workspace

Build an MCP server with Google Calendar and Drive tools using Keycard delegated access

Build an MCP server with Calendar and Drive tools that let AI agents manage a user’s Google Workspace on their behalf.

  1. Copy your Keycard redirect URL

    In Keycard Console, open Settings → Connection and copy the Redirect URL. You will need this when creating the Google OAuth App.

  2. Create a Google OAuth App

    In Google Cloud Console → APIs & Services → Credentials, create an OAuth 2.0 Client ID (application type: Web application). Add the redirect URL you copied from Keycard to Authorized redirect URIs. Note the Client ID and Client Secret.

    Also enable the Calendar API and Drive API under APIs & Services → Enabled APIs.

  3. Add Google from the catalog

    In Keycard Console, open Resources → Add Resource → Explore Resources and add Google Calendar API and Google Drive API. Enter the Client ID and Client Secret from step 2. This creates the Google OAuth provider and API resources.

    Then open each of these Resources and, in its Scopes section, click Add scope for its Google scope, then Save Changes: https://www.googleapis.com/auth/calendar.readonly for Google Calendar API, https://www.googleapis.com/auth/drive.readonly for Google Drive API. The catalog creates both with no scopes, and Google rejects an authorization request without one.

  4. Register your MCP server Resource

    Navigate to Resources → Add Resource → Add Manually:

    Field Value
    Name Google Workspace MCP Server
    Identifier http://localhost:8000/mcp
    Credential Provider Zone Provider
  5. Create an Application

    Navigate to Applications → Add Application, set Name and Identifier (for example google-workspace-mcp), and click Create application.

    Then, on the application details page:

    • On the Provides tab, click Add provided resource and select your Google Workspace MCP Server.
    • On the Dependencies tab, click Add dependency and select Google Calendar API and Google Drive API.

    After creating the Application, generate client credentials and save them.

The implementation follows the same pattern as the GitHub server. The only differences are the API calls you make with the token and the URLs you pass to grant(), which are the Google Resources’ identifiers.

Terminal window
pip install keycardai-fastmcp fastmcp httpx

Set the environment variables:

Terminal window
export KEYCARD_ISSUER=<keycard-issuer-url>
export MCP_SERVER_URL=http://localhost:8000
export KEYCARD_CLIENT_ID=<your-client-id>
export KEYCARD_CLIENT_SECRET=<your-client-secret>

The server setup is identical to GitHub: just change the server name. The tools file uses the same grant pattern:

"""Google Workspace tools: Calendar and Drive."""
import httpx
from fastmcp import Context, FastMCP
from keycardai.fastmcp import AccessContext, AuthProvider
CALENDAR_API = "https://www.googleapis.com/calendar/v3"
DRIVE_API = "https://www.googleapis.com/drive/v3"
def register_tools(mcp: FastMCP, auth_provider: AuthProvider):
@mcp.tool()
@auth_provider.grant(CALENDAR_API)
async def list_calendar_events(ctx: Context, calendar_id: str = "primary") -> dict:
"""List events from a Google Calendar."""
access_context: AccessContext = await ctx.get_state("keycardai")
if access_context.has_errors():
return {"error": "Token exchange failed", "details": access_context.get_errors()}
token = access_context.access(CALENDAR_API).access_token
# Use token to call Google Calendar API
async with httpx.AsyncClient() as client:
response = await client.get(
f"{CALENDAR_API}/calendars/{calendar_id}/events",
headers={"Authorization": f"Bearer {token}"},
params={"singleEvents": "true", "orderBy": "startTime"},
)
response.raise_for_status()
return {
"events": [
{"summary": e.get("summary"), "start": e.get("start")}
for e in response.json().get("items", [])
]
}
@mcp.tool()
@auth_provider.grant(DRIVE_API)
async def list_drive_files(ctx: Context, q: str | None = None) -> dict:
"""List or search files in Google Drive."""
access_context: AccessContext = await ctx.get_state("keycardai")
if access_context.has_errors():
return {"error": "Token exchange failed", "details": access_context.get_errors()}
token = access_context.access(DRIVE_API).access_token
# Use token to call Google Drive API
async with httpx.AsyncClient() as client:
response = await client.get(
f"{DRIVE_API}/files",
headers={"Authorization": f"Bearer {token}"},
params={"q": q} if q else None,
)
response.raise_for_status()
return {"files": response.json().get("files", [])}

For more error handling patterns, see the SDK’s delegated access example (GitHub): Python SDK examples

  1. Start your server

    Terminal window
    python server.py
  2. Configure your agent

    Add the MCP server to your agent configuration:

    Cursor: add to .cursor/mcp.json:

    {
    "mcpServers": {
    "google-mcp": {
    "url": "http://localhost:8000/mcp"
    }
    }
    }

    Claude Code: run in your terminal:

    Terminal window
    claude mcp add --transport http google-mcp http://localhost:8000/mcp
  3. Authenticate

    Restart your coding agent to detect the server, then connect to it. In Claude Code, run /mcp, select the server and choose Authenticate (or run claude mcp login google-mcp). A browser window opens on Keycard; if it asks you to sign in, use the account you use for Keycard Console. If you set this up in a custom Zone instead, you sign in through the identity provider chosen for that Zone. You will then be prompted to authorize Google Calendar and Drive access.

  4. Try these prompts:

    • “Show my calendar events for this week”
    • “List my recent Drive files”
    • “Find the Drive file named ‘Meeting Notes’”
  5. Verify in Audit Logs

    Check Keycard Console Audit Log. Each hop has a credentials:issue event whose identity chain shows your agent or Application, then you. users:authenticate appears only if you signed in during this flow, and users:authorize only when Google asked you to authorize.