Skip to content

Setup

Choose a version from the published releases. The examples below configure your own HTTP endpoint; they do not assume an owner-hosted instance. Replace <version> in Docker examples with a published image version.

This plugin supports 3 install methods. Pick the one that matches your use case:

Priority Method Transport Best for
1. Default Plugin install (npx) stdio Quick local start, single workstation, your own Google OAuth client.
2. Fallback Docker stdio (docker run -i --rm) stdio Windows/macOS where native npx hits PATH or Node version issues.
3. Self-host Docker HTTP (docker run -p 8080:8080) HTTP Multi-user, multi-device, team deploy, claude.ai web compatibility.

⚠️ Mutually exclusive — pick ONE: If you choose Method 2 or Method 3, do NOT also /plugin install this plugin via marketplace. Both load simultaneously and create duplicate entries in the /mcp dialog. Plugin matching is by endpoint (URL or command string), not by name — and npx ≠ docker ≠ HTTP URL, so all three are distinct endpoints. Trade-off: choosing Method 2 or Method 3 means you lose this plugin’s hooks. For full plugin features, use Method 1.

  • Node.js >= 24.18.0 (Method 1) or Docker (Methods 2 and 3)
  • A Google account, and a Google Cloud project where you can create an OAuth client

The server never ships a Google client of its own – you bring your own, so the consent screen names your project and the quota is yours.

  1. Open the Google Cloud Console → APIs & Services → Credentials.
  2. Create an OAuth client ID. The type depends on the mode you plan to run:
    • Desktop app for stdio (Methods 1 and 2). Desktop is the right type because the server receives the consent redirect on a loopback address, not on a public URL.
    • Web application for HTTP (Method 3). Register both <PUBLIC_URL>/callback for delegated sign-in and <PUBLIC_URL>/accounts/callback for adding Google accounts.
  3. Enable the Workspace APIs you plan to call on the same project (Docs, Drive, Calendar, Gmail, Sheets, Slides, Tasks, Chat, People, Forms — enable only what you need).
  4. While the consent screen is unpublished, add yourself as a test user.

A Desktop-app client secret is not a confidential secret in the OAuth sense — installed apps cannot keep one. It still identifies your project, so keep it out of public repos.

Section titled “Method 1: Claude Code Plugin (recommended)”

Plugin install runs in stdio mode with the OAuth client provided via the userConfig install prompt.

When you run /plugin install, Claude Code prompts for the following. Sensitive values are stored in your system keychain and persist across /plugin update:

Field Required Where to obtain
GOOGLE_OAUTH_CLIENT_ID Required Desktop app OAuth client, ends with .apps.googleusercontent.com
GOOGLE_OAUTH_CLIENT_SECRET Required Client secret of that same client
  1. Create a Desktop app OAuth client (see “Create the Google OAuth client” above).
  2. Open Claude Code in your terminal.
  3. Install the plugin (Claude Code prompts for both values):
    Terminal window
    /plugin marketplace add n24q02m/claude-plugins
    /plugin install better-workspace-mcp@n24q02m-plugins
  4. Restart Claude Code — the plugin auto-loads with your client injected.
  5. On the first run the server opens the Google consent screen in your browser. Approve it once; the refresh token is stored encrypted on your machine, so later runs start without asking again.

Or configure it by hand in any MCP client:

{
"mcpServers": {
"better-workspace-mcp": {
"command": "npx",
"args": ["--yes", "@n24q02m/better-workspace-mcp@latest"],
"env": {
"MCP_TRANSPORT": "stdio",
"GOOGLE_OAUTH_CLIENT_ID": "<your-client-id>.apps.googleusercontent.com",
"GOOGLE_OAUTH_CLIENT_SECRET": "<your-client-secret>"
}
}
}
}

⚠️ Before adding the Docker stdio override below, ensure this plugin is NOT installed via marketplace: run /plugin uninstall better-workspace-mcp@n24q02m-plugins first if you previously ran /plugin install.

The published image defaults to HTTP mode (MCP_TRANSPORT=http, port 8080 baked in) and there is no separate :stdio tag, so stdio over Docker means overriding the transport explicitly with -e MCP_TRANSPORT=stdio:

Terminal window
docker pull n24q02m/better-workspace-mcp:<version>
{
"mcpServers": {
"better-workspace-mcp": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT=stdio",
"-e", "GOOGLE_OAUTH_CLIENT_ID",
"-e", "GOOGLE_OAUTH_CLIENT_SECRET",
"n24q02m/better-workspace-mcp:<version>"
]
}
}
}

Set the values in your shell profile:

Terminal window
export GOOGLE_OAUTH_CLIENT_ID="<your-client-id>.apps.googleusercontent.com"
export GOOGLE_OAUTH_CLIENT_SECRET="<your-client-secret>"

A container has no browser, so the first-run consent step cannot open one for you. Either authorize once via Method 1 first (the stored token is reused), or run HTTP mode, where each user consents through the server’s own /authorize.

The corresponding GHCR image is ghcr.io/n24q02m/better-workspace-mcp:<version>; select the version from the release’s published assets.

