feat: add Firestore storage backend for OAuth 2.1 state persistence#721
feat: add Firestore storage backend for OAuth 2.1 state persistence#721peekabo0jackson wants to merge 1 commit into
Conversation
Cloud Run services scale to zero between requests. When the process restarts, any in-memory or local-disk OAuth 2.1 state is gone, causing 401s on the next auth flow. Firestore persists state across scale-to-zero at no cost under the free tier, and is already available in every GCP project without provisioning additional infrastructure. This adds a `firestore` branch to the storage_backend switch alongside the existing `valkey`, `disk`, and `memory` options. State is encrypted with the same Fernet/derive_jwt_key pattern used by the Valkey and disk backends before writing to Firestore. The collection prefix env var lets multiple Cloud Run services share one Firestore database without key collisions. New env vars: - WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND=firestore - WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_PROJECT_ID (optional; inferred from ADC) - WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_DATABASE (optional; defaults to "(default)") - WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX (optional; defaults to "fastmcp-oauth") Dockerfile updated to install --extra firestore alongside --extra disk. uv.lock regenerated to include google-cloud-firestore and transitive deps (google-cloud-core, grpcio, grpcio-status, protobuf). Install: pip install workspace-mcp[firestore] Verified in production across 7 Cloud Run services. State persists correctly across forced container restarts. No regressions in existing 752 tests.
📝 WalkthroughWalkthroughThe changes add support for Firestore as an OAuth proxy storage backend. Documentation is updated to describe Firestore configuration and encryption handling. Dependencies are added via Changes
Sequence DiagramsequenceDiagram
participant Server as Server Init
participant Config as Configuration
participant Firestore as FirestoreStore
participant Encryption as Encryption Wrapper
participant Storage as OAuth Storage
Server->>Config: Read WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND
Config-->>Server: "firestore" mode detected
Server->>Firestore: Dynamically import (with error handling)
alt Dependencies available
Firestore-->>Server: Module loaded
Server->>Config: Extract project, database, collection prefix
Server->>Firestore: Build store with Firestore config
Firestore-->>Server: Store instance created
Server->>Encryption: Derive key from JWT signing key
Server->>Encryption: Wrap FirestoreStore with FernetEncryption
Encryption-->>Storage: Encrypted storage ready
else Dependencies missing or config invalid
Server->>Server: Log warning, fallback to default storage
Server-->>Storage: Default storage ready
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.env.oauth21 (1)
21-24:⚠️ Potential issue | 🟡 MinorOptions legend doesn't list
firestore.The summary line still reads
memory, disk, valkey/memory|disk|valkey, even though a full Firestore section is added below. Users scanning the top of the storage section will miss it.📝 Proposed doc fix
-# Storage backend for OAuth proxy state. Options: memory, disk, valkey +# Storage backend for OAuth proxy state. Options: memory, disk, valkey, firestore # Default: FastMCP's built-in default (disk on Mac/Windows, memory on Linux) # -# WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND=memory|disk|valkey +# WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND=memory|disk|valkey|firestore🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.oauth21 around lines 21 - 24, Update the options legend for the OAuth proxy storage backend to include "firestore" so it matches the detailed Firestore section: change the summary line and the example value for WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND (the symbol to edit) to list memory, disk, valkey, firestore (e.g., memory|disk|valkey|firestore) so users scanning the top see Firestore as an option.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@core/server.py`:
- Around line 458-469: The env var
WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX is misnamed and misleading
because its value is passed into FirestoreStore(..., default_collection=...)
which is only a fallback, not a prefix; update the code to either (A) rename the
env var and related logging to
WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_DEFAULT_COLLECTION and keep passing it to
FirestoreStore(default_collection=...) to match semantics, or (B) implement a
wrapper around FirestoreStore (e.g., a small helper that prepends the intended
prefix to any collection argument before delegating to
FirestoreStore.put/get/delete) so the original prefix intent is honored; adjust
use of FirestoreStore, the variable firestore_collection_prefix, and the log
message to reflect the chosen fix.
- Around line 444-469: The FirestoreStore instantiation is using wrong kwargs
and omits required credentials
(firestore_project_id/firestore_database/firestore_collection_prefix passed as
project/database/default_collection) which will raise TypeError in
FirestoreStore; fix by constructing FirestoreStore with a proper credentials
object (e.g., load service account or ADC into google_credentials and call
FirestoreStore(credentials=google_credentials, database=firestore_database) and
remove unsupported kwargs like project= and default_collection=), and also
update the surrounding exception handling in configure_server_for_http (the
Firestore import/initialization try/except) to catch TypeError or Exception so
initialization failures fall back gracefully instead of propagating.
---
Outside diff comments:
In @.env.oauth21:
- Around line 21-24: Update the options legend for the OAuth proxy storage
backend to include "firestore" so it matches the detailed Firestore section:
change the summary line and the example value for
WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND (the symbol to edit) to list memory,
disk, valkey, firestore (e.g., memory|disk|valkey|firestore) so users scanning
the top see Firestore as an option.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ef1ad9ce-526a-4de8-90b3-8a546f3752f7
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
.env.oauth21Dockerfilecore/server.pypyproject.toml
| from key_value.aio.stores.firestore import FirestoreStore | ||
|
|
||
| firestore_project_id = ( | ||
| os.getenv( | ||
| "WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_PROJECT_ID", "" | ||
| ).strip() | ||
| or None | ||
| ) | ||
| firestore_database = ( | ||
| os.getenv( | ||
| "WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_DATABASE", "" | ||
| ).strip() | ||
| or None | ||
| ) | ||
| firestore_collection_prefix = ( | ||
| os.getenv( | ||
| "WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX", "" | ||
| ).strip() | ||
| or "fastmcp-oauth" | ||
| ) | ||
|
|
||
| client_storage = FirestoreStore( | ||
| project=firestore_project_id, | ||
| database=firestore_database, | ||
| default_collection=firestore_collection_prefix, | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the FirestoreStore constructor signature in the installed version
pip download --no-deps --no-binary=:all: "py-key-value-aio[firestore]>=0.3.0" -d /tmp/pkv 2>/dev/null
fd -e tar.gz . /tmp/pkv -x tar -xzf {} -C /tmp/pkv
fd -t f 'firestore.py|__init__.py' /tmp/pkv --full-path | xargs rg -nP -C2 'class FirestoreStore|def __init__' 2>/dev/null | head -100Repository: taylorwilsdon/google_workspace_mcp
Length of output: 164
🏁 Script executed:
# First, look at the core/server.py file structure around lines 444-469
fd -t f 'server.py' . | head -5Repository: taylorwilsdon/google_workspace_mcp
Length of output: 189
🏁 Script executed:
# Read core/server.py to see the exception handling context
cat -n core/server.py | sed -n '430,480p'Repository: taylorwilsdon/google_workspace_mcp
Length of output: 2616
🏁 Script executed:
# Search for FirestoreStore usage in the codebase to find other examples
rg -n 'FirestoreStore' --type pyRepository: taylorwilsdon/google_workspace_mcp
Length of output: 411
🏁 Script executed:
# Find the exception handlers for the Firestore block
cat -n core/server.py | sed -n '442,570p'Repository: taylorwilsdon/google_workspace_mcp
Length of output: 7274
🏁 Script executed:
# Search for py-key-value package documentation or source in the repo
rg -n 'py-key-value|FirestoreStore' --type py -B2 -A2Repository: taylorwilsdon/google_workspace_mcp
Length of output: 2095
🏁 Script executed:
# Check if there's a requirements file or setup.py that specifies the version
fd -t f '(requirements|setup|pyproject|poetry)' . | head -10Repository: taylorwilsdon/google_workspace_mcp
Length of output: 94
🏁 Script executed:
# Read pyproject.toml to find py-key-value-aio version
cat pyproject.toml | grep -A5 -B5 'py-key-value\|firestore'Repository: taylorwilsdon/google_workspace_mcp
Length of output: 986
🌐 Web query:
py-key-value-aio FirestoreStore constructor signature 0.3.0
💡 Result:
The FirestoreStore constructor signature in py-key-value-aio version 0.3.0 is FirestoreStore(credentials, database). This is shown in the official documentation example: from key_value.aio.stores.firestore import FirestoreStore store = FirestoreStore(credentials=google_credentials, database="firestore-db") Note that FirestoreStore was added in version 0.4.0 (PR #249), but the documentation reflects the current signature, which is likely the same or similar for 0.3.0 if backported or available. Version 0.3.0 was released 2025-11-17 per piwheels. Official docs at https://strawgate.github.io/py-key-value/stores/ provide the usage. GitHub repo: https://github.com/strawgate/py-key-value. No explicit source code snippet for init found, but example indicates keyword arguments credentials (Google credentials object) and database (str, Firestore database ID).
Citations:
- 1: https://github.com/strawgate/py-key-value
- 2: https://pypi.org/project/py-key-value-aio/0.4.3/
- 3: https://strawgate.github.io/py-key-value/stores/
- 4: https://www.piwheels.org/project/py-key-value-aio/
- 5: https://github.com/strawgate/py-key-value/releases
🏁 Script executed:
# Search for any other usages or tests of FirestoreStore to find the correct kwargs
rg -n 'FirestoreStore(' --type py -B2 -A5Repository: taylorwilsdon/google_workspace_mcp
Length of output: 158
🌐 Web query:
py-key-value-aio FirestoreStore project_id credentials database 0.4.0
💡 Result:
py-key-value-aio is an async Python library providing a pluggable interface for key-value stores, supporting multiple backends including Firestore via FirestoreStore (added in v0.4.0). Install with pip install "py-key-value-aio[firestore]". Usage example from official docs: from key_value.aio.stores.firestore import FirestoreStore store = FirestoreStore(credentials=google_credentials, database="firestore-db") FirestoreStore initialization requires: - credentials: Google Cloud credentials object (e.g., from service account JSON via google.oauth2.service_account.Credentials.from_service_account_file or similar). - database: Firestore database ID (e.g., "(default)" or custom name like "firestore-db"). The project_id is typically specified within the credentials object (loaded from service account key file containing "project_id") or via default application credentials. It is not shown as a direct parameter in the documented constructor, but standard Google Cloud clients infer it from credentials. For non-default databases, specify the database parameter explicitly. Version 0.4.0 (released 2026-02-10) introduced Firestore support among other backends. Current latest is v0.4.4.
Citations:
- 1: https://pypi.org/project/py-key-value-aio/
- 2: https://github.com/strawgate/py-key-value
- 3: https://github.com/strawgate/py-key-value/blob/main/README.md
- 4: https://strawgate.github.io/py-key-value/stores/
- 5: https://www.piwheels.org/project/py-key-value-aio/
- 6: https://github.com/strawgate/py-key-value/releases
- 7: https://github.com/strawgate/py-key-value/blob/main/pyproject.toml
🏁 Script executed:
# Escape the parenthesis properly for rg
rg -n 'FirestoreStore\(' --type py -B2 -A5Repository: taylorwilsdon/google_workspace_mcp
Length of output: 507
🌐 Web query:
site:github.com strawgate py-key-value FirestoreStore __init__ source code
💡 Result:
The FirestoreStore class is part of the strawgate/py-key-value library (specifically the py-key-value-aio package), which provides a pluggable async key-value store interface supporting multiple backends including Firestore (added in v0.4.0 via PR #249). Import and initialization example from official docs: from key_value.aio.stores.firestore import FirestoreStore store = FirestoreStore(credentials=google_credentials, database="firestore-db") Install with: pip install py-key-value-aio[firestore] The full source code for the FirestoreStore.init method is in the repository at https://github.com/strawgate/py-key-value, likely under key-value-aio/src/key_value/aio/stores/firestore/store.py (following the pattern of other stores like MemoryStore). The repo structure indicates stores are in src/key_value/aio/stores/[backend]/store.py. Exact init parameters likely include Firestore client credentials and database ID, plus common store options like sanitization_strategy as per library architecture (stores no longer sanitize by default; must specify SanitizationStrategy on init). No snippet of the exact init code was found in searched pages, but docs confirm the usage above. Repository: https://github.com/strawgate/py-key-value
Citations:
- 1: https://github.com/strawgate/py-key-value
- 2: https://github.com/strawgate/py-key-value/blob/main/README.md
- 3: https://github.com/strawgate/py-key-value/blob/main/docs/getting-started.md
- 4: https://github.com/strawgate/py-key-value/releases
- 5: https://github.com/strawgate/py-key-value/blob/main/docs/stores.md
🌐 Web query:
"FirestoreStore" "def __init__" py-key-value credentials database project
💡 Result:
FirestoreStore is a class from the py-key-value library (also available as py-key-value-aio), which provides a key-value store abstraction supporting multiple backends including Google Cloud Firestore. It is used to interface with Firestore as a key-value store. The init method initializes the store with Google Cloud credentials and a database name: from key_value.aio.stores.firestore import FirestoreStore store = FirestoreStore(credentials=google_credentials, database="firestore-db") This matches the query terms: FirestoreStore, def init, py-key-value, credentials, database, project (project is part of the credentials setup for Firestore access). To use it: - Install: pip install py-key-value-aio[firestore] - Requires google-cloud-firestore and authentication via service account credentials (JSON key file path set via GOOGLE_APPLICATION_CREDENTIALS or passed explicitly). - The library supports async operations for distributed storage like Firestore. - Documentation: https://strawgate.github.io/py-key-value/stores/ - GitHub: https://github.com/strawgate/py-key-value
Citations:
🏁 Script executed:
# Check if there are any comments or other clues in the code about credentials handling
cat -n core/server.py | sed -n '400,550p' | grep -i 'credential\|project\|adc'Repository: taylorwilsdon/google_workspace_mcp
Length of output: 521
Fix FirestoreStore constructor: missing credentials parameter and incorrect kwargs.
The documented FirestoreStore signature (py-key-value-aio ≥0.3.0) is FirestoreStore(credentials=google_credentials, database="firestore-db"). The code passes project=, database=, and default_collection= instead. This will fail with TypeError at runtime:
- Missing
credentials=: The constructor requires a Google Cloud credentials object (e.g., from service account JSON or Application Default Credentials). The code does not pass this at all. - Incorrect
project=: The project ID is embedded in the credentials object, not a separate parameter. - Unsupported
default_collection=: Not shown in official examples and likely unsupported.
The exception handler (lines 493–504) catches only ImportError and ValueError, not TypeError. This means initialization errors will not be caught in the Firestore block and will bubble to the outer except Exception on line 570, crashing configure_server_for_http() instead of falling back gracefully.
Fix: Either pass valid credentials (e.g., via Application Default Credentials or service account key) and remove the unsupported kwargs, or widen the Firestore exception handler to catch TypeError (or all Exception types) to fail gracefully.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@core/server.py` around lines 444 - 469, The FirestoreStore instantiation is
using wrong kwargs and omits required credentials
(firestore_project_id/firestore_database/firestore_collection_prefix passed as
project/database/default_collection) which will raise TypeError in
FirestoreStore; fix by constructing FirestoreStore with a proper credentials
object (e.g., load service account or ADC into google_credentials and call
FirestoreStore(credentials=google_credentials, database=firestore_database) and
remove unsupported kwargs like project= and default_collection=), and also
update the surrounding exception handling in configure_server_for_http (the
Firestore import/initialization try/except) to catch TypeError or Exception so
initialization failures fall back gracefully instead of propagating.
| firestore_collection_prefix = ( | ||
| os.getenv( | ||
| "WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX", "" | ||
| ).strip() | ||
| or "fastmcp-oauth" | ||
| ) | ||
|
|
||
| client_storage = FirestoreStore( | ||
| project=firestore_project_id, | ||
| database=firestore_database, | ||
| default_collection=firestore_collection_prefix, | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In py-key-value-aio 0.3.0, does the FirestoreStore default_collectionconstructor argument act as a prefix applied to all collection names, or is it only used when the caller omits thecollection argument on put/get/delete?
💡 Result:
In py-key-value-aio 0.3.0, the FirestoreStore default_collection constructor argument is only used when the caller omits the collection argument on put/get/delete operations. It does not act as a prefix applied to all collection names.
Citations:
- 1: https://github.com/strawgate/py-key-value/blob/main/docs/stores.md
- 2: https://pypi.org/project/py-key-value-aio/
- 3: https://github.com/strawgate/py-key-value
- 4: https://strawgate.github.io/py-key-value/stores/
- 5: https://github.com/strawgate/py-key-value/releases
🏁 Script executed:
# Search for client_storage usage in core/server.py
rg "client_storage" core/server.py -A 3 -B 1
# Also check if there are any other FirestoreStore instantiations
rg "FirestoreStore" . --type pyRepository: taylorwilsdon/google_workspace_mcp
Length of output: 5069
🏁 Script executed:
# Find FastMCP imports and usage
rg "fastmcp" . --type py -i -A 2 | head -100
# Check what method is called on client_storage in the OAuth setup
rg "client_storage" . --type py | grep -E "(put|get|delete|set)" -iRepository: taylorwilsdon/google_workspace_mcp
Length of output: 6198
🏁 Script executed:
# Find GoogleProvider imports and usage
rg "GoogleProvider" . --type py -B 2 -A 5
# Also check auth module structure
fd "auth" --type d | head -10Repository: taylorwilsdon/google_workspace_mcp
Length of output: 10513
🏁 Script executed:
# Look for any FastMCP-related documentation or comments about client_storage usage
rg "client_storage" . --type py -B 5 -A 5 | grep -E "(collection|GoogleProvider|client_storage)" -i
# Check if there are any comments or docs about the OAuth flow
rg "OAuth 2.1" core/server.py -B 2 -A 10 | head -80Repository: taylorwilsdon/google_workspace_mcp
Length of output: 5977
🏁 Script executed:
# Search for any FastMCP documentation or examples in the repo
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "*.rst" \) | xargs grep -l "client_storage\|GoogleProvider" 2>/dev/null | head -5
# Check if there's a requirements or pyproject file that specifies FastMCP version
fd -e txt -e toml -e cfg -e ini | xargs grep -l "fastmcp" 2>/dev/nullRepository: taylorwilsdon/google_workspace_mcp
Length of output: 104
🏁 Script executed:
# Check pyproject.toml for FastMCP version and documentation
cat pyproject.toml | grep -A 10 -B 2 fastmcp
# Check README for any relevant details
grep -A 20 -B 5 "client_storage\|GoogleProvider" ./README.md | head -80Repository: taylorwilsdon/google_workspace_mcp
Length of output: 2315
🏁 Script executed:
# Search web for FastMCP GoogleProvider documentation about client_storage
# First, let's check if there are any inline comments or usage examples in the codebase
rg "GoogleProvider" . --type py -A 15 | grep -A 15 "client_storage"Repository: taylorwilsdon/google_workspace_mcp
Length of output: 1166
Env var named COLLECTION_PREFIX but wired to default_collection — semantic mismatch.
The default_collection parameter on py-key-value-aio's FirestoreStore is confirmed to be a fallback used only when the caller omits the collection= argument on put/get/delete; it does not act as a prefix applied to all collection names. This contradicts the env var name WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX, which implies prefix semantics. The log message at line ~478 even calls it collection_prefix despite passing it to default_collection, reinforcing the confusion.
If FastMCP's GoogleProvider ever explicitly specifies a collection= argument when using client_storage, your "prefix" will be silently ignored, and two services sharing one Firestore DB will collide despite different COLLECTION_PREFIX values.
Recommendation: Rename the env var to WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_DEFAULT_COLLECTION to match the actual semantics, or implement a wrapper that enforces the prefix intent regardless of caller-supplied collection arguments. The current naming creates a false sense of isolation that may not exist depending on FastMCP's internal behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@core/server.py` around lines 458 - 469, The env var
WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX is misnamed and misleading
because its value is passed into FirestoreStore(..., default_collection=...)
which is only a fallback, not a prefix; update the code to either (A) rename the
env var and related logging to
WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_DEFAULT_COLLECTION and keep passing it to
FirestoreStore(default_collection=...) to match semantics, or (B) implement a
wrapper around FirestoreStore (e.g., a small helper that prepends the intended
prefix to any collection argument before delegating to
FirestoreStore.put/get/delete) so the original prefix intent is honored; adjust
use of FirestoreStore, the variable firestore_collection_prefix, and the log
message to reflect the chosen fix.
Summary
firestorebranch to the existingWORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKENDswitch alongsidevalkey,disk, andmemory.py-key-value-aio[firestore](FirestoreStore), wrapped in the sameFernetEncryptionWrapperpattern as the valkey and disk backends.Dockerfileto install thefirestoreextra and regeneratesuv.lockto pullgoogle-cloud-firestoreand transitive deps.Motivation
OAuth 2.1 state (JTI mappings, upstream tokens, DCR client registrations, authorization codes, refresh token metadata) is stored in process memory by default. When the server is deployed to a platform like Google Cloud Run that scales to zero between idle periods, a cold start wipes this state, causing 401s on the next request from clients whose JWTs were issued before the restart. The user-visible symptom is "Connection issue - This connector has a server configuration issue" in Claude Desktop.
See #449 where this behavior is documented as in-memory-by-design with a recommendation to use
valkeyordiskfor persistence. For GCP deployments, Firestore is the natural fit: native to the platform, free tier covers the workload comfortably (1 GiB storage, 50k reads/day, 20k writes/day, 20k deletes/day), no new infrastructure to provision.Configuration
New environment variables:
WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND=firestore— activates the backendWORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_PROJECT_ID— optional; defaults to ADC /GOOGLE_CLOUD_PROJECTWORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_DATABASE— optional; defaults to(default). Note: if deploying to a project with a named default database (not literally(default)), set this explicitly.WORKSPACE_MCP_OAUTH_PROXY_FIRESTORE_COLLECTION_PREFIX— optional; defaults tofastmcp-oauth. Use per-service prefixes if multiple Cloud Run services share one Firestore database.Authentication uses Application Default Credentials — on Cloud Run, grant the runtime service account
roles/datastore.user.Test plan
Summary by CodeRabbit
New Features
Documentation