---
title: Google Workspace | Keycard
description: 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.

Note

This guide assumes you’ve completed the [delegated access setup](/guides/delegated-access/index.md). If you haven’t, start there.

## Google Setup

1. **Add Google from Resource Catalog**

   In [Keycard Console](https://console.keycard.ai), navigate to **Resource Catalog** and add **Google Calendar** and **Google Drive**. This automatically creates the Google OAuth provider and API resources.

   Note

   The Resource Catalog provides one-click setup for popular APIs. For APIs not in the catalog, you can [manually create providers and resources](/getting-started/core-concepts#resources/index.md) in Keycard Console — any OAuth 2.0 provider works.

2. **Set up the Google OAuth App callback**

   1. Go to **Zone Settings** in Keycard Console and copy the **OAuth2 Redirect URL**
   2. In [Google Cloud Console](https://console.cloud.google.com/) → **APIs & Services** → **Credentials**, create or edit your **OAuth 2.0 Client ID**
   3. Add the Keycard redirect URL to **Authorized redirect URIs**
   4. Ensure the **Calendar API** and **Drive API** are enabled under **APIs & Services** → **Enabled APIs**

3. **Register your MCP server resource**

   Navigate to **Resources** → **Create Resource**:

   | Field                   | Value                       |
   | ----------------------- | --------------------------- |
   | **Resource Name**       | Google Workspace MCP Server |
   | **Resource Identifier** | `http://localhost:8000/mcp` |
   | **Credential Provider** | Zone Provider               |

4. **Create an Application**

   Navigate to **Applications** → **Create Application**:

   | Field                 | Value                                         |
   | --------------------- | --------------------------------------------- |
   | **Provided Resource** | Google Workspace MCP Server (your MCP server) |
   | **Dependencies**      | Google Calendar API, Google Drive API         |

   After creating the application, generate **client credentials** and save them.

## Implementation

The implementation follows the same pattern as the GitHub server — the only difference is the API URL you pass to `grant()` and the API calls you make with the token.

- [Python](#tab-panel-120)
- [TypeScript](#tab-panel-121)
- [Go](#tab-panel-122)

Terminal window

```
pip install keycardai-mcp-fastmcp fastmcp httpx
```

The server setup is identical to GitHub — just change the server name. The tools file uses the same grant pattern with `https://www.googleapis.com`:

```
GOOGLE_API = "https://www.googleapis.com"




def register_tools(mcp: FastMCP, auth_provider: AuthProvider):
    @mcp.tool()
    @auth_provider.grant(GOOGLE_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")
        token = access_context.access(GOOGLE_API).access_token


        # Use token to call Google Calendar API
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{GOOGLE_API}/calendar/v3/calendars/{calendar_id}/events",
                headers={"Authorization": f"Bearer {token}"},
                params={"singleEvents": "true", "orderBy": "startTime"},
            )
            # ... process response


    @mcp.tool()
    @auth_provider.grant(GOOGLE_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")
        token = access_context.access(GOOGLE_API).access_token
        # Use token to call Google Drive API
```

See the full example with all tools, error handling, and Google Workspace file export logic: [Python SDK examples](https://github.com/keycardai/python-sdk/tree/main/packages/mcp-fastmcp/examples)

Terminal window

```
npm install @keycardai/mcp @modelcontextprotocol/sdk express
npm install -D typescript @types/express
```

The server setup is identical to GitHub — just change the server name. The tools file uses the same grant pattern with `https://www.googleapis.com`:

```
const GOOGLE_API = "https://www.googleapis.com";


export function registerGoogleRoutes(app: Express, authProvider: AuthProvider) {
  app.get("/api/calendar/events", authProvider.grant(GOOGLE_API), async (req, res) => {
    const { accessContext } = req as DelegatedRequest;
    if (accessContext.hasErrors()) {
      res.status(502).json({ error: "Token exchange failed" });
      return;
    }
    const token = accessContext.access(GOOGLE_API).accessToken;


    // Use token to call Google Calendar API
    const response = await fetch(
      `${GOOGLE_API}/calendar/v3/calendars/primary/events?singleEvents=true&orderBy=startTime`,
      { headers: { Authorization: `Bearer ${token}` } },
    );
    // ... process response
  });


  app.get("/api/drive/files", authProvider.grant(GOOGLE_API), async (req, res) => {
    // Same pattern: grant → accessContext → token → API call
  });
}
```

See the full example with all tools, error handling, and Google Workspace file export logic: [TypeScript SDK examples](https://github.com/keycardai/typescript-sdk/tree/main/examples)

Terminal window

```
go mod init google-mcp-server
go get github.com/keycardai/credentials-go/mcp
```

The server setup is identical to GitHub — just change the server name. The tools file uses the same grant pattern with `https://www.googleapis.com`:

```
const googleAPI = "https://www.googleapis.com"


func registerGoogleRoutes(mux *http.ServeMux, authProvider *mcp.AuthProvider) {
  listEvents := mcp.RequireBearerAuth(
    mcp.WithRequiredScopes("mcp:tools"),
  )(authProvider.Grant(googleAPI)(
    http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
      ac := mcp.AccessContextFromRequest(r)
      token, _ := ac.Access(googleAPI)


      // Use token to call Google Calendar API
      req, _ := http.NewRequestWithContext(r.Context(), "GET",
        googleAPI+"/calendar/v3/calendars/primary/events?singleEvents=true&orderBy=startTime", nil)
      req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))


      resp, err := http.DefaultClient.Do(req)
      if err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
        return
      }
      defer resp.Body.Close()
      // ... process response
    }),
  ))


  listFiles := mcp.RequireBearerAuth(
    mcp.WithRequiredScopes("mcp:tools"),
  )(authProvider.Grant(googleAPI)(
    http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
      ac := mcp.AccessContextFromRequest(r)
      token, _ := ac.Access(googleAPI)
      // Use token to call Google Drive API
      _ = token
    }),
  ))


  mux.Handle("GET /api/calendar/events", listEvents)
  mux.Handle("GET /api/drive/files", listFiles)
}
```

See the full example with all tools, error handling, and Google Workspace file export logic: [Go SDK examples](https://github.com/keycardai/credentials-go/tree/main/examples)

## Test It

1. **Start your server**

2. **Connect from Cursor** and complete the OAuth flow — you will be prompted to authorize Google Calendar and Drive access

3. **Try these prompts:**

   - “Show my calendar events for this week”
   - “List my recent Drive files”
   - “Get the content of the file named ‘Meeting Notes’”

4. **Verify in Audit Logs**

   Check Keycard Console **Audit Logs** — you should see the same `users:authenticate`, `users:authorize`, and `credentials:issue` events, with the identity chain showing your user and the Google application.