Stdio mode is the default and works for single-user local development. Consider HTTP when you need any of:

  • Multi-user / team sharing — each user’s Google credentials live in their own bucket keyed by their JWT sub, so one deployment serves several people without them sharing an account
  • claude.ai web compatibility — connect the server directly from claude.ai without a local CLI
  • 1 server shared across N Claude Code sessions — no per-session daemon proliferation
  • Multi-device credential sync — credentials persist server-side; new devices just point at the URL
  • Browser consent without a local browser — each user consents at the server’s own /authorize

⚠️ Before adding the HTTP override below, ensure this plugin is NOT installed via marketplace: run /plugin uninstall better-workspace-mcp@n24q02m-plugins first if you previously ran /plugin install.

Switching transport vs. setting credentials: the userConfig prompt only configures the OAuth client for stdio mode (Method 1). To switch transport to HTTP, override mcpServers in your client settings per the snippets below — a separate path from userConfig, not driven by the install prompt.

Run your own HTTP service with the Web OAuth client and a persistent credential-encryption secret:

Terminal window
# HOST=0.0.0.0 binds all interfaces so the host can reach the container.
# PORT is already 8080 in the image; set it if you want a different one.
docker run -p 8080:8080 \
-e HOST=0.0.0.0 \
-e PUBLIC_URL=https://<your-host> \
-e GOOGLE_OAUTH_CLIENT_ID=<your-web-client-id>.apps.googleusercontent.com \
-e GOOGLE_OAUTH_CLIENT_SECRET=<your-web-client-secret> \
-e CREDENTIAL_SECRET=<persistent-generated-secret> \
-e MCP_RELAY_PASSWORD=<generated-32-byte-hex> \
n24q02m/better-workspace-mcp:<version>

This mode requires a Web application OAuth client with both https://<your-host>/callback and https://<your-host>/accounts/callback registered as authorized redirect URIs. The first signs in to the server; the second adds a Google account. Authentication is OAuth 2.1 delegated to Google, and credentials are stored per JWT sub. Keep CREDENTIAL_SECRET stable across restarts; the HTTP server refuses to start without it.

Point your MCP client at the host you deployed it on:

{
"mcpServers": {
"better-workspace-mcp": {
"type": "http",
"url": "https://<your-host>/mcp"
}
}
}

A public HTTP deployment exposes <your-host>/authorize to URL discovery. Without a gate in front of it, anyone who finds the URL can start a session on your deployment — per-user credential isolation limits what each session sees, it does not limit who gets one. Mint a relay password:

Terminal window
openssl rand -hex 32
# Save in your secret store as:
MCP_RELAY_PASSWORD=<generated-32-byte-hex>

Share it out-of-band with anyone you invite. They see a login form when first opening /authorize; the cookie then persists 24 hours.

Single-user dev exception: with PUBLIC_URL=http://localhost:8080 you can leave MCP_RELAY_PASSWORD empty to disable the gate.

Variable Required Default Description
GOOGLE_OAUTH_CLIENT_ID Yes – OAuth client ID. Desktop app type for stdio, Web application type for HTTP.
GOOGLE_OAUTH_CLIENT_SECRET Yes – Client secret of the same OAuth client.
MCP_TRANSPORT No stdio http selects HTTP mode. --http on the command line and TRANSPORT_MODE=http do the same.
PUBLIC_URL Yes (http) – The server’s public URL, used to build OAuth redirects.
MCP_RELAY_PASSWORD Yes (http, public) – Shared password gating /authorize. Optional only when PUBLIC_URL is localhost.
CREDENTIAL_SECRET Yes (http) – Persistent secret deriving each subject’s credential-encryption key and the JWT signing key. HTTP startup fails if it is absent.
PORT No 8080 in the image Listen port (http mode).
HOST No – Bind address (http mode); set 0.0.0.0 to expose the container.
MCP_AUTH_DISABLE No (http) – Set to 1 to skip Bearer JWT verification. Only for deploys already behind an external auth gateway — never on a directly-exposed host.
MCP_NO_BROWSER / NO_BROWSER No – Suppress the first-run browser launch (headless environments).
MCP_STORAGE_BACKEND No filesystem Credential storage backend.
MCP_KV_BASE_URL No – Base URL for the KV storage backend, when one is used.

After setup, check that the server answers at all — time needs no Google account:

Use the time tool with action "getCurrentTime".

Then confirm the credentials landed:

Use the config tool with action "status".

Before the first consent this reports awaiting_setup; afterwards it names the account the server is acting as.

Every domain tool takes an account parameter — the email of the Google account the call acts as. Omit it and the call runs against the primary account. Accounts are managed through config: account_add, account_list, account_remove, account_set_default. See the tools reference.

In HTTP mode, account_add completes through /accounts/callback on your host.

“Access blocked: This app is not verified”

Section titled ““Access blocked: This app is not verified””

Your consent screen is still unpublished. Add your Google account as a test user in the Cloud Console, or publish the consent screen.

The Web application client needs https://<your-host>/accounts/callback registered exactly, including scheme and any trailing path. A Desktop-type client will also produce this error in HTTP mode — HTTP needs a Web client.

Containers and headless hosts have no browser. Authorize once via Method 1 on a workstation, or run HTTP mode and consent at the server’s /authorize.

The API is probably not enabled on your Cloud project. Enable it under APIs & Services → Library, then retry — the scope set is requested at consent time, so a scope added later needs a re-consent via config(action="account_add").

Pass account="<email>" explicitly. config(action="account_list") shows which account is primary; config(action="account_set_default") changes it.