- TypeScript 99.1%
- Makefile 0.8%
- Nix 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
* Repair corrupted GroupMember state so deploys stop failing (#133) Every Deploy to Production run since 2026-06-30 aborts about a minute in with "googleapi: Error 409: Member already exists" and merged changes (e.g. #134, #137) never apply. Two external-email GroupMember records in the maintainers group are recorded in state with an empty member ID ("groups/04f1mdlm38smb30/members/"), so each pulumi up plans a create-before-delete replace whose create conflicts with the live membership and fails the whole update. Five other records corrupted at the same time were already healed by the 2026-07-02 deploy run. Add a one-time TEMP block to the deploy workflow (same precedent as the auth-maintainers repair and the experimental-ext-tasks state delete): drop the two broken records, then re-import each membership by member email so the provider records the numeric member ID. All commands are || true-guarded, and the block must be removed after one successful deploy. Fixes #133 Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NazA3QKftRn72tdPG5vrpb * Widen state repair to all seven corrupted GroupMember records (#133) * Replace per-URN state deletes with export surgery; verify healing post-deploy (#133) The committed repair used 'pulumi state delete <urn>' per record, but the failed create-before-delete replaces left duplicate same-URN entries (a live record plus a pending-delete leftover) for some of the seven corrupted GroupMember records. 'pulumi state delete' fails on an ambiguous URN, and the '|| true' guard hid that failure, making the repair a silent no-op. - Replace the per-URN deletes with state surgery: export the stack, drop every entry (including pending-delete duplicates) whose URN is one of the seven with jq, and re-import. Unguarded, so a surgery failure is loud; skips the import when nothing matches (idempotent). - Keep the seven imports-by-email ('|| true' so a not-currently-live membership falls back to a create instead of blocking the deploy). - Add a post-deploy verification step that fails if any GroupMember record still has an empty member ID or a duplicate URN. The pinned provider (SamuZad/googleworkspace 0.11.1) writes an empty member ID even on successful creates (fixed upstream in 0.11.2), so a green 'make up' alone does not prove healing; the TEMP block must only be removed after this verification step passes. - Correct the root-cause comment: Google-side membership removals -> refresh dropped the records -> re-create -> provider create bug wrote empty IDs. * Bump googleworkspace provider 0.11.1 -> 0.11.2 (#133) 0.11.2 carries upstream commit "handle edge case with group memberships" (SamuZad/terraform-provider-googleworkspace, 2026-03-12), which fixes the create-path bug that stores an empty member ID for external-email group members — the bug that corrupted the seven records repaired by the TEMP block in deploy.yml and that would re-corrupt any record restored via the fallback create path. CI regenerates the SDK from this pin via 'pulumi install' and typechecks against it. --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
| .github | ||
| scripts | ||
| src | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| .prettierignore | ||
| .prettierrc | ||
| devenv.nix | ||
| Makefile | ||
| package-lock.json | ||
| package.json | ||
| Pulumi.prod.yaml | ||
| Pulumi.yaml | ||
| README.md | ||
| tsconfig.json | ||
MCP Access Management
Infrastructure as Code for managing access to MCP community resources using Pulumi.
- Define groups in
src/config/groups.ts - Add users to groups in
src/config/users.ts - Changes are applied via GitHub Actions when merged to the main branch
What This Manages
- GitHub Teams: Automatically syncs team memberships in the MCP GitHub organization
- Google Workspace Groups: Automatically syncs group memberships for @modelcontextprotocol.io email accounts
- Email Groups: Groups with
isEmailGroup: trueaccept emails from anyone (including external users) and notify all members. External posts are moderated for security.
- Email Groups: Groups with
- Google Workspace User Accounts: Provisions @modelcontextprotocol.io accounts for members of roles with
provisionUser: true
Opting in to a Google Workspace account (maintainers)
If you're a maintainer and want an @modelcontextprotocol.io account, open a PR adding the following fields to your entry in src/config/users.ts:
{
github: 'your-github-username',
// ...
firstName: 'Your',
lastName: 'Name',
googleEmailPrefix: 'yourname', // -> yourname@modelcontextprotocol.io
memberOf: [ROLE_IDS.MAINTAINERS /* , ... */],
},
Once merged, Pulumi provisions the account. An admin will share your initial password (retrievable via pulumi stack output --show-secrets newGWSUserPasswords).
Deployment
Production Deployment (Automated)
Note: Production deployment is automatically handled by GitHub Actions. All merges to the main branch trigger an automatic deployment via the configured GitHub Actions workflow.
Manual Deployment
Pre-requisites:
- Pulumi CLI installed
- Google Cloud SDK installed
- Access to GCP project and GCS bucket
- Required credentials and secrets
- Authenticate with GCP:
gcloud auth application-default login - Get the passphrase file
passphrase.prod.txtfrom the maintainers - Preview changes:
make preview - Deploy changes:
make up
Key Management
Required GitHub Secrets (for CI/CD)
The following secrets must be configured in GitHub Actions for automated deployments:
-
GCP_PROD_SERVICE_ACCOUNT_KEY: GCP service account key- Used to authenticate with Google Cloud Storage for Pulumi state (
gs://mcp-access-prod-pulumi-state) - Should be a JSON key file for a service account with Storage Admin permissions
- See "Setting Up GCS Backend" below for setup instructions
- Used to authenticate with Google Cloud Storage for Pulumi state (
-
PULUMI_PROD_PASSPHRASE: Passphrase for encrypting Pulumi state- Used to decrypt encrypted values in Pulumi stack configuration
- Keep this secure - if lost, you cannot decrypt your Pulumi state
Initial Setup
If setting up this infrastructure for the first time:
1. Set Up Service Account
# Create project and enable APIs
gcloud projects create mcp-access-prod
gcloud config set project mcp-access-prod
gcloud services enable storage.googleapis.com
gcloud services enable admin.googleapis.com
gcloud services enable groupssettings.googleapis.com
# Create service account
gcloud iam service-accounts create pulumi-svc \
--display-name="MCP Access Management Service Account" \
--description="Service account for Pulumi state and Google Workspace management"
# Grant storage admin permissions (for Pulumi state)
gcloud projects add-iam-policy-binding mcp-access-prod \
--member="serviceAccount:pulumi-svc@mcp-access-prod.iam.gserviceaccount.com" \
--role="roles/storage.admin"
# Create key
gcloud iam service-accounts keys create sa-key.json \
--iam-account=pulumi-svc@mcp-access-prod.iam.gserviceaccount.com
# Create GCS bucket for Pulumi state
gsutil mb gs://mcp-access-prod-pulumi-state
Then:
- In Google Workspace Admin Console, go to Account → Admin roles
- Select Groups Admin role (or create a custom role with these privileges):
- Read, create, update, and delete groups
- Read and update group members
- Click Assign service accounts
- Add your service account email:
pulumi-svc@mcp-access-prod.iam.gserviceaccount.com
2. Initialize Pulumi Stack
# Login to Pulumi backend (GCS)
pulumi login gs://mcp-access-prod-pulumi-state
# Create production stack
export PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt
pulumi stack init prod
# Configure application secrets in Pulumi
pulumi config set --secret googleworkspace:credentials "$(cat sa-key.json)"
pulumi config set --secret github:token "ghp_your_github_token_here"
3. Configure GitHub Actions Secrets
Add the CI/CD secrets to GitHub Actions (repository settings → Secrets and variables → Actions):
GCP_PROD_SERVICE_ACCOUNT_KEY: Content ofsa-key.jsonPULUMI_PROD_PASSPHRASE: The passphrase you set above