From 5b2b01db21c981a32275a6b79e5427a0d5678ef2 Mon Sep 17 00:00:00 2001 From: adam jones Date: Wed, 1 Oct 2025 01:08:42 +0100 Subject: [PATCH 001/196] Use intermediate env var for Pulumi passphrase (#4) Addresses security concern where secrets were directly interpolated in run commands. Following GitHub's security best practices, the secret is now passed through an intermediate environment variable before being written to the file. This maintains compatibility with the existing Makefile workflow while reducing the risk of accidental secret disclosure. --- .github/workflows/deploy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ff4b5b6..d0b7a64 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,6 +49,8 @@ jobs: credentials_json: ${{ secrets.GCP_PROD_SERVICE_ACCOUNT_KEY }} - name: Deploy to Production + env: + PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} run: | - echo "${{ secrets.PULUMI_PROD_PASSPHRASE }}" > passphrase.prod.txt + echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt make up \ No newline at end of file From 7883dbc5e39e833712290b635b697a7e55e53888 Mon Sep 17 00:00:00 2001 From: adam jones Date: Fri, 3 Oct 2025 22:33:37 +0100 Subject: [PATCH 002/196] Migrate GitHub teams to infrastructure-as-code (#5) * Migrate GitHub teams to infrastructure-as-code - Add 25 teams with proper hierarchy (steering-committee, SDK teams, working groups, interest groups) - Add 67 team members with their assignments - Add repository access configuration for all 21 repositories - Refactor github.ts to use config-driven approach with repoAccess.ts - Mark all MCP teams as GitHub-only with onlyOnPlatforms - Use RepositoryCollaborators for authoritative access management Note: GitHub Projects V2 permissions are not yet supported by Pulumi and must be managed manually. See https://github.com/pulumi/pulumi-github/issues/1006 * Add individual user collaborators to repository access Preserves direct collaborator access for users who have explicit permissions outside of team membership, including: - jspahrsummers: admin on multiple core repos - Go SDK collaborators (neild, rsc, rolandshoemaker, h9jiang) - C# SDK collaborators (jeffhandley, MackinnonBuck, jozkee, localden, PederHP) - Rust SDK collaborators (jamadeo, jokemanfire, 4t145) - Inspector collaborators (richardkmichael, olaservo, an-dustin, ashwin-ant) - Financial services IG collaborators (aniabot, imfing, sambhav, KengoA, nitsanh) - And others Without this, RepositoryCollaborators (authoritative mode) would remove all individual user access. --- src/config/groups.ts | 159 ++++++++++++++++++- src/config/repoAccess.ts | 329 +++++++++++++++++++++++++++++++++++++++ src/config/users.ts | 273 +++++++++++++++++++++++++++++++- src/github.ts | 15 ++ src/google.ts | 6 + 5 files changed, 772 insertions(+), 10 deletions(-) create mode 100644 src/config/repoAccess.ts diff --git a/src/config/groups.ts b/src/config/groups.ts index c336f3a..2417b6f 100644 --- a/src/config/groups.ts +++ b/src/config/groups.ts @@ -9,20 +9,163 @@ import { defineGroups } from './utils'; // Groups are created on all platforms (GitHub and Google) by default. // To limit a group to specific platforms, set the onlyOnPlatforms array (e.g., onlyOnPlatforms: ['google']). export const GROUPS = defineGroups([ + // MCP Organization Structure { - name: 'test-parent', - description: 'All maintainers. Users should not be added directly to this group.', + name: 'steering-committee', + description: 'MCP Steering Committee', + onlyOnPlatforms: ['github'], }, { - name: 'test-child', - description: 'Registry maintainers', - memberOf: ['test-parent'], + name: 'core', + description: 'Core team', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], }, { - name: 'test-email-group', - description: 'Example email group that accepts external emails', - isEmailGroup: true, + name: 'core-maintainers', + description: 'Core maintainers', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, + { + name: 'moderators', + description: 'Community moderators', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], }, + { + name: 'docs-maintaners', + description: 'MCP docs maintainers', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, + + // SDK Maintainers + { + name: 'sdk-maintainers', + description: 'Authors and maintainers of official MCP SDKs', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, + { + name: 'csharp-sdk', + description: 'Official C# SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'go-sdk', + description: 'The Go SDK Team', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'java-sdk', + description: 'Official Java SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'kotlin-sdk', + description: 'Official Kotlin SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'php-sdk', + description: 'Official PHP SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'python-sdk', + description: 'Official Python SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'python-sdk-auth', + description: 'Auth related owners', + memberOf: ['python-sdk'], + onlyOnPlatforms: ['github'], + }, + { + name: 'ruby-sdk', + description: 'Official Ruby SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'rust-sdk', + description: 'Official Rust SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'swift-sdk', + description: 'Official Swift SDK maintainers', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'typescript-sdk', + description: 'Official TypeScript SDK', + memberOf: ['sdk-maintainers'], + onlyOnPlatforms: ['github'], + }, + { + name: 'typescript-sdk-auth', + description: 'Code owners for auth in Typescript SDK', + memberOf: ['typescript-sdk'], + onlyOnPlatforms: ['github'], + }, + + // Working Groups + { + name: 'working-groups', + description: 'MCP Working Groups', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, + { + name: 'auth-wg', + description: 'Authentication Working Group', + memberOf: ['working-groups'], + onlyOnPlatforms: ['github'], + }, + { + name: 'registry-wg', + description: 'Official registry builders and maintainers', + memberOf: ['working-groups'], + }, + { + name: 'security-wg', + description: 'Security Working Group', + memberOf: ['working-groups'], + onlyOnPlatforms: ['github'], + }, + { + name: 'transport-wg', + description: 'Transport Working Group', + memberOf: ['working-groups'], + onlyOnPlatforms: ['github'], + }, + + // Interest Groups + { + name: 'interest-groups', + description: 'Interest Groups', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, + { + name: 'ig-financial-services', + description: 'Financial Services Interest Group', + memberOf: ['interest-groups'], + onlyOnPlatforms: ['github'], + }, + + // Email-only groups { name: 'antitrust', description: 'Antitrust compliance contacts', diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts new file mode 100644 index 0000000..d6d348a --- /dev/null +++ b/src/config/repoAccess.ts @@ -0,0 +1,329 @@ +// Repository access configuration +// Each repository lists all teams and users that should have access and their permission level + +export interface RepositoryAccess { + repository: string; + teams?: Array<{ + team: string; // Team slug + permission: 'pull' | 'triage' | 'push' | 'maintain' | 'admin'; + }>; + users?: Array<{ + username: string; // GitHub username + permission: 'pull' | 'triage' | 'push' | 'maintain' | 'admin'; + }>; +} + +export const REPOSITORY_ACCESS: RepositoryAccess[] = [ + { + repository: 'docs', + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + ], + teams: [ + { team: 'auth-wg', permission: 'push' }, + { team: 'core', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'push' }, + { team: 'csharp-sdk', permission: 'push' }, + { team: 'docs-maintaners', permission: 'push' }, + { team: 'go-sdk', permission: 'push' }, + { team: 'ig-financial-services', permission: 'push' }, + { team: 'interest-groups', permission: 'push' }, + { team: 'java-sdk', permission: 'push' }, + { team: 'kotlin-sdk', permission: 'push' }, + { team: 'moderators', permission: 'push' }, + { team: 'php-sdk', permission: 'push' }, + { team: 'python-sdk', permission: 'push' }, + { team: 'python-sdk-auth', permission: 'push' }, + { team: 'registry-wg', permission: 'push' }, + { team: 'ruby-sdk', permission: 'push' }, + { team: 'rust-sdk', permission: 'push' }, + { team: 'sdk-maintainers', permission: 'push' }, + { team: 'security-wg', permission: 'admin' }, + { team: 'steering-committee', permission: 'push' }, + { team: 'swift-sdk', permission: 'push' }, + { team: 'transport-wg', permission: 'push' }, + { team: 'typescript-sdk', permission: 'push' }, + { team: 'typescript-sdk-auth', permission: 'push' }, + { team: 'working-groups', permission: 'push' }, + ], + }, + { + repository: '.github', + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + ], + teams: [ + { team: 'auth-wg', permission: 'triage' }, + { team: 'core', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'push' }, + { team: 'csharp-sdk', permission: 'triage' }, + { team: 'docs-maintaners', permission: 'triage' }, + { team: 'go-sdk', permission: 'triage' }, + { team: 'ig-financial-services', permission: 'triage' }, + { team: 'interest-groups', permission: 'triage' }, + { team: 'java-sdk', permission: 'triage' }, + { team: 'kotlin-sdk', permission: 'triage' }, + { team: 'moderators', permission: 'push' }, + { team: 'php-sdk', permission: 'triage' }, + { team: 'python-sdk', permission: 'triage' }, + { team: 'python-sdk-auth', permission: 'triage' }, + { team: 'registry-wg', permission: 'triage' }, + { team: 'ruby-sdk', permission: 'triage' }, + { team: 'rust-sdk', permission: 'triage' }, + { team: 'sdk-maintainers', permission: 'triage' }, + { team: 'security-wg', permission: 'admin' }, + { team: 'steering-committee', permission: 'triage' }, + { team: 'swift-sdk', permission: 'triage' }, + { team: 'transport-wg', permission: 'triage' }, + { team: 'typescript-sdk', permission: 'triage' }, + { team: 'typescript-sdk-auth', permission: 'triage' }, + { team: 'working-groups', permission: 'triage' }, + ], + }, + { + repository: 'inspector', + users: [ + { username: 'richardkmichael', permission: 'triage' }, + { username: 'jspahrsummers', permission: 'admin' }, + { username: 'olaservo', permission: 'push' }, + { username: 'an-dustin', permission: 'admin' }, + { username: 'ashwin-ant', permission: 'admin' }, + ], + teams: [ + { team: 'auth-wg', permission: 'push' }, + { team: 'core', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'push' }, + { team: 'csharp-sdk', permission: 'push' }, + { team: 'docs-maintaners', permission: 'push' }, + { team: 'go-sdk', permission: 'push' }, + { team: 'ig-financial-services', permission: 'push' }, + { team: 'interest-groups', permission: 'push' }, + { team: 'java-sdk', permission: 'push' }, + { team: 'kotlin-sdk', permission: 'push' }, + { team: 'moderators', permission: 'push' }, + { team: 'php-sdk', permission: 'push' }, + { team: 'python-sdk', permission: 'push' }, + { team: 'python-sdk-auth', permission: 'push' }, + { team: 'registry-wg', permission: 'push' }, + { team: 'ruby-sdk', permission: 'push' }, + { team: 'rust-sdk', permission: 'push' }, + { team: 'sdk-maintainers', permission: 'push' }, + { team: 'security-wg', permission: 'admin' }, + { team: 'steering-committee', permission: 'push' }, + { team: 'swift-sdk', permission: 'push' }, + { team: 'transport-wg', permission: 'push' }, + { team: 'typescript-sdk', permission: 'push' }, + { team: 'typescript-sdk-auth', permission: 'push' }, + { team: 'working-groups', permission: 'push' }, + ], + }, + { + repository: 'modelcontextprotocol', + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + ], + teams: [ + { team: 'auth-wg', permission: 'push' }, + { team: 'core', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'push' }, + { team: 'csharp-sdk', permission: 'triage' }, + { team: 'docs-maintaners', permission: 'push' }, + { team: 'go-sdk', permission: 'triage' }, + { team: 'ig-financial-services', permission: 'triage' }, + { team: 'interest-groups', permission: 'triage' }, + { team: 'java-sdk', permission: 'triage' }, + { team: 'kotlin-sdk', permission: 'triage' }, + { team: 'moderators', permission: 'triage' }, + { team: 'php-sdk', permission: 'triage' }, + { team: 'python-sdk', permission: 'triage' }, + { team: 'python-sdk-auth', permission: 'triage' }, + { team: 'registry-wg', permission: 'triage' }, + { team: 'ruby-sdk', permission: 'triage' }, + { team: 'rust-sdk', permission: 'triage' }, + { team: 'sdk-maintainers', permission: 'triage' }, + { team: 'security-wg', permission: 'admin' }, + { team: 'steering-committee', permission: 'triage' }, + { team: 'swift-sdk', permission: 'triage' }, + { team: 'transport-wg', permission: 'triage' }, + { team: 'typescript-sdk', permission: 'triage' }, + { team: 'typescript-sdk-auth', permission: 'triage' }, + { team: 'working-groups', permission: 'triage' }, + ], + }, + { + repository: 'quickstart-resources', + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + ], + teams: [ + { team: 'auth-wg', permission: 'push' }, + { team: 'core', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'push' }, + { team: 'csharp-sdk', permission: 'push' }, + { team: 'docs-maintaners', permission: 'push' }, + { team: 'go-sdk', permission: 'push' }, + { team: 'ig-financial-services', permission: 'push' }, + { team: 'interest-groups', permission: 'push' }, + { team: 'java-sdk', permission: 'push' }, + { team: 'kotlin-sdk', permission: 'push' }, + { team: 'moderators', permission: 'push' }, + { team: 'php-sdk', permission: 'push' }, + { team: 'python-sdk', permission: 'push' }, + { team: 'python-sdk-auth', permission: 'push' }, + { team: 'registry-wg', permission: 'push' }, + { team: 'ruby-sdk', permission: 'push' }, + { team: 'rust-sdk', permission: 'push' }, + { team: 'sdk-maintainers', permission: 'push' }, + { team: 'security-wg', permission: 'admin' }, + { team: 'steering-committee', permission: 'push' }, + { team: 'swift-sdk', permission: 'push' }, + { team: 'transport-wg', permission: 'push' }, + { team: 'typescript-sdk', permission: 'push' }, + { team: 'typescript-sdk-auth', permission: 'push' }, + { team: 'working-groups', permission: 'push' }, + ], + }, + { + repository: 'servers', + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + { username: 'slimslenderslacks', permission: 'push' }, + ], + teams: [ + { team: 'auth-wg', permission: 'push' }, + { team: 'core', permission: 'admin' }, + { team: 'core-maintainers', permission: 'push' }, + { team: 'csharp-sdk', permission: 'push' }, + { team: 'docs-maintaners', permission: 'push' }, + { team: 'go-sdk', permission: 'push' }, + { team: 'ig-financial-services', permission: 'push' }, + { team: 'interest-groups', permission: 'push' }, + { team: 'java-sdk', permission: 'push' }, + { team: 'kotlin-sdk', permission: 'push' }, + { team: 'moderators', permission: 'push' }, + { team: 'php-sdk', permission: 'push' }, + { team: 'python-sdk', permission: 'push' }, + { team: 'python-sdk-auth', permission: 'push' }, + { team: 'registry-wg', permission: 'push' }, + { team: 'ruby-sdk', permission: 'push' }, + { team: 'rust-sdk', permission: 'push' }, + { team: 'sdk-maintainers', permission: 'push' }, + { team: 'security-wg', permission: 'admin' }, + { team: 'steering-committee', permission: 'push' }, + { team: 'swift-sdk', permission: 'push' }, + { team: 'transport-wg', permission: 'push' }, + { team: 'typescript-sdk', permission: 'push' }, + { team: 'typescript-sdk-auth', permission: 'push' }, + { team: 'working-groups', permission: 'push' }, + ], + }, + { + repository: 'csharp-sdk', + teams: [{ team: 'csharp-sdk', permission: 'admin' }], + users: [ + { username: 'jeffhandley', permission: 'admin' }, + { username: 'MackinnonBuck', permission: 'admin' }, + { username: 'jozkee', permission: 'admin' }, + { username: 'localden', permission: 'admin' }, + { username: 'PederHP', permission: 'triage' }, + ], + }, + { + repository: 'go-sdk', + teams: [{ team: 'go-sdk', permission: 'admin' }], + users: [ + { username: 'neild', permission: 'push' }, + { username: 'rsc', permission: 'push' }, + { username: 'rolandshoemaker', permission: 'push' }, + { username: 'h9jiang', permission: 'maintain' }, + ], + }, + { + repository: 'java-sdk', + teams: [{ team: 'java-sdk', permission: 'admin' }], + }, + { + repository: 'kotlin-sdk', + teams: [{ team: 'kotlin-sdk', permission: 'admin' }], + }, + { + repository: 'php-sdk', + teams: [{ team: 'php-sdk', permission: 'admin' }], + }, + { + repository: 'python-sdk', + teams: [ + { team: 'python-sdk', permission: 'admin' }, + { team: 'python-sdk-auth', permission: 'admin' }, + ], + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + ], + }, + { + repository: 'ruby-sdk', + teams: [{ team: 'ruby-sdk', permission: 'admin' }], + }, + { + repository: 'rust-sdk', + teams: [{ team: 'rust-sdk', permission: 'admin' }], + users: [ + { username: 'jamadeo', permission: 'admin' }, + { username: 'jokemanfire', permission: 'push' }, + { username: '4t145', permission: 'push' }, + ], + }, + { + repository: 'swift-sdk', + teams: [{ team: 'swift-sdk', permission: 'admin' }], + }, + { + repository: 'typescript-sdk', + teams: [ + { team: 'typescript-sdk', permission: 'admin' }, + { team: 'typescript-sdk-auth', permission: 'admin' }, + ], + users: [ + { username: 'jspahrsummers', permission: 'admin' }, + ], + }, + { + repository: 'create-python-server', + teams: [ + { team: 'python-sdk', permission: 'admin' }, + { team: 'python-sdk-auth', permission: 'admin' }, + ], + }, + { + repository: 'create-typescript-server', + teams: [ + { team: 'typescript-sdk', permission: 'admin' }, + { team: 'typescript-sdk-auth', permission: 'admin' }, + ], + }, + { + repository: 'registry', + teams: [{ team: 'registry-wg', permission: 'admin' }], + users: [ + { username: 'rdimitrov', permission: 'admin' }, + ], + }, + { + repository: 'static', + teams: [{ team: 'registry-wg', permission: 'push' }], + }, + { + repository: 'financial-services-interest-group', + teams: [{ team: 'ig-financial-services', permission: 'admin' }], + users: [ + { username: 'aniabot', permission: 'pull' }, + { username: 'imfing', permission: 'triage' }, + { username: 'sambhav', permission: 'admin' }, + { username: 'KengoA', permission: 'triage' }, + { username: 'nitsanh', permission: 'pull' }, + ], + }, +]; + +// GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet +// See: https://github.com/pulumi/pulumi-github/issues/1006 diff --git a/src/config/users.ts b/src/config/users.ts index 7eb78c4..723b0e9 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -1,10 +1,279 @@ import type { Member } from './utils'; export const MEMBERS: readonly Member[] = [ + { + github: '000-000-000-000-000', + memberOf: ['core-maintainers'], + }, + { + github: 'CodeWithKyrian', + memberOf: ['php-sdk'], + }, + { + github: 'D-McAdams', + memberOf: ['auth-wg'], + }, + { + github: 'Kehrlann', + memberOf: ['java-sdk'], + }, + { + github: 'Kludex', + memberOf: ['python-sdk'], + }, + { + github: 'Nyholm', + memberOf: ['php-sdk'], + }, + { + github: 'Ololoshechkin', + memberOf: ['kotlin-sdk'], + }, + { + github: 'aaronpk', + memberOf: ['auth-wg'], + }, + { + github: 'alexhancock', + memberOf: ['rust-sdk'], + }, + { + github: 'an-dustin', + memberOf: ['security-wg'], + }, + { + github: 'ansaba', + memberOf: ['go-sdk'], + }, + { + github: 'atesgoral', + memberOf: ['ruby-sdk'], + }, + { + github: 'baxen', + memberOf: ['rust-sdk'], + }, + { + github: 'bhosmer-ant', + memberOf: ['core', 'core-maintainers', 'docs-maintaners', 'moderators'], + }, + { + github: 'carlpeaslee', + memberOf: ['swift-sdk'], + }, + { + github: 'chemicL', + memberOf: ['java-sdk'], + }, + { + github: 'chr-hertel', + memberOf: ['php-sdk'], + }, + { + github: 'cliffhall', + memberOf: ['moderators'], + }, + { + github: 'crondinini-ant', + memberOf: ['core'], + }, + { + github: 'dend', + memberOf: ['csharp-sdk'], + }, + { + github: 'devcrocod', + memberOf: ['kotlin-sdk'], + }, { github: 'domdomegg', email: 'adam@modelcontextprotocol.io', - memberOf: ['test-child'], + memberOf: ['core', 'registry-wg'], + }, + { + github: 'dsp-ant', + email: 'david@modelcontextprotocol.io', + memberOf: ['auth-wg', 'core', 'core-maintainers', 'docs-maintaners', 'go-sdk', 'ig-financial-services', 'moderators', 'php-sdk', 'python-sdk', 'security-wg', 'transport-wg', 'typescript-sdk'], + }, + { + github: 'e5l', + memberOf: ['kotlin-sdk'], + }, + { + github: 'eiriktsarpalis', + memberOf: ['csharp-sdk'], + }, + { + github: 'evalstate', + memberOf: ['moderators'], + }, + { + github: 'fabpot', + memberOf: ['php-sdk'], + }, + { + github: 'felixweinberger', + memberOf: ['core', 'python-sdk', 'security-wg', 'typescript-sdk'], + }, + { + github: 'findleyr', + memberOf: ['go-sdk'], + }, + { + github: 'halter73', + memberOf: ['csharp-sdk'], + }, + { + github: 'ignatov', + memberOf: ['kotlin-sdk'], + }, + { + github: 'ihrpr', + memberOf: ['core', 'core-maintainers', 'docs-maintaners', 'python-sdk', 'typescript-sdk'], + }, + { + github: 'jamadeo', + memberOf: ['rust-sdk'], + }, + { + github: 'jba', + memberOf: ['go-sdk'], + }, + { + github: 'jenn-newton', + memberOf: ['security-wg'], + }, + { + github: 'jerome3o-anthropic', + memberOf: ['core', 'moderators'], + }, + { + github: 'jonathanhefner', + memberOf: ['docs-maintaners', 'moderators'], + }, + { + github: 'jspahrsummers', + email: 'justin@modelcontextprotocol.io', + memberOf: ['core-maintainers'], + }, + { + github: 'koic', + memberOf: ['ruby-sdk'], + }, + { + github: 'kpavlov', + memberOf: ['kotlin-sdk'], + }, + { + github: 'kurtisvg', + memberOf: ['transport-wg'], + }, + { + github: 'localden', + memberOf: ['auth-wg', 'core-maintainers'], + }, + { + github: 'maheshmurag', + memberOf: ['core', 'moderators'], + }, + { + github: 'markpollack', + memberOf: ['java-sdk'], + }, + { + github: 'mattt', + memberOf: ['swift-sdk'], + }, + { + github: 'maxisbey', + memberOf: ['core'], + }, + { + github: 'michaelneale', + memberOf: ['rust-sdk'], + }, + { + github: 'mikekistler', + memberOf: ['csharp-sdk'], + }, + { + github: 'nickcoai', + memberOf: ['core-maintainers'], + }, + { + github: 'nicolas-grekas', + memberOf: ['php-sdk'], + }, + { + github: 'ochafik', + memberOf: ['core', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth'], + }, + { + github: 'og-ant', + memberOf: ['security-wg'], + }, + { + github: 'olaservo', + memberOf: ['docs-maintaners', 'moderators'], + }, + { + github: 'pcarleton', + memberOf: ['core-maintainers', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth'], + }, + { + github: 'petery-ant', + memberOf: ['security-wg'], + }, + { + github: 'pronskiy', + memberOf: ['php-sdk'], + }, + { + github: 'pwwpche', + memberOf: ['core-maintainers'], + }, + { + github: 'rdimitrov', + email: 'radoslav@modelcontextprotocol.io', + memberOf: ['registry-wg'], + }, + { + github: 'sambhav', + memberOf: ['ig-financial-services'], + }, + { + github: 'samthanawalla', + memberOf: ['go-sdk'], + }, + { + github: 'sdubov', + memberOf: ['kotlin-sdk'], + }, + { + github: 'stephentoub', + memberOf: ['csharp-sdk'], + }, + { + github: 'tadasant', + email: 'tadas@modelcontextprotocol.io', + memberOf: ['moderators', 'registry-wg'], + }, + { + github: 'tiginamaria', + memberOf: ['kotlin-sdk'], + }, + { + github: 'toby', + email: 'toby@modelcontextprotocol.io', + memberOf: ['registry-wg'], + }, + { + github: 'topherbullock', + memberOf: ['ruby-sdk'], + }, + { + github: 'tzolov', + memberOf: ['docs-maintaners', 'java-sdk'], }, { email: 'adamj@anthropic.com', @@ -22,4 +291,4 @@ export const MEMBERS: readonly Member[] = [ email: 'davideramian@anthropic.com', memberOf: ['antitrust'], }, -] as const; \ No newline at end of file +] as const; diff --git a/src/github.ts b/src/github.ts index 6ec40be..d398bf6 100644 --- a/src/github.ts +++ b/src/github.ts @@ -1,5 +1,6 @@ import * as github from '@pulumi/github'; import { GROUPS } from './config/groups'; +import { REPOSITORY_ACCESS } from './config/repoAccess'; import type { Group } from './config/utils'; import { MEMBERS } from './config/users'; @@ -28,3 +29,17 @@ MEMBERS.forEach((member) => { }); }); }); + +REPOSITORY_ACCESS.forEach((repo) => { + new github.RepositoryCollaborators(`repo-${repo.repository}`, { + repository: repo.repository, + teams: repo.teams?.map((t) => ({ + teamId: teams[t.team].id, + permission: t.permission, + })), + users: repo.users?.map((u) => ({ + username: u.username, + permission: u.permission, + })), + }); +}); diff --git a/src/google.ts b/src/google.ts index e257ff1..503ded0 100644 --- a/src/google.ts +++ b/src/google.ts @@ -42,6 +42,9 @@ GROUPS.forEach((group: Group) => { }); group.memberOf?.forEach((parentGroupKey) => { + // Skip if parent group doesn't exist on Google (e.g., onlyOnPlatforms: ['github']) + if (!groups[parentGroupKey]) return; + new gworkspace.GroupMember(`${group.name}-in-${parentGroupKey}`, { groupId: groups[parentGroupKey].id, email: groups[group.name].email, @@ -54,6 +57,9 @@ MEMBERS.forEach((member) => { if (!member.email) return; member.memberOf.forEach((teamKey) => { + // Skip if group doesn't exist on Google (e.g., onlyOnPlatforms: ['github']) + if (!groups[teamKey]) return; + new gworkspace.GroupMember(`${member.email}-${teamKey}`, { groupId: groups[teamKey].id, email: member.email!, From 45497bf4e53176d0c86fb08589d7587bda818f9e Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 6 Oct 2025 13:40:06 +0200 Subject: [PATCH 003/196] give akimov triage on quickstart-resources --- src/config/repoAccess.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index d6d348a..4af8c66 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -154,6 +154,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'quickstart-resources', users: [ { username: 'jspahrsummers', permission: 'admin' }, + { username: 'a-akimov', permission: 'triage' }, ], teams: [ { team: 'auth-wg', permission: 'push' }, From 6e41d744aad3ebe4026a968bb4dcb60c92ff3521 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Mon, 6 Oct 2025 22:04:04 +0200 Subject: [PATCH 004/196] Add a-akimov to quickstart-resources; Add pre-commit hooks for code quality (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * give akimov triage on quickstart-resources * Add pre-commit hooks for code quality Includes standard checks (trailing whitespace, EOF, YAML, merge conflicts) and TypeScript type checking. Expensive checks like Pulumi preview are intentionally excluded for CI instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --------- Co-authored-by: Claude --- .pre-commit-config.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..dd0261b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-merge-conflict + + - repo: local + hooks: + - id: typescript-check + name: TypeScript type check + entry: npm run build + language: system + pass_filenames: false + files: \.(ts|tsx)$ From db8d9eae6cf99e01fa8520e9b249843d35fa11a4 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Tue, 7 Oct 2025 16:28:40 -0700 Subject: [PATCH 005/196] Add evalstate and cliffhall to docs-maintaners team (#7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude --- src/config/users.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 723b0e9..873c9c0 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -71,7 +71,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'cliffhall', - memberOf: ['moderators'], + memberOf: ['docs-maintaners', 'moderators'], }, { github: 'crondinini-ant', @@ -105,7 +105,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'evalstate', - memberOf: ['moderators'], + memberOf: ['docs-maintaners', 'moderators'], }, { github: 'fabpot', From 55fe24b3fa6ebdc18997f0ebb25a450c434f0af1 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Tue, 14 Oct 2025 15:38:07 +0100 Subject: [PATCH 006/196] Grant auth-wg maintainers admin on ext-auth repository (#8) * grant auth-wg admin on ext-auth * add pcarleton to auth-wg --- src/config/repoAccess.ts | 6 ++++++ src/config/users.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 4af8c66..c612feb 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -324,6 +324,12 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'nitsanh', permission: 'pull' }, ], }, + { + repository: 'ext-auth', + teams: [ + { team: 'auth-wg', permission: 'admin' }, + ], + }, ]; // GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet diff --git a/src/config/users.ts b/src/config/users.ts index 873c9c0..b210562 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -218,7 +218,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'pcarleton', - memberOf: ['core-maintainers', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth'], + memberOf: ['core-maintainers', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth', 'auth-wg'], }, { github: 'petery-ant', From e6f5f446fb3f3fbeb6fb9620ce62fa888ac0b765 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 20 Oct 2025 11:58:47 -0700 Subject: [PATCH 007/196] Add inspector maintainer group (#9) --- src/config/groups.ts | 6 ++++++ src/config/repoAccess.ts | 2 +- src/config/users.ts | 8 ++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/config/groups.ts b/src/config/groups.ts index 2417b6f..18b6872 100644 --- a/src/config/groups.ts +++ b/src/config/groups.ts @@ -39,6 +39,12 @@ export const GROUPS = defineGroups([ memberOf: ['steering-committee'], onlyOnPlatforms: ['github'], }, + { + name: 'inspector-maintainers', + description: 'MCP Inspector maintainers', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, // SDK Maintainers { diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index c612feb..0662689 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -85,11 +85,11 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ users: [ { username: 'richardkmichael', permission: 'triage' }, { username: 'jspahrsummers', permission: 'admin' }, - { username: 'olaservo', permission: 'push' }, { username: 'an-dustin', permission: 'admin' }, { username: 'ashwin-ant', permission: 'admin' }, ], teams: [ + { team: 'inspector-maintainers', permission: 'push' }, { team: 'auth-wg', permission: 'push' }, { team: 'core', permission: 'maintain' }, { team: 'core-maintainers', permission: 'push' }, diff --git a/src/config/users.ts b/src/config/users.ts index b210562..8836b8b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -71,7 +71,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'cliffhall', - memberOf: ['docs-maintaners', 'moderators'], + memberOf: ['docs-maintaners', 'inspector-maintainers', 'moderators'], }, { github: 'crondinini-ant', @@ -156,6 +156,10 @@ export const MEMBERS: readonly Member[] = [ email: 'justin@modelcontextprotocol.io', memberOf: ['core-maintainers'], }, + { + github: 'KKonstantinov', + memberOf: ['inspector-maintainers'], + }, { github: 'koic', memberOf: ['ruby-sdk'], @@ -214,7 +218,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'olaservo', - memberOf: ['docs-maintaners', 'moderators'], + memberOf: ['docs-maintaners', 'inspector-maintainers', 'moderators'], }, { github: 'pcarleton', From 0c72db3ac9effedab12a2c437938c44fae314add Mon Sep 17 00:00:00 2001 From: bhosmer-ant Date: Sat, 25 Oct 2025 11:51:51 -0400 Subject: [PATCH 008/196] add bhosmer-ant to typescript-sdk, python-sdk (#10) --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 8836b8b..5615f4f 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -55,7 +55,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'bhosmer-ant', - memberOf: ['core', 'core-maintainers', 'docs-maintaners', 'moderators'], + memberOf: ['core', 'core-maintainers', 'docs-maintaners', 'moderators', 'python-sdk', 'typescript-sdk'], }, { github: 'carlpeaslee', From 96279fb1289396fa30c09e121d2bfd4c4acb4d6e Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Tue, 4 Nov 2025 16:58:43 +0000 Subject: [PATCH 009/196] Add Peder to FSIG (#11) --- src/config/users.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 5615f4f..5ee329e 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -224,6 +224,10 @@ export const MEMBERS: readonly Member[] = [ github: 'pcarleton', memberOf: ['core-maintainers', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth', 'auth-wg'], }, + { + github: 'pederhp', + memberOf: ['ig-financial-services'], + }, { github: 'petery-ant', memberOf: ['security-wg'], From eb56e470d8a708f27828f946d7697f971d82e961 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 7 Nov 2025 16:31:52 +0000 Subject: [PATCH 010/196] make max a python sdk maintianer --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 5ee329e..e3bae78 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -190,7 +190,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'maxisbey', - memberOf: ['core'], + memberOf: ['core', 'python-sdk'], }, { github: 'michaelneale', From 9699fbb3caf897f866525d7316f3b8079c724f67 Mon Sep 17 00:00:00 2001 From: adam jones Date: Tue, 11 Nov 2025 12:49:15 +0000 Subject: [PATCH 011/196] Add MCPB maintainers group (#12) Creates a new mcpb-maintainers team under the steering committee with Joan Xie (joan-anthropic), Adam Jones (domdomegg), and Alex Sklar (asklar) as initial maintainers. --- src/config/groups.ts | 6 ++++++ src/config/users.ts | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/config/groups.ts b/src/config/groups.ts index 18b6872..1a75a51 100644 --- a/src/config/groups.ts +++ b/src/config/groups.ts @@ -45,6 +45,12 @@ export const GROUPS = defineGroups([ memberOf: ['steering-committee'], onlyOnPlatforms: ['github'], }, + { + name: 'mcpb-maintainers', + description: 'MCPB (Model Context Protocol Bundle) maintainers', + memberOf: ['steering-committee'], + onlyOnPlatforms: ['github'], + }, // SDK Maintainers { diff --git a/src/config/users.ts b/src/config/users.ts index e3bae78..82e0ceb 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -45,6 +45,10 @@ export const MEMBERS: readonly Member[] = [ github: 'ansaba', memberOf: ['go-sdk'], }, + { + github: 'asklar', + memberOf: ['mcpb-maintainers'], + }, { github: 'atesgoral', memberOf: ['ruby-sdk'], @@ -88,7 +92,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'domdomegg', email: 'adam@modelcontextprotocol.io', - memberOf: ['core', 'registry-wg'], + memberOf: ['core', 'mcpb-maintainers', 'registry-wg'], }, { github: 'dsp-ant', @@ -147,6 +151,10 @@ export const MEMBERS: readonly Member[] = [ github: 'jerome3o-anthropic', memberOf: ['core', 'moderators'], }, + { + github: 'joan-anthropic', + memberOf: ['mcpb-maintainers'], + }, { github: 'jonathanhefner', memberOf: ['docs-maintaners', 'moderators'], From d0cb7df92f5262f8e054e39e77bff42f1edbc416 Mon Sep 17 00:00:00 2001 From: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:22:38 +0000 Subject: [PATCH 012/196] Add KKonstantinov and mattzcarey to typescript-sdk (#13) --- src/config/users.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 82e0ceb..a225b0d 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -166,7 +166,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'KKonstantinov', - memberOf: ['inspector-maintainers'], + memberOf: ['inspector-maintainers', 'typescript-sdk'], }, { github: 'koic', @@ -200,6 +200,10 @@ export const MEMBERS: readonly Member[] = [ github: 'maxisbey', memberOf: ['core', 'python-sdk'], }, + { + github: 'mattzcarey', + memberOf: ['typescript-sdk'], + }, { github: 'michaelneale', memberOf: ['rust-sdk'], From 0377e271ea5e9c61e6086f4845494f3f85b81ffc Mon Sep 17 00:00:00 2001 From: joan-anthropic Date: Fri, 14 Nov 2025 12:15:26 -0800 Subject: [PATCH 013/196] Add felixreiseberg and marshallofsound to mcpb-maintainers (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Felix (felixreiseberg) and Sam (marshallofsound) as maintainers for the Model Context Protocol Bundle (MCPB) project. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude --- src/config/users.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index a225b0d..98e9777 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -115,6 +115,10 @@ export const MEMBERS: readonly Member[] = [ github: 'fabpot', memberOf: ['php-sdk'], }, + { + github: 'felixreiseberg', + memberOf: ['mcpb-maintainers'], + }, { github: 'felixweinberger', memberOf: ['core', 'python-sdk', 'security-wg', 'typescript-sdk'], @@ -192,6 +196,10 @@ export const MEMBERS: readonly Member[] = [ github: 'markpollack', memberOf: ['java-sdk'], }, + { + github: 'marshallofsound', + memberOf: ['mcpb-maintainers'], + }, { github: 'mattt', memberOf: ['swift-sdk'], From e469f7ea0a6ecd0bdbf1207262559f8d9717bfda Mon Sep 17 00:00:00 2001 From: joan-anthropic Date: Fri, 14 Nov 2025 13:09:43 -0800 Subject: [PATCH 014/196] fix typo; (#16) --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 98e9777..dfe8872 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -116,7 +116,7 @@ export const MEMBERS: readonly Member[] = [ memberOf: ['php-sdk'], }, { - github: 'felixreiseberg', + github: 'felixrieseberg', memberOf: ['mcpb-maintainers'], }, { From 7b068c2d33d9f7c9b85d22132d5f65e21de994bf Mon Sep 17 00:00:00 2001 From: adam jones Date: Tue, 25 Nov 2025 17:29:55 +0000 Subject: [PATCH 015/196] Remove core group (#17) --- src/config/groups.ts | 6 ------ src/config/users.ts | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/config/groups.ts b/src/config/groups.ts index 1a75a51..73291b4 100644 --- a/src/config/groups.ts +++ b/src/config/groups.ts @@ -15,12 +15,6 @@ export const GROUPS = defineGroups([ description: 'MCP Steering Committee', onlyOnPlatforms: ['github'], }, - { - name: 'core', - description: 'Core team', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, { name: 'core-maintainers', description: 'Core maintainers', diff --git a/src/config/users.ts b/src/config/users.ts index dfe8872..b0e6ae9 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -59,7 +59,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'bhosmer-ant', - memberOf: ['core', 'core-maintainers', 'docs-maintaners', 'moderators', 'python-sdk', 'typescript-sdk'], + memberOf: ['core-maintainers', 'docs-maintaners', 'moderators', 'python-sdk', 'typescript-sdk'], }, { github: 'carlpeaslee', @@ -79,7 +79,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'crondinini-ant', - memberOf: ['core'], + memberOf: [], }, { github: 'dend', @@ -92,12 +92,12 @@ export const MEMBERS: readonly Member[] = [ { github: 'domdomegg', email: 'adam@modelcontextprotocol.io', - memberOf: ['core', 'mcpb-maintainers', 'registry-wg'], + memberOf: ['mcpb-maintainers', 'registry-wg'], }, { github: 'dsp-ant', email: 'david@modelcontextprotocol.io', - memberOf: ['auth-wg', 'core', 'core-maintainers', 'docs-maintaners', 'go-sdk', 'ig-financial-services', 'moderators', 'php-sdk', 'python-sdk', 'security-wg', 'transport-wg', 'typescript-sdk'], + memberOf: ['auth-wg', 'core-maintainers', 'docs-maintaners', 'go-sdk', 'ig-financial-services', 'moderators', 'php-sdk', 'python-sdk', 'security-wg', 'transport-wg', 'typescript-sdk'], }, { github: 'e5l', @@ -121,7 +121,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'felixweinberger', - memberOf: ['core', 'python-sdk', 'security-wg', 'typescript-sdk'], + memberOf: ['python-sdk', 'security-wg', 'typescript-sdk'], }, { github: 'findleyr', @@ -137,7 +137,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ihrpr', - memberOf: ['core', 'core-maintainers', 'docs-maintaners', 'python-sdk', 'typescript-sdk'], + memberOf: ['core-maintainers', 'docs-maintaners', 'python-sdk', 'typescript-sdk'], }, { github: 'jamadeo', @@ -153,7 +153,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'jerome3o-anthropic', - memberOf: ['core', 'moderators'], + memberOf: ['moderators'], }, { github: 'joan-anthropic', @@ -190,7 +190,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'maheshmurag', - memberOf: ['core', 'moderators'], + memberOf: ['moderators'], }, { github: 'markpollack', @@ -206,7 +206,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'maxisbey', - memberOf: ['core', 'python-sdk'], + memberOf: ['python-sdk'], }, { github: 'mattzcarey', @@ -230,7 +230,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ochafik', - memberOf: ['core', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth'], + memberOf: ['python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth'], }, { github: 'og-ant', From 524a045a2038dcb75cff543c92f78df941ee78df Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 1 Dec 2025 14:40:45 +0000 Subject: [PATCH 016/196] give akimov write access --- src/config/repoAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 0662689..0af4e99 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -154,7 +154,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'quickstart-resources', users: [ { username: 'jspahrsummers', permission: 'admin' }, - { username: 'a-akimov', permission: 'triage' }, + { username: 'a-akimov', permission: 'write' }, ], teams: [ { team: 'auth-wg', permission: 'push' }, From e20b61b60585d78c68b2a415c8ca9ac6d954878b Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 1 Dec 2025 17:11:00 +0000 Subject: [PATCH 017/196] fix --- src/config/repoAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 0af4e99..154e919 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -154,7 +154,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'quickstart-resources', users: [ { username: 'jspahrsummers', permission: 'admin' }, - { username: 'a-akimov', permission: 'write' }, + { username: 'a-akimov', permission: 'push' }, ], teams: [ { team: 'auth-wg', permission: 'push' }, From e9821691b9ef4acd37df58d0f91746aa60abbcdb Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 09:42:56 +0000 Subject: [PATCH 018/196] add michael bolin from openai as a rust sdk maintainer --- src/config/repoAccess.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 154e919..e8caf57 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -272,6 +272,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jamadeo', permission: 'admin' }, { username: 'jokemanfire', permission: 'push' }, { username: '4t145', permission: 'push' }, + { username: 'bolinfest', permission: 'push' }, ], }, { From 8d0a1bc7a1863ddc91d3394d2ccf25110f6fb175 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 09:48:13 +0000 Subject: [PATCH 019/196] fix --- src/config/repoAccess.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index e8caf57..3514bbf 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -273,6 +273,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jokemanfire', permission: 'push' }, { username: '4t145', permission: 'push' }, { username: 'bolinfest', permission: 'push' }, + { username: 'alexhancock', permission: 'push' }, ], }, { From 680934d3a8d7a2e4bf5b491f08d3de2cd7d6e6f1 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 09:58:49 +0000 Subject: [PATCH 020/196] Fix deployment: remove stale 'core' team references and add validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #17 removed the 'core' group but left references in repoAccess.ts, causing deployments to fail since Nov 25th. Changes: - Remove 'core' team references from 6 repositories in repoAccess.ts - Replace with 'core-maintainers' to maintain equivalent permissions - Add scripts/validate-config.ts to validate all team references - Add pre-commit hook to run validation on config changes - Add npm run validate script 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .pre-commit-config.yaml | 7 ++++ package.json | 3 +- scripts/validate-config.ts | 82 ++++++++++++++++++++++++++++++++++++++ src/config/repoAccess.ts | 18 +++------ 4 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 scripts/validate-config.ts diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dd0261b..770ad7e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,3 +15,10 @@ repos: language: system pass_filenames: false files: \.(ts|tsx)$ + + - id: validate-config + name: Validate team references + entry: npm run validate + language: system + pass_filenames: false + files: ^src/config/ diff --git a/package.json b/package.json index fed8ef4..42d2018 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Infrastructure as Code for MCP access management", "main": "src/index.ts", "scripts": { - "build": "tsc" + "build": "tsc", + "validate": "npx ts-node scripts/validate-config.ts" }, "license": "MIT", "dependencies": { diff --git a/scripts/validate-config.ts b/scripts/validate-config.ts new file mode 100644 index 0000000..d813246 --- /dev/null +++ b/scripts/validate-config.ts @@ -0,0 +1,82 @@ +#!/usr/bin/env npx ts-node + +/** + * Validates that all team references in repoAccess.ts exist in groups.ts + * Run with: npx ts-node scripts/validate-config.ts + */ + +import { GROUPS } from '../src/config/groups'; +import { REPOSITORY_ACCESS } from '../src/config/repoAccess'; +import { MEMBERS } from '../src/config/users'; +import type { Group, Member } from '../src/config/utils'; + +// Get all GitHub team names (groups not limited to google-only platforms) +const githubTeamNames = new Set(); +// Get all group names (for member validation - members can be in any group) +const allGroupNames = new Set(); + +for (const g of GROUPS) { + const group = g as Group; + allGroupNames.add(group.name); + + const platforms = group.onlyOnPlatforms; + if (!platforms || platforms.includes('github')) { + githubTeamNames.add(group.name); + } +} + +let hasErrors = false; + +// Validate team references in REPOSITORY_ACCESS +console.log('Validating team references in repoAccess.ts...'); +for (const repo of REPOSITORY_ACCESS) { + if (!repo.teams) continue; + + for (const teamRef of repo.teams) { + if (!githubTeamNames.has(teamRef.team)) { + console.error( + `ERROR: Repository "${repo.repository}" references team "${teamRef.team}" which does not exist in groups.ts` + ); + hasErrors = true; + } + } +} + +// Validate team references in MEMBERS (memberOf) +// Members can be in any group (GitHub or Google-only) +console.log('Validating team references in users.ts...'); +for (const m of MEMBERS) { + const member = m as Member; + for (const teamKey of member.memberOf) { + if (!allGroupNames.has(teamKey)) { + console.error( + `ERROR: Member "${member.github || member.email}" references team "${teamKey}" which does not exist in groups.ts` + ); + hasErrors = true; + } + } +} + +// Validate parent team references (memberOf in groups) +console.log('Validating parent team references in groups.ts...'); +for (const g of GROUPS) { + const group = g as Group; + if (!group.memberOf) continue; + + for (const parentTeam of group.memberOf) { + if (!allGroupNames.has(parentTeam)) { + console.error( + `ERROR: Group "${group.name}" has parent "${parentTeam}" which does not exist in groups.ts` + ); + hasErrors = true; + } + } +} + +if (hasErrors) { + console.error('\nValidation failed! Please fix the errors above.'); + process.exit(1); +} else { + console.log('\nAll team references are valid.'); + process.exit(0); +} diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 3514bbf..e05b59f 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -21,8 +21,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], teams: [ { team: 'auth-wg', permission: 'push' }, - { team: 'core', permission: 'maintain' }, - { team: 'core-maintainers', permission: 'push' }, + { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, @@ -54,8 +53,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], teams: [ { team: 'auth-wg', permission: 'triage' }, - { team: 'core', permission: 'maintain' }, - { team: 'core-maintainers', permission: 'push' }, + { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'triage' }, { team: 'docs-maintaners', permission: 'triage' }, { team: 'go-sdk', permission: 'triage' }, @@ -91,8 +89,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ teams: [ { team: 'inspector-maintainers', permission: 'push' }, { team: 'auth-wg', permission: 'push' }, - { team: 'core', permission: 'maintain' }, - { team: 'core-maintainers', permission: 'push' }, + { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, @@ -124,8 +121,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], teams: [ { team: 'auth-wg', permission: 'push' }, - { team: 'core', permission: 'maintain' }, - { team: 'core-maintainers', permission: 'push' }, + { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'triage' }, { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'triage' }, @@ -158,8 +154,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], teams: [ { team: 'auth-wg', permission: 'push' }, - { team: 'core', permission: 'maintain' }, - { team: 'core-maintainers', permission: 'push' }, + { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, @@ -192,8 +187,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], teams: [ { team: 'auth-wg', permission: 'push' }, - { team: 'core', permission: 'admin' }, - { team: 'core-maintainers', permission: 'push' }, + { team: 'core-maintainers', permission: 'admin' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, From 3c1d641add7c6e103e391baba07e8c41bd5f43f4 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 10:01:28 +0000 Subject: [PATCH 021/196] Add prettier formatting and CI validation checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add prettier with consistent formatting config - Add format and format:check npm scripts - Add prettier pre-commit hook - Add validation and formatting checks to CI workflow - Reformat all TypeScript and JSON files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci.yml | 8 +++++++- .pre-commit-config.yaml | 7 +++++++ .prettierignore | 5 +++++ .prettierrc | 7 +++++++ README.md | 3 +++ package-lock.json | 17 +++++++++++++++++ package.json | 5 ++++- src/config/groups.ts | 2 +- src/config/repoAccess.ts | 28 +++++++--------------------- src/config/users.ts | 23 +++++++++++++++++++++-- src/config/utils.ts | 6 +++--- src/google.ts | 21 +++++++++++---------- tsconfig.json | 2 +- 13 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1ff399..bcbd3db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,4 +33,10 @@ jobs: run: npm ci - name: Build - run: npm run build \ No newline at end of file + run: npm run build + + - name: Validate config + run: npm run validate + + - name: Check formatting + run: npm run format:check \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 770ad7e..7962d99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,3 +22,10 @@ repos: language: system pass_filenames: false files: ^src/config/ + + - id: prettier + name: Prettier formatting + entry: npx prettier --write --ignore-unknown + language: system + types: [text] + files: \.(ts|tsx|js|jsx|json|md)$ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..8e8e1fe --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +node_modules/ +sdks/ +*.yaml +*.yml +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a95cb05 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 2 +} diff --git a/README.md b/README.md index 57a50af..e07859b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Infrastructure as Code for managing access to MCP community resources using Pulu ### Manual Deployment Pre-requisites: + - [Pulumi CLI installed](https://www.pulumi.com/docs/iac/download-install/) - [Google Cloud SDK installed](https://cloud.google.com/sdk/docs/install) - Access to GCP project and GCS bucket @@ -79,6 +80,7 @@ gsutil mb gs://mcp-access-prod-pulumi-state ``` Then: + 1. In Google Workspace Admin Console, go to **Account** → **Admin roles** 2. Select **Groups Admin** role (or create a custom role with these privileges): - Read, create, update, and delete groups @@ -104,5 +106,6 @@ 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 of `sa-key.json` - `PULUMI_PROD_PASSPHRASE`: The passphrase you set above diff --git a/package-lock.json b/package-lock.json index c914992..84cac9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@types/node": "^22.18.6", + "prettier": "^3.7.2", "typescript": "^5.9.2" } }, @@ -2513,6 +2514,22 @@ "node": ">=4" } }, + "node_modules/prettier": { + "version": "3.7.2", + "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/prettier/-/prettier-3.7.2.tgz", + "integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/proc-log": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", diff --git a/package.json b/package.json index 42d2018..59c9dd7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "src/index.ts", "scripts": { "build": "tsc", - "validate": "npx ts-node scripts/validate-config.ts" + "validate": "npx ts-node scripts/validate-config.ts", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "license": "MIT", "dependencies": { @@ -15,6 +17,7 @@ }, "devDependencies": { "@types/node": "^22.18.6", + "prettier": "^3.7.2", "typescript": "^5.9.2" } } diff --git a/src/config/groups.ts b/src/config/groups.ts index 73291b4..72c1552 100644 --- a/src/config/groups.ts +++ b/src/config/groups.ts @@ -184,4 +184,4 @@ export const GROUPS = defineGroups([ isEmailGroup: true, onlyOnPlatforms: ['google'], }, -] as const); \ No newline at end of file +] as const); diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index e05b59f..937a0f3 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -16,9 +16,7 @@ export interface RepositoryAccess { export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'docs', - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - ], + users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -48,9 +46,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: '.github', - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - ], + users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-wg', permission: 'triage' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -116,9 +112,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'modelcontextprotocol', - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - ], + users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -251,9 +245,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'python-sdk', permission: 'admin' }, { team: 'python-sdk-auth', permission: 'admin' }, ], - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - ], + users: [{ username: 'jspahrsummers', permission: 'admin' }], }, { repository: 'ruby-sdk', @@ -280,9 +272,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'typescript-sdk', permission: 'admin' }, { team: 'typescript-sdk-auth', permission: 'admin' }, ], - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - ], + users: [{ username: 'jspahrsummers', permission: 'admin' }], }, { repository: 'create-python-server', @@ -301,9 +291,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'registry', teams: [{ team: 'registry-wg', permission: 'admin' }], - users: [ - { username: 'rdimitrov', permission: 'admin' }, - ], + users: [{ username: 'rdimitrov', permission: 'admin' }], }, { repository: 'static', @@ -322,9 +310,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'ext-auth', - teams: [ - { team: 'auth-wg', permission: 'admin' }, - ], + teams: [{ team: 'auth-wg', permission: 'admin' }], }, ]; diff --git a/src/config/users.ts b/src/config/users.ts index b0e6ae9..da7a07c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -97,7 +97,19 @@ export const MEMBERS: readonly Member[] = [ { github: 'dsp-ant', email: 'david@modelcontextprotocol.io', - memberOf: ['auth-wg', 'core-maintainers', 'docs-maintaners', 'go-sdk', 'ig-financial-services', 'moderators', 'php-sdk', 'python-sdk', 'security-wg', 'transport-wg', 'typescript-sdk'], + memberOf: [ + 'auth-wg', + 'core-maintainers', + 'docs-maintaners', + 'go-sdk', + 'ig-financial-services', + 'moderators', + 'php-sdk', + 'python-sdk', + 'security-wg', + 'transport-wg', + 'typescript-sdk', + ], }, { github: 'e5l', @@ -242,7 +254,14 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'pcarleton', - memberOf: ['core-maintainers', 'python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth', 'auth-wg'], + memberOf: [ + 'core-maintainers', + 'python-sdk', + 'python-sdk-auth', + 'typescript-sdk', + 'typescript-sdk-auth', + 'auth-wg', + ], }, { github: 'pederhp', diff --git a/src/config/utils.ts b/src/config/utils.ts index 241b86f..fd0112a 100644 --- a/src/config/utils.ts +++ b/src/config/utils.ts @@ -10,10 +10,10 @@ export function defineGroups< const T extends readonly { name: Lowercase; description: string; - memberOf?: readonly (T[number]['name'])[]; + memberOf?: readonly T[number]['name'][]; isEmailGroup?: boolean; onlyOnPlatforms?: readonly Platform[]; - }[] + }[], >(groups: T) { for (const group of groups) { if (!isValidGroupName(group.name)) { @@ -40,4 +40,4 @@ export interface Member { github?: string; email?: string; memberOf: readonly GroupKey[]; -} \ No newline at end of file +} diff --git a/src/google.ts b/src/google.ts index 503ded0..96c7d7c 100644 --- a/src/google.ts +++ b/src/google.ts @@ -29,16 +29,17 @@ GROUPS.forEach((group: Group) => { // Email groups allow anyone (including externals) to post // Non-email groups are not intended as mailing lists, so use the most restrictive settings // whoCanViewGroup is badly named, but actually means 'Permissions to view group messages'. See https://developers.google.com/workspace/admin/groups-settings/v1/reference/groups - ...(group.isEmailGroup ? { - whoCanPostMessage: 'ANYONE_CAN_POST', - whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', - whoCanViewGroup: 'ALL_MEMBERS_CAN_VIEW', - } : { - whoCanPostMessage: 'ALL_OWNERS_CAN_POST', - whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', - whoCanViewGroup: 'ALL_OWNERS_CAN_VIEW', - }), - + ...(group.isEmailGroup + ? { + whoCanPostMessage: 'ANYONE_CAN_POST', + whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', + whoCanViewGroup: 'ALL_MEMBERS_CAN_VIEW', + } + : { + whoCanPostMessage: 'ALL_OWNERS_CAN_POST', + whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', + whoCanViewGroup: 'ALL_OWNERS_CAN_VIEW', + }), }); group.memberOf?.forEach((parentGroupKey) => { diff --git a/tsconfig.json b/tsconfig.json index 4b8e53f..09e8d66 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,4 +13,4 @@ }, "include": ["src/**/*"], "exclude": ["node_modules"] -} \ No newline at end of file +} From fc9e1bae132ef5f2f97aaad0e1356a0a9e3fec17 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 10:31:51 +0000 Subject: [PATCH 022/196] fix --- package-lock.json | 241 ++++++++++++---------------------------------- 1 file changed, 59 insertions(+), 182 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84cac9a..00cf211 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,9 +20,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.0.tgz", - "integrity": "sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.8.0", @@ -609,9 +609,9 @@ "license": "BSD-3-Clause" }, "node_modules/@pulumi/github": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/@pulumi/github/-/github-6.7.3.tgz", - "integrity": "sha512-uqXvZba3FIc5ZSZFk6G5fMtNvtjQAOP+pg8hXFx8VAd01eCXqquY6xgB5HlR7xhHclGanAjtyaWOyvTrHDP3kQ==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@pulumi/github/-/github-6.9.1.tgz", + "integrity": "sha512-B7FcceruXfMClws4poIyUfJoy2dqcdhi8P5+ahfArIeaDjFDlcRNU9SZbfFRbkdoXflMJi+AI3rWabyvqS2+YQ==", "license": "Apache-2.0", "dependencies": { "@pulumi/pulumi": "^3.142.0" @@ -622,9 +622,9 @@ "link": true }, "node_modules/@pulumi/pulumi": { - "version": "3.197.0", - "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.197.0.tgz", - "integrity": "sha512-Pfjg6sluGDw11crvmI8wbi5k4YDBTxZBM1aI7h5Gd2a4DUyVhrSMUE2fFd1XN05wQKEDisahsygBbBZO0lg2zw==", + "version": "3.212.0", + "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.212.0.tgz", + "integrity": "sha512-UXV6UQLS2elP0yQNWCQWKjY+dc8w0TXC9uJLIiybzEpFyeKdPhuA0zJrI1zOql5Y7V9q5xtF2sqmHh52HLJVKg==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.10.1", @@ -645,11 +645,11 @@ "google-protobuf": "^3.21.4", "got": "^11.8.6", "ini": "^2.0.0", - "js-yaml": "^3.14.0", + "js-yaml": "^3.14.2", "minimist": "^1.2.6", "normalize-package-data": "^6.0.0", + "package-directory": "^8.1.0", "picomatch": "^3.0.1", - "pkg-dir": "^7.0.0", "require-from-string": "^2.0.1", "semver": "^7.5.2", "source-map-support": "^0.5.6", @@ -826,9 +826,9 @@ } }, "node_modules/@types/node": { - "version": "22.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.6.tgz", - "integrity": "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==", + "version": "22.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz", + "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -946,15 +946,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1404,9 +1395,9 @@ } }, "node_modules/exponential-backoff": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", - "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "license": "Apache-2.0" }, "node_modules/fdir": { @@ -1426,17 +1417,13 @@ } } }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "license": "MIT", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1513,9 +1500,9 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -1673,9 +1660,9 @@ } }, "node_modules/import-in-the-middle": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.2.tgz", - "integrity": "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", + "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", "license": "Apache-2.0", "dependencies": { "acorn": "^8.14.0", @@ -1712,9 +1699,9 @@ } }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "license": "MIT", "engines": { "node": ">= 12" @@ -1787,9 +1774,9 @@ } }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -1853,21 +1840,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -2338,46 +2310,31 @@ "node": ">=8" } }, - "node_modules/p-limit": { + "node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "license": "MIT", "dependencies": { - "p-limit": "^4.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/package-directory": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/package-directory/-/package-directory-8.1.0.tgz", + "integrity": "sha512-qHKRW0pw3lYdZMQVkjDBqh8HlamH/LCww2PH7OWEp4Qrt3SFeYMNpnJrQzlSnGrDD5zGR51XqBh7FnNCdVNEHA==", "license": "MIT", "dependencies": { - "aggregate-error": "^3.0.0" + "find-up-simple": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2434,15 +2391,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2486,21 +2434,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "license": "MIT", - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/postcss-selector-parser": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", @@ -2515,9 +2448,9 @@ } }, "node_modules/prettier": { - "version": "3.7.2", - "resolved": "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/prettier/-/prettier-3.7.2.tgz", - "integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "bin": { @@ -2686,12 +2619,12 @@ } }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -2740,9 +2673,9 @@ "optional": true }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3093,12 +3026,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, "node_modules/tuf-js": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", @@ -3114,9 +3041,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -3418,56 +3345,6 @@ "node": ">=8" } }, - "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "sdks/googleworkspace": { - "name": "@pulumi/googleworkspace", - "version": "0.7.0", - "hasInstallScript": true, - "dependencies": { - "@pulumi/pulumi": "^3.142.0", - "@types/node": "^18", - "async-mutex": "^0.5.0", - "typescript": "^4.3.5" - } - }, - "sdks/googleworkspace/node_modules/@types/node": { - "version": "18.19.127", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.127.tgz", - "integrity": "sha512-gSjxjrnKXML/yo0BO099uPixMqfpJU0TKYjpfLU7TrtA2WWDki412Np/RSTPRil1saKBhvVVKzVx/p/6p94nVA==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "sdks/googleworkspace/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "sdks/googleworkspace/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - } + "sdks/googleworkspace": {} } } From dd446c3dab9990a6e0c1a2877e6ab29e399e19db Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 17:40:00 +0000 Subject: [PATCH 023/196] Use GitHub secret for Pulumi GitHub token instead of encrypted config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move GitHub token from Pulumi encrypted config to GitHub Actions secret (PULUMI_GITHUB_TOKEN) for better secret management. The token is passed via GITHUB_TOKEN env var which the Pulumi GitHub provider reads automatically. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/deploy.yml | 1 + Pulumi.prod.yaml | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d0b7a64..951800f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -51,6 +51,7 @@ jobs: - name: Deploy to Production env: PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} + GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} run: | echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt make up \ No newline at end of file diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml index 9e4243b..eb4ad25 100644 --- a/Pulumi.prod.yaml +++ b/Pulumi.prod.yaml @@ -4,5 +4,3 @@ config: googleworkspace:credentials: secure: v1:Ntr2su0kgwp0oAh3:av1h5ir44/HNmtOQBftB6AD/0N7r+nLtIuPNnM47Xgyo71xO0GiV6oG81fT7dDhaxOwbBMWiEiOP+8s3XRiTJoAQHlqybvyHsgiprELsQfAPo2srnSDt289ggi11zPttYb/iNgOvoKwFxCfrapPv3Gnh0akbl3AgsJafs0uNToiNWmV176GaepR2JHEc7dpjIzpS6CFS8lVVl262ROInU85n1mEtaQ9eYZ69LPJn+ZQLSHAdMRROP1hv49Q26iqh4icJGmqbQQ6XGh4e5E0TgHT+/UPbuAzKnearRN+jFdqaJPQneXav9xy5W9zS4834LGa6Otawa12r5DlSIB5LunYCVDENhXcDhouwFHc7OK6bVUe2iY+557YfESZcznuYuHOq0ChPyKKJ7n5nE0Pu34nEJWMR3BARCLcGMuPIQALiecFMKDJjbVJN0ShS0nch4dDJpWH4KHNqyCs2q9h/Mume9pSu4PhZXg28VcBILh4a9B0JF2BzwqFy79OP0gmoz/B7IoIcjN28HnHrYmnK/97xrbTV7YzI2x5iYY9xwW3IlaKIrzr8f6vBAXTZmSFTOAqVWdsGgzalpqljDIqwJbh2YVQAy8NPLhfSxlkMJNPxfO0DR9AcL/kITQPL/f5WkRGU9+aur2K7gp2vkXUoObx8elnogIHpklPHRmb0obqoR8LTCD97Vt/rLL+ZaPgp2eBWwWydz1XAo78PVq53qGGbXk5lrv+i4qUxiUy8639ea4sdRJf2yGNWJb14hrpf5PBn7+bn2mSMdyY2jQIVbE0ltFYUVfPrzUNG51O39EwD9iFf/aR0lyxgO+fF8mMWOOTZleCPBMDY1t6c5u3TlipuSZF79q2LbuTnXGA8XmONiqoGyKDVIaYTNpOllFtuL105scGledqj9S2Q5bbgdRldlFXbFbGXWAYtj4A1z7gBRklb9UMJXbMyECRtfepbLu93XgKZaKjfXdRZkNtVNOjrE9KsGEUp0dkLUToKzj9tdDGxninCEUanhEV5ACTuXxGNqcvYoegYVRnGuLfCZTVMWwIezombduQDh7mEKkBRyw+XqQeRjCGHm+tZaAeo5p82cGX2bcxR2KselYfCVzP3n/yJsqp/D+STYd3bqq5Kno1f2obys2pyGhDri+scaZRZiWF7EysuYEuMC1D6EPkIo9ZEDfhgBU5RbJqC8h8DTTTLzsJa8r25L7qph/E7wVQywSLlL6e6GQ9bbepSikrNxXvftzkmEDNruN+5dVFznOEL5rTRa8Q98Dwet3i5c2PzWCzhz9/24rE0vrEOPO6NNgQ+du53wfG5uwkXEjAC4+w8nbXUrqe64y6o6AnRlo8YBwECV+qNfdpl0+6/JTP83L6aWo+u+QIiq4qpTF95SBNBt//SXk1KkYYgYNTo5Y/thjQ4ycHakHO4Mu/6Gz3+20uIOcSyhvO30Pk65scNcsTPHy1cT61cdK77fipJynVkn6rLKSWvqXMEeP6LwnioLx5WChyNAf+oubFMzlgS8zLW+kH8NxR4BIBpwbuyy+hpXXKZKNYKJtCzSUdBuV3PNZ0b0BaIEr6CyeqPtTPCfAoCxpvXipYJUEWLe6yPdorBQU0AYp3jMblrmqz/eAAPkiI5c7gTF6FAk0ezjzhvY3nVKPIvNi8Zj/5oxlt7p/kGjtUbMaQmGDrZco6TfDf0AtdERIDCUWnplKqLuz6NjIbZ7acAeyrBKyVslKNDn6tRZGk/4XrUFYgIEdWShOLZxAxI7o564n9+TycSIkz+LWYltQtRM1v5lnwNHSWXaXp7NUbhwWA5FlWPKBDOznhM0d6R5CRGfmtxE0KMQ3wac6QiODL3Zh5TJeKyFvXTh9hpSVfCafCMDtPWjzYj0X5G/DFCyNhIOf8Z8jfMBf2kh0AWI8ojZNS3c8ip1+elzLcGLDVfkLGF7rwvVDYXL/2cfamtfwR+CrljXlzT+FPRaMER8plhj6hN27UCWjILXVE7UqInNz/nkTS/YsZtVRaafCEXO1ytyV0gkXbKk00WFN6Qg3cK8HB0ITTYns8+NhstRTXewj9VJv/Kl7GV9Uz/DQX4KZW9rlJNd3XZjPYJRsHeRNXCnezTydyLpjrLYipbpYnuQcqBhE7S3nBiM+fTFrm8XJr7EBf4uIm8/9PSG52PMUiSsLHt+qMg36z+pliavLU5GXUw8RLONP2EnjI14vu8rz8BqlXhUuluWYRKeVNXprIG6pAkahqxhTmqwqYu1VIPDTPfuWJfQ9RCz5BYQEN4flTkuqGj0yYdl9CRxuL+m1BbPcZLufx+gY0gC5buiskzalfek3QhQLu2kH9P4zGuK/03jRVYlPf4IL0rt4l/e5iMV3bKB27Ds6W2An4NQB13WaujyINDiicJcRCyIQwsJt9aiLxf+NKc7N3iCLgjVw85K63WcDcW+zh8YOc89mJj/y3SOfTDAVCUjEzye9TldX8/7biavhsn4Bwdd71VkzxS/eSAJHeTXjw94FveP1d/A86LMBl/PThVEJmQMBZFjiMVw8x8xWG78NfqZDR2QeMU8meBhXrj4gwb3s2H9+H/6HcxoPB4O5kGbY4piQ3yu9MzPojVpQspdT2tgH+CAaNg3qHy3R/HHuWLxuXVGZj+2t3NoGvHrQRiS5e/9IwkQ2EA/3b20ZsBqP9F63oApr+PVnZYMbtRjJLdl02tIsMm4n5xoOI+0yP/93XSwD/rM+YXYu2PLiQRm5IkbDVu8Hg15OzIHSiGkTyPZb2/QBja10O9M/suKF7VZmS74skWZH3ETCNWyA5TpNwltafvou/1WtipHqNs45SXKJMr9iT3yI2jr/KGXlBDIih7mTiY5KN9mioAH1F5ROe0siEzBdaQe9soQsOBfFIZYzMBN/IKjuiqohG2DdDfsZqyYn0zO4GrqQ6Afv0fDY2fJJfbXCbn9i8saEI8CcSI2c2WTIxgQk+q6iOBBr0X/7WGzH7lmAHd4sDoIIi8F1QJyNlFsWj3r+5oe64jQa2XAkgVF3I6JENWpgSz6AJu7zQgnbg3ZIfF0kkwt+jRxEXsuLwdDO0XSxKBqv7Ynk0xDsV+5BkHzLv70fXh6BM8+LcK7kI8dgy8zJMLtctmAd9LmWcs7uWw8Di/EnZHZR5pDju0a+miq15yvHiJVjN6k5SoFRCVOQ== github:owner: modelcontextprotocol - github:token: - secure: v1:RKjKUcq1e0V7kVNf:5PP+vWbnRzgX8MmhF/yp52Fh78cTMBYz798JNc9LiyrubSt4AAZH/y/UA6kErP0FmXfFLRpdDIMmrR3RiNQlCe7nbkB6hf0CsV8mg8U7oB0EkA5wnRlqrZaKBri5l5puy2ZBFWjLhHr6oC714A== From 609880c57ffc217dbd5b6e2eed86765fd2a4b547 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 17:46:16 +0000 Subject: [PATCH 024/196] Add one-time migration workflow to set GitHub token and clean up state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add migrate-state.yml workflow (manual trigger) that: - Sets github:token in Pulumi config from PULUMI_GITHUB_TOKEN secret - Cleans up orphaned 'core' team memberships from state - Commits the updated Pulumi.prod.yaml back to repo - Revert deploy.yml to use encrypted token from Pulumi config 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/deploy.yml | 1 - .github/workflows/migrate-state.yml | 78 +++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/migrate-state.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 951800f..d0b7a64 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -51,7 +51,6 @@ jobs: - name: Deploy to Production env: PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} - GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} run: | echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt make up \ No newline at end of file diff --git a/.github/workflows/migrate-state.yml b/.github/workflows/migrate-state.yml new file mode 100644 index 0000000..90d7f34 --- /dev/null +++ b/.github/workflows/migrate-state.yml @@ -0,0 +1,78 @@ +name: Migrate State (One-Time) + +on: + workflow_dispatch: + +permissions: + contents: write + +env: + PULUMI_VERSION: "3.197.0" + +jobs: + migrate: + name: Migrate Pulumi State + runs-on: ubuntu-latest + environment: production + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Setup Pulumi + uses: pulumi/actions@v6 + with: + pulumi-version: ${{ env.PULUMI_VERSION }} + + - name: Install Pulumi packages + run: pulumi install + + - name: Install dependencies + run: npm ci + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_PROD_SERVICE_ACCOUNT_KEY }} + + - name: Migrate State + env: + PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} + PULUMI_GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} + run: | + echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt + export PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt + + # Login to state backend + pulumi login gs://mcp-access-prod-pulumi-state + + # Set the GitHub token in Pulumi config (will be encrypted) + pulumi config set --secret --stack prod github:token "$PULUMI_GITHUB_TOKEN" + + # Clean up orphaned core team memberships from state + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maxisbey-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::bhosmer-ant-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::crondinini-ant-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ochafik-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ihrpr-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::jerome3o-anthropic-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::felixweinberger-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maheshmurag-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::domdomegg-core' --yes || true + pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/team:Team::core' --yes || true + + echo "State cleanup complete!" + + - name: Commit updated Pulumi config + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Pulumi.prod.yaml + git commit -m "Update encrypted GitHub token in Pulumi config" || echo "No changes to commit" + git push From c47f7692c37005794ed16248a7e1ad9706bc828e Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 17:48:40 +0000 Subject: [PATCH 025/196] Use GITHUB_TOKEN env var for Pulumi GitHub provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the token via GITHUB_TOKEN environment variable which the Pulumi GitHub provider reads automatically. Remove the one-time migration workflow since state cleanup already succeeded. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/deploy.yml | 1 + .github/workflows/migrate-state.yml | 78 ----------------------------- 2 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 .github/workflows/migrate-state.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d0b7a64..951800f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -51,6 +51,7 @@ jobs: - name: Deploy to Production env: PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} + GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} run: | echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt make up \ No newline at end of file diff --git a/.github/workflows/migrate-state.yml b/.github/workflows/migrate-state.yml deleted file mode 100644 index 90d7f34..0000000 --- a/.github/workflows/migrate-state.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Migrate State (One-Time) - -on: - workflow_dispatch: - -permissions: - contents: write - -env: - PULUMI_VERSION: "3.197.0" - -jobs: - migrate: - name: Migrate Pulumi State - runs-on: ubuntu-latest - environment: production - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' - - - name: Setup Pulumi - uses: pulumi/actions@v6 - with: - pulumi-version: ${{ env.PULUMI_VERSION }} - - - name: Install Pulumi packages - run: pulumi install - - - name: Install dependencies - run: npm ci - - - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 - with: - credentials_json: ${{ secrets.GCP_PROD_SERVICE_ACCOUNT_KEY }} - - - name: Migrate State - env: - PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} - PULUMI_GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} - run: | - echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt - export PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt - - # Login to state backend - pulumi login gs://mcp-access-prod-pulumi-state - - # Set the GitHub token in Pulumi config (will be encrypted) - pulumi config set --secret --stack prod github:token "$PULUMI_GITHUB_TOKEN" - - # Clean up orphaned core team memberships from state - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maxisbey-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::bhosmer-ant-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::crondinini-ant-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ochafik-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ihrpr-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::jerome3o-anthropic-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::felixweinberger-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maheshmurag-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::domdomegg-core' --yes || true - pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/team:Team::core' --yes || true - - echo "State cleanup complete!" - - - name: Commit updated Pulumi config - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add Pulumi.prod.yaml - git commit -m "Update encrypted GitHub token in Pulumi config" || echo "No changes to commit" - git push From 27f046e66dbfb6082092acbb7954e3e22d48d99f Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 17:50:48 +0000 Subject: [PATCH 026/196] Add state cleanup before deploy to remove orphaned core team MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'core' team was deleted from GitHub but still exists in Pulumi state. Add cleanup-state target that runs before deploy to remove these orphaned resources. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Makefile | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4e9674b..52e8e68 100644 --- a/Makefile +++ b/Makefile @@ -12,5 +12,20 @@ login: ## Login to Pulumi backend (GCS) preview: login ## Preview infrastructure changes PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi preview --stack prod -up: login ## Deploy infrastructure +cleanup-state: login ## Remove orphaned resources from state + @echo "Cleaning up orphaned state entries..." + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maxisbey-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::bhosmer-ant-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::crondinini-ant-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ochafik-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ihrpr-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::jerome3o-anthropic-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::felixweinberger-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maheshmurag-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::domdomegg-core' --yes 2>/dev/null + -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/team:Team::core' --yes 2>/dev/null + @echo "State cleanup complete" + +up: login cleanup-state ## Deploy infrastructure PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack prod \ No newline at end of file From 22972bf8855a6f6c0444be0ac05475612779d783 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 17:54:54 +0000 Subject: [PATCH 027/196] Add bolinfest to rust-sdk group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/config/users.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index da7a07c..36a5fa1 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -61,6 +61,10 @@ export const MEMBERS: readonly Member[] = [ github: 'bhosmer-ant', memberOf: ['core-maintainers', 'docs-maintaners', 'moderators', 'python-sdk', 'typescript-sdk'], }, + { + github: 'bolinfest', + memberOf: ['rust-sdk'], + }, { github: 'carlpeaslee', memberOf: ['swift-sdk'], From fbc185181fffb02c310c1237aaf30b1513a3ed3c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 13 Dec 2025 17:57:16 +0000 Subject: [PATCH 028/196] Remove bolinfest from rust-sdk group, keep direct push access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bolinfest should have push access to rust-sdk, not admin. Direct access is already configured in repoAccess.ts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/config/users.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 36a5fa1..da7a07c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -61,10 +61,6 @@ export const MEMBERS: readonly Member[] = [ github: 'bhosmer-ant', memberOf: ['core-maintainers', 'docs-maintaners', 'moderators', 'python-sdk', 'typescript-sdk'], }, - { - github: 'bolinfest', - memberOf: ['rust-sdk'], - }, { github: 'carlpeaslee', memberOf: ['swift-sdk'], From d12eeca0022945fa7fd48f0585a80fabd224d543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ate=C5=9F=20G=C3=B6ral?= Date: Tue, 6 Jan 2026 16:21:35 -0500 Subject: [PATCH 029/196] Add jonathanhefner to ruby-sdk (#20) --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index da7a07c..2a24c0b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -173,7 +173,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'jonathanhefner', - memberOf: ['docs-maintaners', 'moderators'], + memberOf: ['docs-maintaners', 'moderators', 'ruby-sdk'], }, { github: 'jspahrsummers', From 6aa2a38d11bf8d83041ad79796d68be9b3fc1e6f Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 13 Jan 2026 10:50:18 +0100 Subject: [PATCH 030/196] add kurtis and caitie to core maintainers as per decision of core maintainers --- src/config/users.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 2a24c0b..befa277 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -194,7 +194,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'kurtisvg', - memberOf: ['transport-wg'], + memberOf: ['core-maintainers', 'transport-wg'], }, { github: 'localden', @@ -338,4 +338,8 @@ export const MEMBERS: readonly Member[] = [ email: 'davideramian@anthropic.com', memberOf: ['antitrust'], }, + { + github: 'caitiem20', + memberOf: ['core-maintainers'], + }, ] as const; From 0c010f3fcd23e43638351fe8001c1f7a4293bf6e Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 13 Jan 2026 10:50:42 +0100 Subject: [PATCH 031/196] remove inna from core maintainers as per her own wish --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index befa277..9a702f0 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -149,7 +149,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ihrpr', - memberOf: ['core-maintainers', 'docs-maintaners', 'python-sdk', 'typescript-sdk'], + memberOf: ['docs-maintaners', 'python-sdk', 'typescript-sdk'], }, { github: 'jamadeo', From 7281f0dc21c1dd00cdb020f3392910ab6126105d Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Tue, 13 Jan 2026 04:22:41 -0700 Subject: [PATCH 032/196] Add a-akimov to docs-maintaners, tighten inspector/servers permissions (#19) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add evalstate and cliffhall to docs-maintaners team 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Add a-akimov to docs-maintaners team, tighten inspector/servers permissions - Add a-akimov to docs-maintaners team (replaces direct repo access) - Remove a-akimov direct push from quickstart-resources (now via team) - Remove interest groups from inspector push access (ig-financial-services, interest-groups) - Remove working-groups umbrella from inspector push access - Remove docs-maintaners from inspector push access - Remove interest groups from servers push access (ig-financial-services, interest-groups) - Remove working-groups umbrella from servers push access 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude --- src/config/repoAccess.ts | 12 +----------- src/config/users.ts | 4 ++++ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 937a0f3..09ef936 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -87,10 +87,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, - { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, - { team: 'ig-financial-services', permission: 'push' }, - { team: 'interest-groups', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, { team: 'moderators', permission: 'push' }, @@ -107,7 +104,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'transport-wg', permission: 'push' }, { team: 'typescript-sdk', permission: 'push' }, { team: 'typescript-sdk-auth', permission: 'push' }, - { team: 'working-groups', permission: 'push' }, ], }, { @@ -142,10 +138,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'quickstart-resources', - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - { username: 'a-akimov', permission: 'push' }, - ], + users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -185,8 +178,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintaners', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, - { team: 'ig-financial-services', permission: 'push' }, - { team: 'interest-groups', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, { team: 'moderators', permission: 'push' }, @@ -203,7 +194,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'transport-wg', permission: 'push' }, { team: 'typescript-sdk', permission: 'push' }, { team: 'typescript-sdk-auth', permission: 'push' }, - { team: 'working-groups', permission: 'push' }, ], }, { diff --git a/src/config/users.ts b/src/config/users.ts index 9a702f0..88f229c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -29,6 +29,10 @@ export const MEMBERS: readonly Member[] = [ github: 'Ololoshechkin', memberOf: ['kotlin-sdk'], }, + { + github: 'a-akimov', + memberOf: ['docs-maintaners'], + }, { github: 'aaronpk', memberOf: ['auth-wg'], From f0336a9991229cb1119fa6b6609319b5388cc496 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Tue, 13 Jan 2026 14:25:30 +0000 Subject: [PATCH 033/196] Add movetz and stallent as Swift SDK maintainers (#21) * Add movetz and stallent as Swift SDK maintainers Adding Maksym Mova (MacPaw) and Stephen Tallent (Mercury.io) as new maintainers for the Swift SDK, as discussed in the SDK working group. * Set up tiered access: admins + contributor team - movetz and stallent get admin access directly - swift-sdk team grants push access (for future contributors) This follows the pattern used by go-sdk and rust-sdk. * Preserve mattt as admin --- src/config/repoAccess.ts | 7 ++++++- src/config/users.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 09ef936..054429f 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -254,7 +254,12 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'swift-sdk', - teams: [{ team: 'swift-sdk', permission: 'admin' }], + teams: [{ team: 'swift-sdk', permission: 'push' }], + users: [ + { username: 'mattt', permission: 'admin' }, + { username: 'movetz', permission: 'admin' }, + { username: 'stallent', permission: 'admin' }, + ], }, { repository: 'typescript-sdk', diff --git a/src/config/users.ts b/src/config/users.ts index 88f229c..169725a 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -232,6 +232,10 @@ export const MEMBERS: readonly Member[] = [ github: 'michaelneale', memberOf: ['rust-sdk'], }, + { + github: 'movetz', + memberOf: ['swift-sdk'], + }, { github: 'mikekistler', memberOf: ['csharp-sdk'], @@ -304,6 +308,10 @@ export const MEMBERS: readonly Member[] = [ github: 'stephentoub', memberOf: ['csharp-sdk'], }, + { + github: 'stallent', + memberOf: ['swift-sdk'], + }, { github: 'tadasant', email: 'tadas@modelcontextprotocol.io', From f960494174f6a78f2d1cd74f23e66e7b27be5a17 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Tue, 13 Jan 2026 22:03:59 +0000 Subject: [PATCH 034/196] Add Discord role sync with role-centric architecture (#22) * Add Discord role sync with role-centric architecture This refactors the access management to use a role-centric model where each role explicitly defines which platforms it exists on (GitHub, Discord, Google). Key changes: - Add Discord role sync via Pulumi Dynamic Provider - Replace groups.ts with roleIds.ts + roles.ts for type-safe role definitions - Use ROLE_IDS constants to prevent typos in memberOf references - Each role has platform-specific config (github/discord/google keys) - Discord-only roles (e.g., administrators) and GitHub-only roles (e.g., typescript-sdk-auth) are now explicitly modeled - Add PR preview workflow to show Pulumi changes before merge - Add test-config.ts for config structure validation - Fix typo: docs-maintaners -> docs-maintainers Discord roles will match existing server roles: - administrators, lead maintainers, core maintainers - sdk maintainers, working groups, interest groups - etc. Requires new GitHub secrets: - DISCORD_BOT_TOKEN - DISCORD_GUILD_ID * Fix GitHub team ordering and make Discord integration optional - Add topological sort for GitHub teams to ensure parent teams are created before child teams, fixing the registry-wg parentTeamId issue - Make Discord integration optional by using config.get() instead of config.require(), allowing CI preview to succeed without Discord secrets - Add sortRolesByGitHubDependency() utility function in config/utils.ts Claude-Generated-By: Claude Code (cli/claude-opus-4-5=20%) Claude-Steers: 2 Claude-Permission-Prompts: 1 Claude-Escapes: 0 * Reconcile Discord roles for listed users Replace per-role-membership approach with per-user role sync that: - Adds missing roles users should have - Removes managed roles users shouldn't have - Only touches roles defined in config (won't affect admin roles, etc.) Users not in config remain untouched. Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 4 Claude-Permission-Prompts: 1 Claude-Escapes: 0 * Fix descriptions to match existing resources * Cache Pulumi plugins and fix GITHUB_TOKEN for pulumi install * Remove accidentally committed state backup * Use github.token for pulumi install * Fix formatting Fix deploy: add pulumi login before config set Fix deploy: set PULUMI_CONFIG_PASSPHRASE_FILE for config commands Import existing docs-maintainers team (remove after deploy) Remove docs-maintainers import workaround Add docs-maintainers cleanup to state cleanup Add pulumi refresh before up to sync state with reality Rename Discord roles to add (synced) suffix Cleanup Makefile and add lead-maintainers GitHub team --- .github/workflows/ci.yml | 10 + .github/workflows/deploy.yml | 16 ++ .github/workflows/preview.yml | 153 +++++++++++++ Makefile | 22 +- package.json | 2 + scripts/test-config.ts | 87 +++++++ scripts/validate-config.ts | 69 +++--- src/config/groups.ts | 187 --------------- src/config/repoAccess.ts | 10 +- src/config/roleIds.ts | 75 ++++++ src/config/roles.ts | 316 +++++++++++++++++++++++++ src/config/users.ts | 207 +++++++++-------- src/config/utils.ts | 96 +++++--- src/discord.ts | 418 ++++++++++++++++++++++++++++++++++ src/github.ts | 49 ++-- src/google.ts | 50 ++-- src/index.ts | 1 + 17 files changed, 1354 insertions(+), 414 deletions(-) create mode 100644 .github/workflows/preview.yml create mode 100644 scripts/test-config.ts delete mode 100644 src/config/groups.ts create mode 100644 src/config/roleIds.ts create mode 100644 src/config/roles.ts create mode 100644 src/discord.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcbd3db..92415c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,17 @@ jobs: with: pulumi-version: '3.197.0' + - name: Cache Pulumi plugins + uses: actions/cache@v4 + with: + path: ~/.pulumi/plugins + key: pulumi-plugins-${{ hashFiles('Pulumi.yaml') }} + restore-keys: | + pulumi-plugins- + - name: Install Pulumi packages + env: + GITHUB_TOKEN: ${{ github.token }} run: pulumi install - name: Install dependencies diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 951800f..ae2b7c8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -34,7 +34,17 @@ jobs: with: pulumi-version: ${{ env.PULUMI_VERSION }} + - name: Cache Pulumi plugins + uses: actions/cache@v4 + with: + path: ~/.pulumi/plugins + key: pulumi-plugins-${{ hashFiles('Pulumi.yaml') }} + restore-keys: | + pulumi-plugins- + - name: Install Pulumi packages + env: + GITHUB_TOKEN: ${{ github.token }} run: pulumi install - name: Install dependencies @@ -52,6 +62,12 @@ jobs: env: PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} + DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} + DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }} run: | echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt + export PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt + pulumi login gs://mcp-access-prod-pulumi-state + pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod + pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod make up \ No newline at end of file diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..8f5c812 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,153 @@ +name: Preview + +on: + pull_request: + branches: + - main + +permissions: + contents: read + pull-requests: write + +env: + PULUMI_VERSION: "3.197.0" + +jobs: + preview: + name: Preview Changes + runs-on: ubuntu-latest + environment: production + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Setup Pulumi + uses: pulumi/actions@v6 + with: + pulumi-version: ${{ env.PULUMI_VERSION }} + + - name: Cache Pulumi plugins + uses: actions/cache@v4 + with: + path: ~/.pulumi/plugins + key: pulumi-plugins-${{ hashFiles('Pulumi.yaml') }} + restore-keys: | + pulumi-plugins- + + - name: Install Pulumi packages + env: + GITHUB_TOKEN: ${{ github.token }} + run: pulumi install + + - name: Install dependencies + run: npm ci + + - name: Run validation + run: npm run check + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_PROD_SERVICE_ACCOUNT_KEY }} + + - name: Preview changes + id: preview + env: + PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} + GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} + DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} + DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }} + run: | + echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt + pulumi login gs://mcp-access-prod-pulumi-state + + # Build config flags for Discord if secrets are available + CONFIG_FLAGS="" + if [ -n "$DISCORD_GUILD_ID" ]; then + CONFIG_FLAGS="$CONFIG_FLAGS --config discord:guildId=$DISCORD_GUILD_ID" + fi + if [ -n "$DISCORD_BOT_TOKEN" ]; then + CONFIG_FLAGS="$CONFIG_FLAGS --config discord:botToken=$DISCORD_BOT_TOKEN" + fi + + # Run preview and capture output + set +e + PREVIEW_OUTPUT=$(PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi preview --stack prod --diff $CONFIG_FLAGS 2>&1) + PREVIEW_EXIT_CODE=$? + set -e + + # Save output for comment + echo "exit_code=$PREVIEW_EXIT_CODE" >> $GITHUB_OUTPUT + + # Write preview to file (handles multiline) + echo "$PREVIEW_OUTPUT" > preview_output.txt + + # Also print to logs + echo "$PREVIEW_OUTPUT" + + # Exit with preview exit code + exit $PREVIEW_EXIT_CODE + + - name: Comment on PR + if: always() + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + let output = ''; + try { + output = fs.readFileSync('preview_output.txt', 'utf8'); + } catch (e) { + output = 'Failed to read preview output'; + } + + // Truncate if too long for GitHub comment + const maxLength = 60000; + if (output.length > maxLength) { + output = output.substring(0, maxLength) + '\n\n... (truncated)'; + } + + const body = `## Pulumi Preview + +
+ Click to expand preview output + + \`\`\` + ${output} + \`\`\` + +
+ `; + + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => + c.user.type === 'Bot' && c.body.includes('## Pulumi Preview') + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + } diff --git a/Makefile b/Makefile index 52e8e68..0f7d48f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help login preview up +.PHONY: help login preview refresh up # Default target help: ## Show this help message @@ -12,20 +12,8 @@ login: ## Login to Pulumi backend (GCS) preview: login ## Preview infrastructure changes PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi preview --stack prod -cleanup-state: login ## Remove orphaned resources from state - @echo "Cleaning up orphaned state entries..." - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maxisbey-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::bhosmer-ant-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::crondinini-ant-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ochafik-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::ihrpr-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::jerome3o-anthropic-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::felixweinberger-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::maheshmurag-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::domdomegg-core' --yes 2>/dev/null - -PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi state delete --stack prod 'urn:pulumi:prod::mcp-access::github:index/team:Team::core' --yes 2>/dev/null - @echo "State cleanup complete" +refresh: login ## Refresh state to match reality + PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi refresh --yes --stack prod -up: login cleanup-state ## Deploy infrastructure - PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack prod \ No newline at end of file +up: login refresh ## Deploy infrastructure + PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack prod diff --git a/package.json b/package.json index 59c9dd7..e23b708 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,8 @@ "scripts": { "build": "tsc", "validate": "npx ts-node scripts/validate-config.ts", + "test": "npx ts-node scripts/test-config.ts", + "check": "npm run validate && npm run test", "format": "prettier --write .", "format:check": "prettier --check ." }, diff --git a/scripts/test-config.ts b/scripts/test-config.ts new file mode 100644 index 0000000..471bbf4 --- /dev/null +++ b/scripts/test-config.ts @@ -0,0 +1,87 @@ +#!/usr/bin/env npx ts-node + +/** + * Tests the configuration structure without needing Pulumi credentials. + * Run with: npx ts-node scripts/test-config.ts + */ + +import { ROLES, buildRoleLookup, getRolesForPlatform } from '../src/config/roles'; +import { ROLE_IDS, isValidRoleId } from '../src/config/roleIds'; +import { MEMBERS } from '../src/config/users'; + +let passed = 0; +let failed = 0; + +function test(name: string, fn: () => boolean) { + try { + if (fn()) { + console.log(`✓ ${name}`); + passed++; + } else { + console.log(`✗ ${name}`); + failed++; + } + } catch (e) { + console.log(`✗ ${name}: ${e}`); + failed++; + } +} + +console.log('Testing role configuration...\n'); + +// Test ROLE_IDS +test('ROLE_IDS has entries', () => Object.keys(ROLE_IDS).length > 0); +test('All ROLE_IDS values are valid', () => + Object.values(ROLE_IDS).every((id) => isValidRoleId(id))); + +// Test ROLES +test('ROLES array is not empty', () => ROLES.length > 0); +test('All roles have id and description', () => ROLES.every((r) => r.id && r.description)); +test('All role IDs are unique', () => { + const ids = ROLES.map((r) => r.id); + return ids.length === new Set(ids).size; +}); + +// Test platform configs +const githubRoles = getRolesForPlatform('github'); +const discordRoles = getRolesForPlatform('discord'); +const googleRoles = getRolesForPlatform('google'); + +test('Has GitHub roles', () => githubRoles.length > 0); +test('Has Discord roles', () => discordRoles.length > 0); +test('Has Google roles', () => googleRoles.length > 0); + +test('GitHub roles have team names', () => githubRoles.every((r) => r.github?.team)); +test('Discord roles have role names', () => discordRoles.every((r) => r.discord?.role)); +test('Google roles have group names', () => googleRoles.every((r) => r.google?.group)); + +// Test parent relationships +const roleLookup = buildRoleLookup(); +test('All GitHub parent references are valid', () => + githubRoles.every((r) => { + if (!r.github?.parent) return true; + const parent = roleLookup.get(r.github.parent); + return parent && parent.github; + })); + +// Test members +test('MEMBERS array is not empty', () => MEMBERS.length > 0); +test('All members have at least one identifier', () => + MEMBERS.every((m) => m.github || m.email || m.discord)); +test('All member role references are valid', () => + MEMBERS.every((m) => m.memberOf.every((id) => roleLookup.has(id)))); + +// Test specific roles exist +test('CORE_MAINTAINERS role exists', () => !!roleLookup.get(ROLE_IDS.CORE_MAINTAINERS)); +test('ADMINISTRATORS role exists (Discord-only)', () => { + const role = roleLookup.get(ROLE_IDS.ADMINISTRATORS); + return role !== undefined && role.discord !== undefined && role.github === undefined; +}); +test('TYPESCRIPT_SDK_AUTH role exists (GitHub-only)', () => { + const role = roleLookup.get(ROLE_IDS.TYPESCRIPT_SDK_AUTH); + return role !== undefined && role.github !== undefined && role.discord === undefined; +}); + +// Summary +console.log(`\n${passed} passed, ${failed} failed`); +process.exit(failed > 0 ? 1 : 0); diff --git a/scripts/validate-config.ts b/scripts/validate-config.ts index d813246..59d8a78 100644 --- a/scripts/validate-config.ts +++ b/scripts/validate-config.ts @@ -1,27 +1,27 @@ #!/usr/bin/env npx ts-node /** - * Validates that all team references in repoAccess.ts exist in groups.ts + * Validates that all references in the config are valid. * Run with: npx ts-node scripts/validate-config.ts */ -import { GROUPS } from '../src/config/groups'; +import { ROLES, buildRoleLookup } from '../src/config/roles'; import { REPOSITORY_ACCESS } from '../src/config/repoAccess'; import { MEMBERS } from '../src/config/users'; -import type { Group, Member } from '../src/config/utils'; +import type { RoleId } from '../src/config/roleIds'; -// Get all GitHub team names (groups not limited to google-only platforms) +const roleLookup = buildRoleLookup(); + +// Get all GitHub team names (roles that have GitHub config) const githubTeamNames = new Set(); -// Get all group names (for member validation - members can be in any group) -const allGroupNames = new Set(); +// Get all role IDs (for member validation) +const allRoleIds = new Set(); -for (const g of GROUPS) { - const group = g as Group; - allGroupNames.add(group.name); +for (const role of ROLES) { + allRoleIds.add(role.id); - const platforms = group.onlyOnPlatforms; - if (!platforms || platforms.includes('github')) { - githubTeamNames.add(group.name); + if (role.github) { + githubTeamNames.add(role.github.team); } } @@ -35,41 +35,42 @@ for (const repo of REPOSITORY_ACCESS) { for (const teamRef of repo.teams) { if (!githubTeamNames.has(teamRef.team)) { console.error( - `ERROR: Repository "${repo.repository}" references team "${teamRef.team}" which does not exist in groups.ts` + `ERROR: Repository "${repo.repository}" references team "${teamRef.team}" which does not exist in roles.ts` ); hasErrors = true; } } } -// Validate team references in MEMBERS (memberOf) -// Members can be in any group (GitHub or Google-only) -console.log('Validating team references in users.ts...'); -for (const m of MEMBERS) { - const member = m as Member; - for (const teamKey of member.memberOf) { - if (!allGroupNames.has(teamKey)) { +// Validate role references in MEMBERS (memberOf) +console.log('Validating role references in users.ts...'); +for (const member of MEMBERS) { + for (const roleId of member.memberOf) { + if (!allRoleIds.has(roleId)) { console.error( - `ERROR: Member "${member.github || member.email}" references team "${teamKey}" which does not exist in groups.ts` + `ERROR: Member "${member.github || member.email}" references role "${roleId}" which does not exist in roles.ts` ); hasErrors = true; } } } -// Validate parent team references (memberOf in groups) -console.log('Validating parent team references in groups.ts...'); -for (const g of GROUPS) { - const group = g as Group; - if (!group.memberOf) continue; +// Validate parent role references in roles.ts +console.log('Validating parent role references in roles.ts...'); +for (const role of ROLES) { + if (!role.github?.parent) continue; - for (const parentTeam of group.memberOf) { - if (!allGroupNames.has(parentTeam)) { - console.error( - `ERROR: Group "${group.name}" has parent "${parentTeam}" which does not exist in groups.ts` - ); - hasErrors = true; - } + const parentRole = roleLookup.get(role.github.parent); + if (!parentRole) { + console.error( + `ERROR: Role "${role.id}" has parent "${role.github.parent}" which does not exist in roles.ts` + ); + hasErrors = true; + } else if (!parentRole.github) { + console.error( + `ERROR: Role "${role.id}" has parent "${role.github.parent}" which does not have GitHub config` + ); + hasErrors = true; } } @@ -77,6 +78,6 @@ if (hasErrors) { console.error('\nValidation failed! Please fix the errors above.'); process.exit(1); } else { - console.log('\nAll team references are valid.'); + console.log('\nAll references are valid.'); process.exit(0); } diff --git a/src/config/groups.ts b/src/config/groups.ts deleted file mode 100644 index 72c1552..0000000 --- a/src/config/groups.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { defineGroups } from './utils'; - -// NOTE: For GitHub teams, only the first memberOf will be used as the parent team. -// GitHub only supports one parent team per team. -// -// Email groups (isEmailGroup: true) accept emails from anyone (including external users) -// and notify group members for each email. -// -// Groups are created on all platforms (GitHub and Google) by default. -// To limit a group to specific platforms, set the onlyOnPlatforms array (e.g., onlyOnPlatforms: ['google']). -export const GROUPS = defineGroups([ - // MCP Organization Structure - { - name: 'steering-committee', - description: 'MCP Steering Committee', - onlyOnPlatforms: ['github'], - }, - { - name: 'core-maintainers', - description: 'Core maintainers', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'moderators', - description: 'Community moderators', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'docs-maintaners', - description: 'MCP docs maintainers', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'inspector-maintainers', - description: 'MCP Inspector maintainers', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'mcpb-maintainers', - description: 'MCPB (Model Context Protocol Bundle) maintainers', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - - // SDK Maintainers - { - name: 'sdk-maintainers', - description: 'Authors and maintainers of official MCP SDKs', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'csharp-sdk', - description: 'Official C# SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'go-sdk', - description: 'The Go SDK Team', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'java-sdk', - description: 'Official Java SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'kotlin-sdk', - description: 'Official Kotlin SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'php-sdk', - description: 'Official PHP SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'python-sdk', - description: 'Official Python SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'python-sdk-auth', - description: 'Auth related owners', - memberOf: ['python-sdk'], - onlyOnPlatforms: ['github'], - }, - { - name: 'ruby-sdk', - description: 'Official Ruby SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'rust-sdk', - description: 'Official Rust SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'swift-sdk', - description: 'Official Swift SDK maintainers', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'typescript-sdk', - description: 'Official TypeScript SDK', - memberOf: ['sdk-maintainers'], - onlyOnPlatforms: ['github'], - }, - { - name: 'typescript-sdk-auth', - description: 'Code owners for auth in Typescript SDK', - memberOf: ['typescript-sdk'], - onlyOnPlatforms: ['github'], - }, - - // Working Groups - { - name: 'working-groups', - description: 'MCP Working Groups', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'auth-wg', - description: 'Authentication Working Group', - memberOf: ['working-groups'], - onlyOnPlatforms: ['github'], - }, - { - name: 'registry-wg', - description: 'Official registry builders and maintainers', - memberOf: ['working-groups'], - }, - { - name: 'security-wg', - description: 'Security Working Group', - memberOf: ['working-groups'], - onlyOnPlatforms: ['github'], - }, - { - name: 'transport-wg', - description: 'Transport Working Group', - memberOf: ['working-groups'], - onlyOnPlatforms: ['github'], - }, - - // Interest Groups - { - name: 'interest-groups', - description: 'Interest Groups', - memberOf: ['steering-committee'], - onlyOnPlatforms: ['github'], - }, - { - name: 'ig-financial-services', - description: 'Financial Services Interest Group', - memberOf: ['interest-groups'], - onlyOnPlatforms: ['github'], - }, - - // Email-only groups - { - name: 'antitrust', - description: 'Antitrust compliance contacts', - isEmailGroup: true, - onlyOnPlatforms: ['google'], - }, - { - name: 'catch-all', - description: 'Catch-all email group', - isEmailGroup: true, - onlyOnPlatforms: ['google'], - }, -] as const); diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 054429f..3434d95 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -21,7 +21,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, - { team: 'docs-maintaners', permission: 'push' }, + { team: 'docs-maintainers', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, { team: 'ig-financial-services', permission: 'push' }, { team: 'interest-groups', permission: 'push' }, @@ -51,7 +51,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'auth-wg', permission: 'triage' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'triage' }, - { team: 'docs-maintaners', permission: 'triage' }, + { team: 'docs-maintainers', permission: 'triage' }, { team: 'go-sdk', permission: 'triage' }, { team: 'ig-financial-services', permission: 'triage' }, { team: 'interest-groups', permission: 'triage' }, @@ -113,7 +113,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'triage' }, - { team: 'docs-maintaners', permission: 'push' }, + { team: 'docs-maintainers', permission: 'push' }, { team: 'go-sdk', permission: 'triage' }, { team: 'ig-financial-services', permission: 'triage' }, { team: 'interest-groups', permission: 'triage' }, @@ -143,7 +143,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, - { team: 'docs-maintaners', permission: 'push' }, + { team: 'docs-maintainers', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, { team: 'ig-financial-services', permission: 'push' }, { team: 'interest-groups', permission: 'push' }, @@ -176,7 +176,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'auth-wg', permission: 'push' }, { team: 'core-maintainers', permission: 'admin' }, { team: 'csharp-sdk', permission: 'push' }, - { team: 'docs-maintaners', permission: 'push' }, + { team: 'docs-maintainers', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts new file mode 100644 index 0000000..2170c63 --- /dev/null +++ b/src/config/roleIds.ts @@ -0,0 +1,75 @@ +/** + * Role ID constants for type-safe role references. + * Using constants prevents typos and enables autocomplete. + */ +export const ROLE_IDS = { + // =================== + // Organization Structure + // =================== + STEERING_COMMITTEE: 'steering-committee', + CORE_MAINTAINERS: 'core-maintainers', + LEAD_MAINTAINERS: 'lead-maintainers', + MODERATORS: 'moderators', + ADMINISTRATORS: 'administrators', // Discord only + + // =================== + // Maintainer Groups + // =================== + MAINTAINERS: 'maintainers', + DOCS_MAINTAINERS: 'docs-maintainers', + INSPECTOR_MAINTAINERS: 'inspector-maintainers', + MCPB_MAINTAINERS: 'mcpb-maintainers', + REFERENCE_SERVERS_MAINTAINERS: 'reference-servers-maintainers', + REGISTRY_MAINTAINERS: 'registry-maintainers', + USE_MCP_MAINTAINERS: 'use-mcp-maintainers', + + // =================== + // SDK Maintainers + // =================== + SDK_MAINTAINERS: 'sdk-maintainers', + CSHARP_SDK: 'csharp-sdk', + GO_SDK: 'go-sdk', + JAVA_SDK: 'java-sdk', + KOTLIN_SDK: 'kotlin-sdk', + PHP_SDK: 'php-sdk', + PYTHON_SDK: 'python-sdk', + PYTHON_SDK_AUTH: 'python-sdk-auth', // GitHub only (CODEOWNERS) + RUBY_SDK: 'ruby-sdk', + RUST_SDK: 'rust-sdk', + SWIFT_SDK: 'swift-sdk', + TYPESCRIPT_SDK: 'typescript-sdk', + TYPESCRIPT_SDK_AUTH: 'typescript-sdk-auth', // GitHub only (CODEOWNERS) + + // =================== + // Working Groups + // =================== + WORKING_GROUPS: 'working-groups', + AUTH_WG: 'auth-wg', + SECURITY_WG: 'security-wg', + SERVER_IDENTITY_WG: 'server-identity-wg', + TRANSPORT_WG: 'transport-wg', + + // =================== + // Interest Groups + // =================== + INTEREST_GROUPS: 'interest-groups', + AGENTS_IG: 'agents-ig', + AUTH_IG: 'auth-ig', + CLIENT_IMPLEMENTOR_IG: 'client-implementor-ig', + FINANCIAL_SERVICES_IG: 'financial-services-ig', + + // =================== + // Email Groups (Google only) + // =================== + ANTITRUST: 'antitrust', + CATCH_ALL: 'catch-all', +} as const; + +export type RoleId = (typeof ROLE_IDS)[keyof typeof ROLE_IDS]; + +/** + * Helper to check if a string is a valid RoleId at runtime. + */ +export function isValidRoleId(id: string): id is RoleId { + return Object.values(ROLE_IDS).includes(id as RoleId); +} diff --git a/src/config/roles.ts b/src/config/roles.ts new file mode 100644 index 0000000..e328637 --- /dev/null +++ b/src/config/roles.ts @@ -0,0 +1,316 @@ +import { ROLE_IDS, type RoleId } from './roleIds'; + +/** + * GitHub team configuration + */ +export interface GitHubConfig { + /** Team name (usually matches role ID) */ + team: string; + /** Parent team role ID */ + parent?: RoleId; +} + +/** + * Discord role configuration + */ +export interface DiscordConfig { + /** Display name in Discord (can have spaces) */ + role: string; +} + +/** + * Google Workspace group configuration + */ +export interface GoogleConfig { + /** Group name (used as prefix for @modelcontextprotocol.io email) */ + group: string; + /** If true, accepts emails from anyone including external users */ + isEmailGroup?: boolean; +} + +/** + * Role definition with platform-specific configurations. + * A role only exists on platforms where it has a config key. + */ +export interface Role { + id: RoleId; + description: string; + github?: GitHubConfig; + discord?: DiscordConfig; + google?: GoogleConfig; +} + +/** + * All roles in the MCP organization. + * Each role specifies which platforms it exists on via presence of config keys. + */ +export const ROLES: readonly Role[] = [ + // =================== + // Organization Structure + // =================== + { + id: ROLE_IDS.STEERING_COMMITTEE, + description: 'MCP Steering Committee', + github: { team: 'steering-committee' }, + // No discord - this is a GitHub-only organizational container + }, + { + id: ROLE_IDS.ADMINISTRATORS, + description: 'Discord server administrators', + discord: { role: 'administrators (synced)' }, + // Discord only - no GitHub equivalent + }, + { + id: ROLE_IDS.LEAD_MAINTAINERS, + description: 'Lead core maintainers', + github: { team: 'lead-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, + discord: { role: 'lead maintainers (synced)' }, + // Discord only for now - could add GitHub if needed + }, + { + id: ROLE_IDS.CORE_MAINTAINERS, + description: 'Core maintainers', + github: { team: 'core-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, + discord: { role: 'core maintainers (synced)' }, + }, + { + id: ROLE_IDS.MODERATORS, + description: 'Community moderators', + github: { team: 'moderators', parent: ROLE_IDS.STEERING_COMMITTEE }, + discord: { role: 'community moderators (synced)' }, + }, + + // =================== + // Maintainer Groups + // =================== + { + id: ROLE_IDS.MAINTAINERS, + description: 'General maintainers', + discord: { role: 'maintainers (synced)' }, + // Discord only - general maintainer role + }, + { + id: ROLE_IDS.DOCS_MAINTAINERS, + description: 'MCP docs maintainers', + github: { team: 'docs-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, + // No discord role for docs maintainers + }, + { + id: ROLE_IDS.INSPECTOR_MAINTAINERS, + description: 'MCP Inspector maintainers', + github: { team: 'inspector-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, + discord: { role: 'inspector maintainers (synced)' }, + }, + { + id: ROLE_IDS.MCPB_MAINTAINERS, + description: 'MCPB (Model Context Protocol Bundle) maintainers', + github: { team: 'mcpb-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, + // No discord role + }, + { + id: ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, + description: 'Reference servers maintainers', + discord: { role: 'reference servers maintainers (synced)' }, + // Discord only for now + }, + { + id: ROLE_IDS.REGISTRY_MAINTAINERS, + description: 'Official registry builders and maintainers', + github: { team: 'registry-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'registry maintainers (synced)' }, + google: { group: 'registry-wg' }, + }, + { + id: ROLE_IDS.USE_MCP_MAINTAINERS, + description: 'use-mcp maintainers', + discord: { role: 'use-mcp maintainers (synced)' }, + // Discord only + }, + + // =================== + // SDK Maintainers + // =================== + { + id: ROLE_IDS.SDK_MAINTAINERS, + description: 'Authors and maintainers of official MCP SDKs', + github: { team: 'sdk-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, + discord: { role: 'sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.CSHARP_SDK, + description: 'Official C# SDK maintainers', + github: { team: 'csharp-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'c# sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.GO_SDK, + description: 'The Go SDK Team', + github: { team: 'go-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'go sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.JAVA_SDK, + description: 'Official Java SDK maintainers', + github: { team: 'java-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'java sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.KOTLIN_SDK, + description: 'Official Kotlin SDK maintainers', + github: { team: 'kotlin-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'kotlin sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.PHP_SDK, + description: 'Official PHP SDK maintainers', + github: { team: 'php-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'php sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.PYTHON_SDK, + description: 'Official Python SDK maintainers', + github: { team: 'python-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'python sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.PYTHON_SDK_AUTH, + description: 'Python SDK auth code owners', + github: { team: 'python-sdk-auth', parent: ROLE_IDS.PYTHON_SDK }, + // GitHub only - for CODEOWNERS + }, + { + id: ROLE_IDS.RUBY_SDK, + description: 'Official Ruby SDK maintainers', + github: { team: 'ruby-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'ruby sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.RUST_SDK, + description: 'Official Rust SDK maintainers', + github: { team: 'rust-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'rust sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.SWIFT_SDK, + description: 'Official Swift SDK maintainers', + github: { team: 'swift-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'swift sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.TYPESCRIPT_SDK, + description: 'Official TypeScript SDK', + github: { team: 'typescript-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + discord: { role: 'typescript sdk maintainers (synced)' }, + }, + { + id: ROLE_IDS.TYPESCRIPT_SDK_AUTH, + description: 'Code owners for auth in Typescript SDK', + github: { team: 'typescript-sdk-auth', parent: ROLE_IDS.TYPESCRIPT_SDK }, + // GitHub only - for CODEOWNERS + }, + + // =================== + // Working Groups + // =================== + { + id: ROLE_IDS.WORKING_GROUPS, + description: 'MCP Working Groups', + github: { team: 'working-groups', parent: ROLE_IDS.STEERING_COMMITTEE }, + // No discord - organizational container + }, + { + id: ROLE_IDS.AUTH_WG, + description: 'Authentication Working Group', + github: { team: 'auth-wg', parent: ROLE_IDS.WORKING_GROUPS }, + // See AUTH_IG for Discord role + }, + { + id: ROLE_IDS.SECURITY_WG, + description: 'Security Working Group', + github: { team: 'security-wg', parent: ROLE_IDS.WORKING_GROUPS }, + // See interest group for Discord role + }, + { + id: ROLE_IDS.SERVER_IDENTITY_WG, + description: 'Server Identity Working Group', + discord: { role: 'server identity working group (synced)' }, + // Discord only for now + }, + { + id: ROLE_IDS.TRANSPORT_WG, + description: 'Transport Working Group', + github: { team: 'transport-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'transports working group (synced)' }, + }, + + // =================== + // Interest Groups + // =================== + { + id: ROLE_IDS.INTEREST_GROUPS, + description: 'Interest Groups', + github: { team: 'interest-groups', parent: ROLE_IDS.STEERING_COMMITTEE }, + // No discord - organizational container + }, + { + id: ROLE_IDS.AGENTS_IG, + description: 'Agents Interest Group', + discord: { role: 'agents interest group (synced)' }, + // Discord only + }, + { + id: ROLE_IDS.AUTH_IG, + description: 'Auth Interest Group', + discord: { role: 'auth interest group (synced)' }, + // Discord only - separate from AUTH_WG which is GitHub + }, + { + id: ROLE_IDS.CLIENT_IMPLEMENTOR_IG, + description: 'Client Implementor Interest Group', + discord: { role: 'client implementor interest group (synced)' }, + // Discord only + }, + { + id: ROLE_IDS.FINANCIAL_SERVICES_IG, + description: 'Financial Services Interest Group', + github: { team: 'ig-financial-services', parent: ROLE_IDS.INTEREST_GROUPS }, + discord: { role: 'financial services interest group (synced)' }, + }, + + // =================== + // Email Groups (Google only) + // =================== + { + id: ROLE_IDS.ANTITRUST, + description: 'Antitrust compliance contacts', + google: { group: 'antitrust', isEmailGroup: true }, + // Google only + }, + { + id: ROLE_IDS.CATCH_ALL, + description: 'Catch-all email group', + google: { group: 'catch-all', isEmailGroup: true }, + // Google only + }, +] as const; + +/** + * Get a role by ID + */ +export function getRole(id: RoleId): Role | undefined { + return ROLES.find((r) => r.id === id); +} + +/** + * Get all roles that exist on a specific platform + */ +export function getRolesForPlatform(platform: 'github' | 'discord' | 'google'): Role[] { + return ROLES.filter((r) => r[platform] !== undefined); +} + +/** + * Build a lookup map of roles by ID + */ +export function buildRoleLookup(): Map { + return new Map(ROLES.map((r) => [r.id, r])); +} diff --git a/src/config/users.ts b/src/config/users.ts index 169725a..072613b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -1,85 +1,93 @@ import type { Member } from './utils'; +import { ROLE_IDS } from './roleIds'; export const MEMBERS: readonly Member[] = [ { github: '000-000-000-000-000', - memberOf: ['core-maintainers'], + memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { github: 'CodeWithKyrian', - memberOf: ['php-sdk'], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'D-McAdams', - memberOf: ['auth-wg'], + memberOf: [ROLE_IDS.AUTH_WG], }, { github: 'Kehrlann', - memberOf: ['java-sdk'], + memberOf: [ROLE_IDS.JAVA_SDK], }, { github: 'Kludex', - memberOf: ['python-sdk'], + memberOf: [ROLE_IDS.PYTHON_SDK], }, { github: 'Nyholm', - memberOf: ['php-sdk'], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'Ololoshechkin', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'a-akimov', - memberOf: ['docs-maintaners'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS], }, { github: 'aaronpk', - memberOf: ['auth-wg'], + memberOf: [ROLE_IDS.AUTH_WG], }, { github: 'alexhancock', - memberOf: ['rust-sdk'], + memberOf: [ROLE_IDS.RUST_SDK], }, { github: 'an-dustin', - memberOf: ['security-wg'], + memberOf: [ROLE_IDS.SECURITY_WG], }, { github: 'ansaba', - memberOf: ['go-sdk'], + memberOf: [ROLE_IDS.GO_SDK], }, { github: 'asklar', - memberOf: ['mcpb-maintainers'], + memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'atesgoral', - memberOf: ['ruby-sdk'], + memberOf: [ROLE_IDS.RUBY_SDK], }, { github: 'baxen', - memberOf: ['rust-sdk'], + memberOf: [ROLE_IDS.RUST_SDK], }, { github: 'bhosmer-ant', - memberOf: ['core-maintainers', 'docs-maintaners', 'moderators', 'python-sdk', 'typescript-sdk'], + discord: '1272295077074567242', + memberOf: [ + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.MODERATORS, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.TYPESCRIPT_SDK, + ], }, { github: 'carlpeaslee', - memberOf: ['swift-sdk'], + memberOf: [ROLE_IDS.SWIFT_SDK], }, { github: 'chemicL', - memberOf: ['java-sdk'], + memberOf: [ROLE_IDS.JAVA_SDK], }, { github: 'chr-hertel', - memberOf: ['php-sdk'], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'cliffhall', - memberOf: ['docs-maintaners', 'inspector-maintainers', 'moderators'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], }, { github: 'crondinini-ant', @@ -87,271 +95,278 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'dend', - memberOf: ['csharp-sdk'], + memberOf: [ROLE_IDS.CSHARP_SDK], }, { github: 'devcrocod', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'domdomegg', email: 'adam@modelcontextprotocol.io', - memberOf: ['mcpb-maintainers', 'registry-wg'], + memberOf: [ROLE_IDS.MCPB_MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'dsp-ant', email: 'david@modelcontextprotocol.io', + discord: '166107790262272000', // Example Discord user ID - replace with real ID memberOf: [ - 'auth-wg', - 'core-maintainers', - 'docs-maintaners', - 'go-sdk', - 'ig-financial-services', - 'moderators', - 'php-sdk', - 'python-sdk', - 'security-wg', - 'transport-wg', - 'typescript-sdk', + ROLE_IDS.AUTH_WG, + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.GO_SDK, + ROLE_IDS.FINANCIAL_SERVICES_IG, + ROLE_IDS.MODERATORS, + ROLE_IDS.PHP_SDK, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.SECURITY_WG, + ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.TYPESCRIPT_SDK, ], }, { github: 'e5l', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'eiriktsarpalis', - memberOf: ['csharp-sdk'], + memberOf: [ROLE_IDS.CSHARP_SDK], }, { github: 'evalstate', - memberOf: ['docs-maintaners', 'moderators'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS], }, { github: 'fabpot', - memberOf: ['php-sdk'], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'felixrieseberg', - memberOf: ['mcpb-maintainers'], + memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'felixweinberger', - memberOf: ['python-sdk', 'security-wg', 'typescript-sdk'], + memberOf: [ROLE_IDS.PYTHON_SDK, ROLE_IDS.SECURITY_WG, ROLE_IDS.TYPESCRIPT_SDK], }, { github: 'findleyr', - memberOf: ['go-sdk'], + memberOf: [ROLE_IDS.GO_SDK], }, { github: 'halter73', - memberOf: ['csharp-sdk'], + memberOf: [ROLE_IDS.CSHARP_SDK], }, { github: 'ignatov', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'ihrpr', - memberOf: ['docs-maintaners', 'python-sdk', 'typescript-sdk'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.PYTHON_SDK, ROLE_IDS.TYPESCRIPT_SDK], }, { github: 'jamadeo', - memberOf: ['rust-sdk'], + memberOf: [ROLE_IDS.RUST_SDK], }, { github: 'jba', - memberOf: ['go-sdk'], + memberOf: [ROLE_IDS.GO_SDK], }, { github: 'jenn-newton', - memberOf: ['security-wg'], + memberOf: [ROLE_IDS.SECURITY_WG], }, { github: 'jerome3o-anthropic', - memberOf: ['moderators'], + memberOf: [ROLE_IDS.MODERATORS], }, { github: 'joan-anthropic', - memberOf: ['mcpb-maintainers'], + memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'jonathanhefner', - memberOf: ['docs-maintaners', 'moderators', 'ruby-sdk'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.RUBY_SDK], }, { github: 'jspahrsummers', email: 'justin@modelcontextprotocol.io', - memberOf: ['core-maintainers'], + memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { github: 'KKonstantinov', - memberOf: ['inspector-maintainers', 'typescript-sdk'], + memberOf: [ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.TYPESCRIPT_SDK], }, { github: 'koic', - memberOf: ['ruby-sdk'], + memberOf: [ROLE_IDS.RUBY_SDK], }, { github: 'kpavlov', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'kurtisvg', - memberOf: ['core-maintainers', 'transport-wg'], + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { github: 'localden', - memberOf: ['auth-wg', 'core-maintainers'], + memberOf: [ROLE_IDS.AUTH_WG, ROLE_IDS.CORE_MAINTAINERS], }, { github: 'maheshmurag', - memberOf: ['moderators'], + memberOf: [ROLE_IDS.MODERATORS], }, { github: 'markpollack', - memberOf: ['java-sdk'], + memberOf: [ROLE_IDS.JAVA_SDK], }, { github: 'marshallofsound', - memberOf: ['mcpb-maintainers'], + memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'mattt', - memberOf: ['swift-sdk'], + memberOf: [ROLE_IDS.SWIFT_SDK], }, { github: 'maxisbey', - memberOf: ['python-sdk'], + memberOf: [ROLE_IDS.PYTHON_SDK], }, { github: 'mattzcarey', - memberOf: ['typescript-sdk'], + memberOf: [ROLE_IDS.TYPESCRIPT_SDK], }, { github: 'michaelneale', - memberOf: ['rust-sdk'], + memberOf: [ROLE_IDS.RUST_SDK], }, { github: 'movetz', - memberOf: ['swift-sdk'], + memberOf: [ROLE_IDS.SWIFT_SDK], }, { github: 'mikekistler', - memberOf: ['csharp-sdk'], + memberOf: [ROLE_IDS.CSHARP_SDK], }, { github: 'nickcoai', - memberOf: ['core-maintainers'], + memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { github: 'nicolas-grekas', - memberOf: ['php-sdk'], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'ochafik', - memberOf: ['python-sdk', 'python-sdk-auth', 'typescript-sdk', 'typescript-sdk-auth'], + memberOf: [ + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.PYTHON_SDK_AUTH, + ROLE_IDS.TYPESCRIPT_SDK, + ROLE_IDS.TYPESCRIPT_SDK_AUTH, + ], }, { github: 'og-ant', - memberOf: ['security-wg'], + memberOf: [ROLE_IDS.SECURITY_WG], }, { github: 'olaservo', - memberOf: ['docs-maintaners', 'inspector-maintainers', 'moderators'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], }, { github: 'pcarleton', memberOf: [ - 'core-maintainers', - 'python-sdk', - 'python-sdk-auth', - 'typescript-sdk', - 'typescript-sdk-auth', - 'auth-wg', + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.PYTHON_SDK_AUTH, + ROLE_IDS.TYPESCRIPT_SDK, + ROLE_IDS.TYPESCRIPT_SDK_AUTH, + ROLE_IDS.AUTH_WG, ], }, { github: 'pederhp', - memberOf: ['ig-financial-services'], + memberOf: [ROLE_IDS.FINANCIAL_SERVICES_IG], }, { github: 'petery-ant', - memberOf: ['security-wg'], + memberOf: [ROLE_IDS.SECURITY_WG], }, { github: 'pronskiy', - memberOf: ['php-sdk'], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'pwwpche', - memberOf: ['core-maintainers'], + memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { github: 'rdimitrov', email: 'radoslav@modelcontextprotocol.io', - memberOf: ['registry-wg'], + memberOf: [ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'sambhav', - memberOf: ['ig-financial-services'], + memberOf: [ROLE_IDS.FINANCIAL_SERVICES_IG], }, { github: 'samthanawalla', - memberOf: ['go-sdk'], + memberOf: [ROLE_IDS.GO_SDK], }, { github: 'sdubov', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'stephentoub', - memberOf: ['csharp-sdk'], + memberOf: [ROLE_IDS.CSHARP_SDK], }, { github: 'stallent', - memberOf: ['swift-sdk'], + memberOf: [ROLE_IDS.SWIFT_SDK], }, { github: 'tadasant', email: 'tadas@modelcontextprotocol.io', - memberOf: ['moderators', 'registry-wg'], + memberOf: [ROLE_IDS.MODERATORS, ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.ADMINISTRATORS], }, { github: 'tiginamaria', - memberOf: ['kotlin-sdk'], + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'toby', email: 'toby@modelcontextprotocol.io', - memberOf: ['registry-wg'], + memberOf: [ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'topherbullock', - memberOf: ['ruby-sdk'], + memberOf: [ROLE_IDS.RUBY_SDK], }, { github: 'tzolov', - memberOf: ['docs-maintaners', 'java-sdk'], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.JAVA_SDK], }, { email: 'adamj@anthropic.com', - memberOf: ['catch-all'], + memberOf: [ROLE_IDS.CATCH_ALL], }, { email: 'davidsp@anthropic.com', - memberOf: ['antitrust'], + memberOf: [ROLE_IDS.ANTITRUST], }, { email: 'mattsamuels@anthropic.com', - memberOf: ['antitrust'], + memberOf: [ROLE_IDS.ANTITRUST], }, { email: 'davideramian@anthropic.com', - memberOf: ['antitrust'], + memberOf: [ROLE_IDS.ANTITRUST], }, { github: 'caitiem20', - memberOf: ['core-maintainers'], + discord: '1425586366288494722', + memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, ] as const; diff --git a/src/config/utils.ts b/src/config/utils.ts index fd0112a..782c3e9 100644 --- a/src/config/utils.ts +++ b/src/config/utils.ts @@ -1,43 +1,71 @@ -import type { GROUPS } from './groups'; +import type { RoleId } from './roleIds'; +import type { Role } from './roles'; -function isValidGroupName(name: string): boolean { - return /^[a-z][a-z0-9-]*[a-z]$/.test(name); +/** + * A member of the MCP organization. + * Members are assigned to roles via memberOf, and the role definitions + * determine which platforms (GitHub, Discord, Google) they get access to. + */ +export interface Member { + /** GitHub username */ + github?: string; + /** Email address (for Google Workspace) */ + email?: string; + /** Discord user ID (snowflake) */ + discord?: string; + /** Roles this member belongs to */ + memberOf: readonly RoleId[]; } -export type Platform = 'github' | 'google'; - -export function defineGroups< - const T extends readonly { - name: Lowercase; - description: string; - memberOf?: readonly T[number]['name'][]; - isEmailGroup?: boolean; - onlyOnPlatforms?: readonly Platform[]; - }[], ->(groups: T) { - for (const group of groups) { - if (!isValidGroupName(group.name)) { - throw new Error( - `Invalid group name: ${group.name}. Must be lowercase alphanumeric with dashes, starting with a letter.` - ); +/** + * Sort roles by GitHub parent dependency (topological sort). + * Ensures parent teams are created before child teams. + * + * This is necessary because when creating GitHub teams, we need the parent + * team's ID. If we process roles in arbitrary order, a child team might be + * processed before its parent, resulting in undefined parentTeamId. + */ +export function sortRolesByGitHubDependency( + roles: readonly Role[], + roleLookup: Map +): Role[] { + const result: Role[] = []; + const visited = new Set(); + + function visit(role: Role): void { + if (visited.has(role.id)) return; + + // Only process roles with GitHub config + if (!role.github) { + visited.add(role.id); + return; } - } - return groups; -} + // Visit parent first if it exists and has GitHub config + if (role.github.parent) { + const parentRole = roleLookup.get(role.github.parent); + if (parentRole) { + visit(parentRole); + } + } -export type GroupKey = (typeof GROUPS)[number]['name']; + visited.add(role.id); + result.push(role); + } -export interface Group { - name: GroupKey; - description: string; - memberOf?: readonly GroupKey[]; - isEmailGroup?: boolean; - onlyOnPlatforms?: readonly Platform[]; -} + for (const role of roles) { + visit(role); + } -export interface Member { - github?: string; - email?: string; - memberOf: readonly GroupKey[]; + return result; } + +// Re-export for convenience +export { ROLE_IDS, type RoleId } from './roleIds'; +export { + ROLES, + type Role, + type GitHubConfig, + type DiscordConfig, + type GoogleConfig, +} from './roles'; diff --git a/src/discord.ts b/src/discord.ts new file mode 100644 index 0000000..a26f6ce --- /dev/null +++ b/src/discord.ts @@ -0,0 +1,418 @@ +import * as pulumi from '@pulumi/pulumi'; +import { ROLES, type Role, buildRoleLookup } from './config/roles'; +import { MEMBERS } from './config/users'; +import type { RoleId } from './config/roleIds'; + +const config = new pulumi.Config('discord'); +// Discord integration is optional - only enabled if botToken and guildId are configured +const DISCORD_BOT_TOKEN = config.getSecret('botToken'); +const DISCORD_GUILD_ID = config.get('guildId'); +const DISCORD_ENABLED = DISCORD_BOT_TOKEN !== undefined && DISCORD_GUILD_ID !== undefined; + +if (!DISCORD_ENABLED) { + pulumi.log.info('Discord integration disabled: botToken or guildId not configured'); +} + +const DISCORD_API_BASE = 'https://discord.com/api/v10'; + +interface DiscordApiError { + code: number; + message: string; +} + +async function discordFetch( + token: string, + endpoint: string, + options: RequestInit = {} +): Promise { + const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, { + ...options, + headers: { + Authorization: `Bot ${token}`, + 'Content-Type': 'application/json', + ...options.headers, + }, + }); + + if (!response.ok) { + const error = (await response.json()) as DiscordApiError; + throw new Error(`Discord API error: ${error.message} (code: ${error.code})`); + } + + // Handle 204 No Content + if (response.status === 204) { + return undefined as T; + } + + return response.json() as Promise; +} + +// Discord API response types +interface DiscordRoleApiResponse { + id: string; + name: string; + position: number; + permissions: string; + managed: boolean; +} + +interface DiscordGuildMemberApiResponse { + roles: string[]; +} + +// Discord Role Dynamic Provider +interface DiscordRoleInputs { + guildId: string; + roleName: string; + token: string; +} + +interface DiscordRoleOutputs extends DiscordRoleInputs { + roleId: string; +} + +const discordRoleProvider: pulumi.dynamic.ResourceProvider = { + async create( + inputs: DiscordRoleInputs + ): Promise> { + const role = await discordFetch( + inputs.token, + `/guilds/${inputs.guildId}/roles`, + { + method: 'POST', + body: JSON.stringify({ + name: inputs.roleName, + permissions: '0', // No special permissions - roles are for organization only + mentionable: false, + hoist: false, + }), + } + ); + + return { + id: role.id, + outs: { + ...inputs, + roleId: role.id, + }, + }; + }, + + async read( + id: string, + props: DiscordRoleOutputs + ): Promise> { + try { + const roles = await discordFetch( + props.token, + `/guilds/${props.guildId}/roles` + ); + + const role = roles.find((r) => r.id === id); + if (!role) { + // Role was deleted externally + throw new Error(`Role ${id} not found`); + } + + return { + id, + props: { + ...props, + roleName: role.name, + roleId: role.id, + }, + }; + } catch { + throw new Error(`Failed to read role ${id}`); + } + }, + + async update( + id: string, + _olds: DiscordRoleOutputs, + news: DiscordRoleInputs + ): Promise> { + await discordFetch(news.token, `/guilds/${news.guildId}/roles/${id}`, { + method: 'PATCH', + body: JSON.stringify({ + name: news.roleName, + }), + }); + + return { + outs: { + ...news, + roleId: id, + }, + }; + }, + + async delete(id: string, props: DiscordRoleOutputs): Promise { + try { + await discordFetch(props.token, `/guilds/${props.guildId}/roles/${id}`, { + method: 'DELETE', + }); + } catch (error) { + // Ignore errors if role is already deleted + console.warn(`Failed to delete role ${id}: ${error}`); + } + }, +}; + +class DiscordRole extends pulumi.dynamic.Resource { + public readonly roleId!: pulumi.Output; + public readonly roleName!: pulumi.Output; + public readonly guildId!: pulumi.Output; + + constructor( + name: string, + args: { + guildId: pulumi.Input; + roleName: pulumi.Input; + token: pulumi.Input; + }, + opts?: pulumi.CustomResourceOptions + ) { + super( + discordRoleProvider, + name, + { + roleId: undefined, + ...args, + }, + opts + ); + } +} + +// Discord Member Role Sync Dynamic Provider +// This provider reconciles a user's roles to match exactly what's defined in config +// It adds missing roles AND removes extra roles (only for roles we manage) +interface DiscordMemberRoleSyncInputs { + guildId: string; + userId: string; + /** Role IDs that this user SHOULD have (managed roles only) */ + expectedRoleIds: string[]; + /** All role IDs that we manage (to know which ones to potentially remove) */ + managedRoleIds: string[]; + token: string; +} + +interface DiscordMemberRoleSyncOutputs extends DiscordMemberRoleSyncInputs { + /** Roles that were added during last sync */ + addedRoles: string[]; + /** Roles that were removed during last sync */ + removedRoles: string[]; +} + +async function syncMemberRoles( + inputs: DiscordMemberRoleSyncInputs +): Promise<{ addedRoles: string[]; removedRoles: string[] }> { + // Get the user's current roles + const member = await discordFetch( + inputs.token, + `/guilds/${inputs.guildId}/members/${inputs.userId}` + ); + + const currentRoles = new Set(member.roles); + const expectedRoles = new Set(inputs.expectedRoleIds); + const managedRoles = new Set(inputs.managedRoleIds); + + const addedRoles: string[] = []; + const removedRoles: string[] = []; + + // Add missing roles + for (const roleId of Array.from(expectedRoles)) { + if (!currentRoles.has(roleId)) { + await discordFetch( + inputs.token, + `/guilds/${inputs.guildId}/members/${inputs.userId}/roles/${roleId}`, + { method: 'PUT' } + ); + addedRoles.push(roleId); + } + } + + // Remove roles that the user has but shouldn't (only managed roles) + for (const roleId of Array.from(currentRoles)) { + if (managedRoles.has(roleId) && !expectedRoles.has(roleId)) { + await discordFetch( + inputs.token, + `/guilds/${inputs.guildId}/members/${inputs.userId}/roles/${roleId}`, + { method: 'DELETE' } + ); + removedRoles.push(roleId); + } + } + + return { addedRoles, removedRoles }; +} + +const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { + async create( + inputs: DiscordMemberRoleSyncInputs + ): Promise> { + const { addedRoles, removedRoles } = await syncMemberRoles(inputs); + + return { + id: inputs.userId, + outs: { + ...inputs, + addedRoles, + removedRoles, + }, + }; + }, + + async read( + id: string, + props: DiscordMemberRoleSyncOutputs + ): Promise> { + try { + const member = await discordFetch( + props.token, + `/guilds/${props.guildId}/members/${props.userId}` + ); + + const currentRoles = new Set(member.roles); + const expectedRoles = new Set(props.expectedRoleIds); + const managedRoles = new Set(props.managedRoleIds); + + // Check if roles are in sync (only considering managed roles) + const outOfSync = + Array.from(expectedRoles).some((r) => !currentRoles.has(r)) || + Array.from(currentRoles).some((r) => managedRoles.has(r) && !expectedRoles.has(r)); + + if (outOfSync) { + // Return current state but note it needs update + return { + id, + props: { + ...props, + addedRoles: [], + removedRoles: [], + }, + }; + } + + return { id, props }; + } catch { + throw new Error(`Failed to read member roles for ${id}`); + } + }, + + async update( + id: string, + _olds: DiscordMemberRoleSyncOutputs, + news: DiscordMemberRoleSyncInputs + ): Promise> { + const { addedRoles, removedRoles } = await syncMemberRoles(news); + + return { + outs: { + ...news, + addedRoles, + removedRoles, + }, + }; + }, + + async delete(id: string, props: DiscordMemberRoleSyncOutputs): Promise { + // When a user is removed from config, remove all their managed roles + for (const roleId of props.expectedRoleIds) { + try { + await discordFetch( + props.token, + `/guilds/${props.guildId}/members/${props.userId}/roles/${roleId}`, + { method: 'DELETE' } + ); + } catch (error) { + console.warn(`Failed to remove role ${roleId} from user ${id}: ${error}`); + } + } + }, +}; + +class DiscordMemberRoleSync extends pulumi.dynamic.Resource { + public readonly addedRoles!: pulumi.Output; + public readonly removedRoles!: pulumi.Output; + + constructor( + name: string, + args: { + guildId: pulumi.Input; + userId: pulumi.Input; + expectedRoleIds: pulumi.Input[]>; + managedRoleIds: pulumi.Input[]>; + token: pulumi.Input; + }, + opts?: pulumi.CustomResourceOptions + ) { + super( + discordMemberRoleSyncProvider, + name, + { + addedRoles: undefined, + removedRoles: undefined, + ...args, + }, + opts + ); + } +} + +const roleLookup = buildRoleLookup(); +// Discord roles keyed by Discord role name +const roles: Record = {}; + +// Only create Discord resources if Discord is enabled +if (DISCORD_ENABLED) { + // These are guaranteed to be defined when DISCORD_ENABLED is true + const guildId = DISCORD_GUILD_ID!; + const botToken = DISCORD_BOT_TOKEN!; + + // Create Discord roles for roles that have Discord config + ROLES.forEach((role: Role) => { + if (!role.discord) return; + + roles[role.discord.role] = new DiscordRole(`discord-role-${role.id}`, { + guildId, + roleName: role.discord.role, + token: botToken, + }); + }); + + // Collect all managed role IDs (roles that have Discord config) + const allManagedRoleIds = ROLES.filter((r) => r.discord).map( + (r) => roles[r.discord!.role].roleId + ); + + // Sync roles for each member + MEMBERS.forEach((member) => { + if (!member.discord) return; + + // Get the Discord role IDs this member should have + const expectedRoleIds = member.memberOf + .map((roleId: RoleId) => { + const role = roleLookup.get(roleId); + if (!role?.discord) return null; + return roles[role.discord.role].roleId; + }) + .filter((id): id is pulumi.Output => id !== null); + + // Create a sync resource for this member + new DiscordMemberRoleSync( + `discord-member-sync-${member.discord}`, + { + guildId, + userId: member.discord!, + expectedRoleIds, + managedRoleIds: allManagedRoleIds, + token: botToken, + }, + { dependsOn: Object.values(roles) } + ); + }); +} + +export { roles as discordRoles }; diff --git a/src/github.ts b/src/github.ts index d398bf6..3d6a223 100644 --- a/src/github.ts +++ b/src/github.ts @@ -1,40 +1,61 @@ import * as github from '@pulumi/github'; -import { GROUPS } from './config/groups'; +import { ROLES, type Role, buildRoleLookup } from './config/roles'; import { REPOSITORY_ACCESS } from './config/repoAccess'; -import type { Group } from './config/utils'; import { MEMBERS } from './config/users'; +import { sortRolesByGitHubDependency } from './config/utils'; +import type { RoleId } from './config/roleIds'; +const roleLookup = buildRoleLookup(); +// Teams keyed by GitHub team name (matches repoAccess.ts references) const teams: Record = {}; -GROUPS.forEach((group: Group) => { - // Skip groups that don't include github in their platforms - if (group.onlyOnPlatforms && !group.onlyOnPlatforms.includes('github')) return; +// Sort roles so parent teams are created before child teams +const sortedRoles = sortRolesByGitHubDependency(ROLES, roleLookup); - teams[group.name] = new github.Team(group.name, { - name: group.name, - description: group.description + ' \n(Managed by github.com/modelcontextprotocol/access)', +// Create GitHub teams for roles that have GitHub config +sortedRoles.forEach((role: Role) => { + if (!role.github) return; + + // Resolve parent team ID if specified + // Parent is guaranteed to exist in `teams` due to topological sort + let parentTeamId: github.Team['id'] | undefined; + if (role.github.parent) { + const parentRole = roleLookup.get(role.github.parent); + if (parentRole?.github) { + parentTeamId = teams[parentRole.github.team]?.id; + } + } + + teams[role.github.team] = new github.Team(role.github.team, { + name: role.github.team, + description: role.description + ' \n(Managed by github.com/modelcontextprotocol/access)', privacy: 'closed', - parentTeamId: group.memberOf?.[0] ? teams[group.memberOf[0]].id : undefined, + parentTeamId, }); }); +// Create team memberships MEMBERS.forEach((member) => { if (!member.github) return; - member.memberOf.forEach((teamKey) => { - new github.TeamMembership(`${member.github}-${teamKey}`, { - teamId: teams[teamKey].id, + member.memberOf.forEach((roleId: RoleId) => { + const role = roleLookup.get(roleId); + if (!role?.github) return; // Role doesn't have GitHub config + + new github.TeamMembership(`${member.github}-${role.github.team}`, { + teamId: teams[role.github.team].id, username: member.github!, role: 'member', }); }); }); +// Configure repository access REPOSITORY_ACCESS.forEach((repo) => { new github.RepositoryCollaborators(`repo-${repo.repository}`, { repository: repo.repository, teams: repo.teams?.map((t) => ({ - teamId: teams[t.team].id, + teamId: teams[t.team]?.id, permission: t.permission, })), users: repo.users?.map((u) => ({ @@ -43,3 +64,5 @@ REPOSITORY_ACCESS.forEach((repo) => { })), }); }); + +export { teams as githubTeams }; diff --git a/src/google.ts b/src/google.ts index 96c7d7c..f52499b 100644 --- a/src/google.ts +++ b/src/google.ts @@ -1,22 +1,24 @@ import * as gworkspace from '@pulumi/googleworkspace'; -import { GROUPS } from './config/groups'; -import type { Group } from './config/utils'; +import { ROLES, type Role, buildRoleLookup } from './config/roles'; import { MEMBERS } from './config/users'; +import type { RoleId } from './config/roleIds'; +const roleLookup = buildRoleLookup(); +// Groups keyed by Google group name const groups: Record = {}; -GROUPS.forEach((group: Group) => { - // Skip groups that don't include google in their platforms - if (group.onlyOnPlatforms && !group.onlyOnPlatforms.includes('google')) return; +// Create Google groups for roles that have Google config +ROLES.forEach((role: Role) => { + if (!role.google) return; - groups[group.name] = new gworkspace.Group(group.name, { - email: `${group.name}@modelcontextprotocol.io`, - name: group.name, - description: group.description + ' \n(Managed by github.com/modelcontextprotocol/access)', + groups[role.google.group] = new gworkspace.Group(role.google.group, { + email: `${role.google.group}@modelcontextprotocol.io`, + name: role.google.group, + description: role.description + ' \n(Managed by github.com/modelcontextprotocol/access)', }); - new gworkspace.GroupSettings(group.name, { - email: groups[group.name].email, + new gworkspace.GroupSettings(role.google.group, { + email: groups[role.google.group].email, // Maximise visibility of group. It's visible in GitHub anyway whoCanViewMembership: 'ALL_IN_DOMAIN_CAN_VIEW', @@ -29,7 +31,7 @@ GROUPS.forEach((group: Group) => { // Email groups allow anyone (including externals) to post // Non-email groups are not intended as mailing lists, so use the most restrictive settings // whoCanViewGroup is badly named, but actually means 'Permissions to view group messages'. See https://developers.google.com/workspace/admin/groups-settings/v1/reference/groups - ...(group.isEmailGroup + ...(role.google.isEmailGroup ? { whoCanPostMessage: 'ANYONE_CAN_POST', whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', @@ -41,30 +43,22 @@ GROUPS.forEach((group: Group) => { whoCanViewGroup: 'ALL_OWNERS_CAN_VIEW', }), }); - - group.memberOf?.forEach((parentGroupKey) => { - // Skip if parent group doesn't exist on Google (e.g., onlyOnPlatforms: ['github']) - if (!groups[parentGroupKey]) return; - - new gworkspace.GroupMember(`${group.name}-in-${parentGroupKey}`, { - groupId: groups[parentGroupKey].id, - email: groups[group.name].email, - role: 'MEMBER', - }); - }); }); +// Create group memberships for users MEMBERS.forEach((member) => { if (!member.email) return; - member.memberOf.forEach((teamKey) => { - // Skip if group doesn't exist on Google (e.g., onlyOnPlatforms: ['github']) - if (!groups[teamKey]) return; + member.memberOf.forEach((roleId: RoleId) => { + const role = roleLookup.get(roleId); + if (!role?.google) return; // Role doesn't have Google config - new gworkspace.GroupMember(`${member.email}-${teamKey}`, { - groupId: groups[teamKey].id, + new gworkspace.GroupMember(`${member.email}-${role.google.group}`, { + groupId: groups[role.google.group].id, email: member.email!, role: 'MEMBER', }); }); }); + +export { groups as googleGroups }; diff --git a/src/index.ts b/src/index.ts index 4fe06f1..c72d079 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,3 @@ export * from './github'; export * from './google'; +export * from './discord'; From 4177552a612830e079986046d480e877594e5c16 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 13 Jan 2026 23:33:31 +0000 Subject: [PATCH 035/196] Add dsp-ant to lead-maintainers --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index 072613b..4d3e932 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -112,6 +112,7 @@ export const MEMBERS: readonly Member[] = [ discord: '166107790262272000', // Example Discord user ID - replace with real ID memberOf: [ ROLE_IDS.AUTH_WG, + ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.GO_SDK, From 4e08d76e950acb31e98e62cb2ed7cacf35d784aa Mon Sep 17 00:00:00 2001 From: Caitie McCaffrey Date: Wed, 14 Jan 2026 01:43:11 -0800 Subject: [PATCH 036/196] Add user caitiem20 (#26) * Add user caitiem20 * removing duplicate caitie * ran prettier --- src/config/users.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 4d3e932..7e113cd 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -73,6 +73,12 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.TYPESCRIPT_SDK, ], }, + { + github: 'caitiem20', + email: 'caitie.mccaffrey@microsoft.com', + discord: '1425586366288494722', + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], + }, { github: 'carlpeaslee', memberOf: [ROLE_IDS.SWIFT_SDK], @@ -365,9 +371,4 @@ export const MEMBERS: readonly Member[] = [ email: 'davideramian@anthropic.com', memberOf: [ROLE_IDS.ANTITRUST], }, - { - github: 'caitiem20', - discord: '1425586366288494722', - memberOf: [ROLE_IDS.CORE_MAINTAINERS], - }, ] as const; From a1e9b03831f37898adcffed063777d305c0528e3 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 14 Jan 2026 09:44:20 +0000 Subject: [PATCH 037/196] Skip preview workflow for fork PRs Fork PRs don't have access to repository secrets, causing the GCP auth and PR comment steps to fail. Instead of failing, skip the preview job entirely for fork PRs. The job will show as 'skipped' rather than 'failed', allowing fork PRs to be merged based on the CI check alone. Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 2 Claude-Permission-Prompts: 1 Claude-Escapes: 0 --- .github/workflows/preview.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 8f5c812..f8700b4 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -16,6 +16,8 @@ jobs: preview: name: Preview Changes runs-on: ubuntu-latest + # Skip preview for fork PRs - they don't have access to secrets + if: github.event.pull_request.head.repo.full_name == github.repository environment: production steps: - name: Checkout code From 12e04177166a2c03cef0310489ba099424afdda5 Mon Sep 17 00:00:00 2001 From: Luca Chang <131398524+LucaButBoring@users.noreply.github.com> Date: Wed, 14 Jan 2026 01:45:17 -0800 Subject: [PATCH 038/196] Add LucaButBoring (#24) --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 7e113cd..c3886de 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -220,6 +220,11 @@ export const MEMBERS: readonly Member[] = [ github: 'localden', memberOf: [ROLE_IDS.AUTH_WG, ROLE_IDS.CORE_MAINTAINERS], }, + { + github: 'LucaButBoring', + discord: '1366470072729866252', + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.AGENTS_IG, ROLE_IDS.WORKING_GROUPS], + }, { github: 'maheshmurag', memberOf: [ROLE_IDS.MODERATORS], From 9cd19af177195bd14fd29d0e8dbd8b7c3042e07d Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Wed, 14 Jan 2026 10:46:31 +0100 Subject: [PATCH 039/196] Update discord & email for chr-hertel (#27) * Update discord & email for chr-hertel * Update src/config/users.ts --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index c3886de..45f71c3 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -89,7 +89,9 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'chr-hertel', - memberOf: [ROLE_IDS.PHP_SDK], + email: 'mail@christopher-hertel.de', + discord: '633566986827464704', + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.PHP_SDK], }, { github: 'cliffhall', From a3269d383d66cc6dd1b90524f67a7bb1dd5d27e8 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Wed, 14 Jan 2026 11:48:16 +0200 Subject: [PATCH 040/196] Add Discord ID to user configuration for Rado (#28) * Add Discord ID to user configuration for Rado * Update src/config/users.ts --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 45f71c3..f17d04c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -317,8 +317,9 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'rdimitrov', + discord: '1088231882979815424', email: 'radoslav@modelcontextprotocol.io', - memberOf: [ROLE_IDS.REGISTRY_MAINTAINERS], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'sambhav', From ac93a40b9251acca478382b530e7719200573880 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 14 Jan 2026 09:50:31 +0000 Subject: [PATCH 041/196] Remove automatic refresh from deploy flow Refresh before every deploy adds latency without much benefit since infrastructure is fully managed via code. The refresh target remains available for manual use when drift is suspected. Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 2 Claude-Permission-Prompts: 0 Claude-Escapes: 0 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0f7d48f..1d2d627 100644 --- a/Makefile +++ b/Makefile @@ -15,5 +15,5 @@ preview: login ## Preview infrastructure changes refresh: login ## Refresh state to match reality PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi refresh --yes --stack prod -up: login refresh ## Deploy infrastructure +up: login ## Deploy infrastructure PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack prod From 4c01df2c83ed73637dda43bb5875696ca553ace3 Mon Sep 17 00:00:00 2001 From: Cliff Hall Date: Wed, 14 Jan 2026 04:54:27 -0500 Subject: [PATCH 042/196] Updated user Cliff Hall (#25) * Updated user Cliff Hall - Add email address - Add discord user id - Add missing role.REFERENCE_SERVERS_MAINTAINERS * Updated user Cliff Hall - Add missing role MAINTAINERS * Updated user Cliff Hall - Add missing role WORKING_GROUPS * Prettier --- src/config/users.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index f17d04c..cf79ac9 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -95,7 +95,16 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'cliffhall', - memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], + email: 'cliff@futurescale.com', + discord: '501498061965754380', + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.INSPECTOR_MAINTAINERS, + ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, + ROLE_IDS.MODERATORS, + ROLE_IDS.WORKING_GROUPS, + ], }, { github: 'crondinini-ant', From 6db46ea763a43118175eed36bb8ff5cc23ac7121 Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Wed, 14 Jan 2026 14:52:34 -0500 Subject: [PATCH 043/196] chore: add discord identifier for myself (#31) * chore: add discord identifier for myself * Update src/config/users.ts --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index cf79ac9..4175822 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -40,7 +40,8 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'alexhancock', - memberOf: [ROLE_IDS.RUST_SDK], + discord: '1325885093343924316', + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.RUST_SDK], }, { github: 'an-dustin', From 29f1d92c220204cac666087966e9ccf89641b4cc Mon Sep 17 00:00:00 2001 From: Den Delimarsky Date: Wed, 14 Jan 2026 12:55:22 -0800 Subject: [PATCH 044/196] Core Maintainer Discord IDs (#29) * Add Discord user IDs * Update users.ts * Update member roles and remove 'dend' from users config Removed the user 'dend' from the MEMBERS list. Updated roles for 'ihrpr', 'localden', and 'nickcoai' to reflect new responsibilities and group memberships. * Update formatting based on prettier --------- Co-authored-by: Den Delimarsky <53200638+localden@users.noreply.github.com> Co-authored-by: Den Delimarsky --- src/config/users.ts | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 4175822..42a034f 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -4,6 +4,7 @@ import { ROLE_IDS } from './roleIds'; export const MEMBERS: readonly Member[] = [ { github: '000-000-000-000-000', + discord: '1360717264051241071', memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { @@ -111,10 +112,6 @@ export const MEMBERS: readonly Member[] = [ github: 'crondinini-ant', memberOf: [], }, - { - github: 'dend', - memberOf: [ROLE_IDS.CSHARP_SDK], - }, { github: 'devcrocod', memberOf: [ROLE_IDS.KOTLIN_SDK], @@ -181,7 +178,13 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ihrpr', - memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.PYTHON_SDK, ROLE_IDS.TYPESCRIPT_SDK], + discord: '1086709448816468038', + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.TYPESCRIPT_SDK, + ], }, { github: 'jamadeo', @@ -226,11 +229,18 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'kurtisvg', + discord: '1158458388917780590', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { github: 'localden', - memberOf: [ROLE_IDS.AUTH_WG, ROLE_IDS.CORE_MAINTAINERS], + discord: '1351224014143754260', + memberOf: [ + ROLE_IDS.AUTH_WG, + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.CSHARP_SDK, + ROLE_IDS.DOCS_MAINTAINERS, + ], }, { github: 'LucaButBoring', @@ -275,7 +285,8 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'nickcoai', - memberOf: [ROLE_IDS.CORE_MAINTAINERS], + discord: '1153783469860732968', + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.SERVER_IDENTITY_WG], }, { github: 'nicolas-grekas', @@ -300,6 +311,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'pcarleton', + discord: '1354465170969067852', memberOf: [ ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.PYTHON_SDK, @@ -323,6 +335,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'pwwpche', + discord: '1226238847013228604', memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { From 3b45cbf787abd9ed03ec3d5fa91e0fdf9af33bc4 Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:57:38 -0700 Subject: [PATCH 045/196] add discord id for kurtisvg (#33) From 9fb0db9f9cfdb39185d36aea731dbda3f72cb92b Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 14 Jan 2026 21:30:24 +0000 Subject: [PATCH 046/196] Remove invalid Discord ID for ihrpr The Discord ID 1086709448816468038 is not a member of the MCP Discord server, causing deploy failures with 'Unknown Member (code: 10007)' Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 1 Claude-Permission-Prompts: 2 Claude-Escapes: 0 --- src/config/users.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 42a034f..39c09de 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -178,7 +178,6 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ihrpr', - discord: '1086709448816468038', memberOf: [ ROLE_IDS.MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, From c92a40849e2bb3d8980531235e41e89bd521db91 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 14 Jan 2026 21:39:52 +0000 Subject: [PATCH 047/196] Handle missing Discord members gracefully When a user with a Discord ID leaves the server (or the ID is invalid), the sync now logs a warning and continues instead of failing the entire deploy. This prevents deploys from breaking when someone leaves Discord. The memberNotFound flag is tracked in Pulumi state so we can identify stale Discord IDs. Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 0 Claude-Permission-Prompts: 0 Claude-Escapes: 0 --- src/discord.ts | 81 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/src/discord.ts b/src/discord.ts index a26f6ce..9ef4f43 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -203,16 +203,31 @@ interface DiscordMemberRoleSyncOutputs extends DiscordMemberRoleSyncInputs { addedRoles: string[]; /** Roles that were removed during last sync */ removedRoles: string[]; + /** True if the member was not found on the Discord server */ + memberNotFound: boolean; } async function syncMemberRoles( inputs: DiscordMemberRoleSyncInputs -): Promise<{ addedRoles: string[]; removedRoles: string[] }> { +): Promise<{ addedRoles: string[]; removedRoles: string[]; memberNotFound: boolean }> { // Get the user's current roles - const member = await discordFetch( - inputs.token, - `/guilds/${inputs.guildId}/members/${inputs.userId}` - ); + let member: DiscordGuildMemberApiResponse; + try { + member = await discordFetch( + inputs.token, + `/guilds/${inputs.guildId}/members/${inputs.userId}` + ); + } catch (error) { + // If the member isn't on the server, skip gracefully + if (error instanceof Error && error.message.includes('code: 10007')) { + console.warn( + `Discord member ${inputs.userId} not found on server - skipping role sync. ` + + `They may have left the server or the Discord ID may be incorrect.` + ); + return { addedRoles: [], removedRoles: [], memberNotFound: true }; + } + throw error; + } const currentRoles = new Set(member.roles); const expectedRoles = new Set(inputs.expectedRoleIds); @@ -245,14 +260,14 @@ async function syncMemberRoles( } } - return { addedRoles, removedRoles }; + return { addedRoles, removedRoles, memberNotFound: false }; } const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { async create( inputs: DiscordMemberRoleSyncInputs ): Promise> { - const { addedRoles, removedRoles } = await syncMemberRoles(inputs); + const { addedRoles, removedRoles, memberNotFound } = await syncMemberRoles(inputs); return { id: inputs.userId, @@ -260,6 +275,7 @@ const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { ...inputs, addedRoles, removedRoles, + memberNotFound, }, }; }, @@ -268,37 +284,51 @@ const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { id: string, props: DiscordMemberRoleSyncOutputs ): Promise> { + let member: DiscordGuildMemberApiResponse; try { - const member = await discordFetch( + member = await discordFetch( props.token, `/guilds/${props.guildId}/members/${props.userId}` ); - - const currentRoles = new Set(member.roles); - const expectedRoles = new Set(props.expectedRoleIds); - const managedRoles = new Set(props.managedRoleIds); - - // Check if roles are in sync (only considering managed roles) - const outOfSync = - Array.from(expectedRoles).some((r) => !currentRoles.has(r)) || - Array.from(currentRoles).some((r) => managedRoles.has(r) && !expectedRoles.has(r)); - - if (outOfSync) { - // Return current state but note it needs update + } catch (error) { + // If the member isn't on the server, return state indicating they're not found + if (error instanceof Error && error.message.includes('code: 10007')) { return { id, props: { ...props, addedRoles: [], removedRoles: [], + memberNotFound: true, }, }; } - - return { id, props }; - } catch { throw new Error(`Failed to read member roles for ${id}`); } + + const currentRoles = new Set(member.roles); + const expectedRoles = new Set(props.expectedRoleIds); + const managedRoles = new Set(props.managedRoleIds); + + // Check if roles are in sync (only considering managed roles) + const outOfSync = + Array.from(expectedRoles).some((r) => !currentRoles.has(r)) || + Array.from(currentRoles).some((r) => managedRoles.has(r) && !expectedRoles.has(r)); + + if (outOfSync) { + // Return current state but note it needs update + return { + id, + props: { + ...props, + addedRoles: [], + removedRoles: [], + memberNotFound: false, + }, + }; + } + + return { id, props: { ...props, memberNotFound: false } }; }, async update( @@ -306,13 +336,14 @@ const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { _olds: DiscordMemberRoleSyncOutputs, news: DiscordMemberRoleSyncInputs ): Promise> { - const { addedRoles, removedRoles } = await syncMemberRoles(news); + const { addedRoles, removedRoles, memberNotFound } = await syncMemberRoles(news); return { outs: { ...news, addedRoles, removedRoles, + memberNotFound, }, }; }, @@ -336,6 +367,7 @@ const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { class DiscordMemberRoleSync extends pulumi.dynamic.Resource { public readonly addedRoles!: pulumi.Output; public readonly removedRoles!: pulumi.Output; + public readonly memberNotFound!: pulumi.Output; constructor( name: string, @@ -354,6 +386,7 @@ class DiscordMemberRoleSync extends pulumi.dynamic.Resource { { addedRoles: undefined, removedRoles: undefined, + memberNotFound: undefined, ...args, }, opts From 95bcd728777d0c50a8679a2d20ea20bc85fa2120 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Wed, 14 Jan 2026 15:59:37 -0800 Subject: [PATCH 048/196] Add gateways-ig, roles for Tadas (#30) * Add * Missing comma * Add Discord ID * Prettier --- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 10 +++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 2170c63..b1aa0a3 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -57,6 +57,7 @@ export const ROLE_IDS = { AUTH_IG: 'auth-ig', CLIENT_IMPLEMENTOR_IG: 'client-implementor-ig', FINANCIAL_SERVICES_IG: 'financial-services-ig', + GATEWAYS_IG: 'gateways-ig', // =================== // Email Groups (Google only) diff --git a/src/config/roles.ts b/src/config/roles.ts index e328637..001c08e 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -276,6 +276,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'ig-financial-services', parent: ROLE_IDS.INTEREST_GROUPS }, discord: { role: 'financial services interest group (synced)' }, }, + { + id: ROLE_IDS.GATEWAYS_IG, + description: 'Gateways Interest Group', + // No GitHub role yet + discord: { role: 'gateways interest group (synced)' }, + }, // =================== // Email Groups (Google only) diff --git a/src/config/users.ts b/src/config/users.ts index 39c09de..d2a92de 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -365,8 +365,16 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'tadasant', + discord: '400092503677599754', email: 'tadas@modelcontextprotocol.io', - memberOf: [ROLE_IDS.MODERATORS, ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.ADMINISTRATORS], + memberOf: [ + ROLE_IDS.MODERATORS, + ROLE_IDS.MAINTAINERS, + ROLE_IDS.WORKING_GROUPS, + ROLE_IDS.INTEREST_GROUPS, + ROLE_IDS.REGISTRY_MAINTAINERS, + ROLE_IDS.ADMINISTRATORS, + ], }, { github: 'tiginamaria', From 1ee90512f054171863a65b493199b9f357da4243 Mon Sep 17 00:00:00 2001 From: Liad Yosef Date: Thu, 15 Jan 2026 02:03:50 +0200 Subject: [PATCH 049/196] chore: add apps-wg and discord/github for liad (#32) * chore: add apps-wg and discord/github for liad * Update src/config/roles.ts Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> * fix: PR comments * minor: comment --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/roleIds.ts | 2 ++ src/config/roles.ts | 12 ++++++++++++ src/config/users.ts | 10 ++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index b1aa0a3..b560bc4 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -31,6 +31,7 @@ export const ROLE_IDS = { GO_SDK: 'go-sdk', JAVA_SDK: 'java-sdk', KOTLIN_SDK: 'kotlin-sdk', + MCP_APPS_SDK: 'mcp-apps-sdk', PHP_SDK: 'php-sdk', PYTHON_SDK: 'python-sdk', PYTHON_SDK_AUTH: 'python-sdk-auth', // GitHub only (CODEOWNERS) @@ -48,6 +49,7 @@ export const ROLE_IDS = { SECURITY_WG: 'security-wg', SERVER_IDENTITY_WG: 'server-identity-wg', TRANSPORT_WG: 'transport-wg', + MCP_APPS_WG: 'mcp-apps-wg', // =================== // Interest Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index 001c08e..38515d0 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -160,6 +160,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'kotlin-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, discord: { role: 'kotlin sdk maintainers (synced)' }, }, + { + id: ROLE_IDS.MCP_APPS_SDK, + description: 'Official MCP Apps SDK maintainers', + github: { team: 'mcp-apps-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, + // No Discord channel/role yet: #mcp-apps-sdk-dev in the future + }, { id: ROLE_IDS.PHP_SDK, description: 'Official PHP SDK maintainers', @@ -242,6 +248,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'transport-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'transports working group (synced)' }, }, + { + id: ROLE_IDS.MCP_APPS_WG, + description: 'MCP Apps Working Group', + github: { team: 'mcp-apps-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'mcp apps working group (synced)' }, + }, // =================== // Interest Groups diff --git a/src/config/users.ts b/src/config/users.ts index d2a92de..55329b2 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -231,6 +231,16 @@ export const MEMBERS: readonly Member[] = [ discord: '1158458388917780590', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, + { + github: 'liady', + discord: '383565833768665088', + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.WORKING_GROUPS, + ROLE_IDS.MCP_APPS_WG, + ROLE_IDS.MCP_APPS_SDK, + ], + }, { github: 'localden', discord: '1351224014143754260', From 52112dfb4ff1bf54fac08e796fac4f94f4e43771 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 15 Jan 2026 13:50:42 +0000 Subject: [PATCH 050/196] Add Discord IDs for 30 users Automated matching found 18 users by username/display name: - CodeWithKyrian, Kehrlann, Kludex, Nyholm, asklar, baxen - carlpeaslee, evalstate, felixweinberger, jba, jerome3o-anthropic - joan-anthropic, jonathanhefner, ochafik, olaservo, stallent - tadasant, topherbullock Manually provided IDs for 12 users: - D-McAdams, a-akimov, aaronpk, atesgoral, domdomegg, findleyr - koic, mikekistler, pederhp, sambhav, toby, tzolov Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 2 Claude-Permission-Prompts: 7 Claude-Escapes: 0 --- src/config/users.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 55329b2..97e1c7b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -9,22 +9,27 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'CodeWithKyrian', + discord: '951883230250946633', memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'D-McAdams', + discord: '1364696680980545697', memberOf: [ROLE_IDS.AUTH_WG], }, { github: 'Kehrlann', + discord: '1112624611901837373', memberOf: [ROLE_IDS.JAVA_SDK], }, { github: 'Kludex', + discord: '247021664624312322', memberOf: [ROLE_IDS.PYTHON_SDK], }, { github: 'Nyholm', + discord: '466593085984342016', memberOf: [ROLE_IDS.PHP_SDK], }, { @@ -33,10 +38,12 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'a-akimov', + discord: '1365254196621738116', memberOf: [ROLE_IDS.DOCS_MAINTAINERS], }, { github: 'aaronpk', + discord: '324624369428987905', memberOf: [ROLE_IDS.AUTH_WG], }, { @@ -54,14 +61,17 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'asklar', + discord: '633837375734153216', memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'atesgoral', + discord: '201179934775836672', memberOf: [ROLE_IDS.RUBY_SDK], }, { github: 'baxen', + discord: '360224027769307136', memberOf: [ROLE_IDS.RUST_SDK], }, { @@ -83,6 +93,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'carlpeaslee', + discord: '288330838951723009', memberOf: [ROLE_IDS.SWIFT_SDK], }, { @@ -119,6 +130,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'domdomegg', email: 'adam@modelcontextprotocol.io', + discord: '102128241715716096', memberOf: [ROLE_IDS.MCPB_MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { @@ -150,6 +162,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'evalstate', + discord: '779268016121577492', memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS], }, { @@ -162,10 +175,12 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'felixweinberger', + discord: '1377138523492057212', memberOf: [ROLE_IDS.PYTHON_SDK, ROLE_IDS.SECURITY_WG, ROLE_IDS.TYPESCRIPT_SDK], }, { github: 'findleyr', + discord: '776094836796424213', memberOf: [ROLE_IDS.GO_SDK], }, { @@ -191,6 +206,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'jba', + discord: '773276903364755518', memberOf: [ROLE_IDS.GO_SDK], }, { @@ -199,14 +215,17 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'jerome3o-anthropic', + discord: '222246825397059585', memberOf: [ROLE_IDS.MODERATORS], }, { github: 'joan-anthropic', + discord: '1398403578892128437', memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'jonathanhefner', + discord: '1301960963087663186', memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.RUBY_SDK], }, { @@ -220,6 +239,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'koic', + discord: '880937364208361483', memberOf: [ROLE_IDS.RUBY_SDK], }, { @@ -290,6 +310,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'mikekistler', + discord: '915345005982408754', memberOf: [ROLE_IDS.CSHARP_SDK], }, { @@ -303,6 +324,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ochafik', + discord: '1004897332069925024', memberOf: [ ROLE_IDS.PYTHON_SDK, ROLE_IDS.PYTHON_SDK_AUTH, @@ -316,6 +338,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'olaservo', + discord: '1079841769946095620', memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], }, { @@ -332,6 +355,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'pederhp', + discord: '166255967665651713', memberOf: [ROLE_IDS.FINANCIAL_SERVICES_IG], }, { @@ -355,6 +379,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'sambhav', + discord: '840109459212206090', memberOf: [ROLE_IDS.FINANCIAL_SERVICES_IG], }, { @@ -371,6 +396,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'stallent', + discord: '1137898074086314136', memberOf: [ROLE_IDS.SWIFT_SDK], }, { @@ -393,14 +419,17 @@ export const MEMBERS: readonly Member[] = [ { github: 'toby', email: 'toby@modelcontextprotocol.io', + discord: '560155411777323048', memberOf: [ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'topherbullock', + discord: '1059910719124013168', memberOf: [ROLE_IDS.RUBY_SDK], }, { github: 'tzolov', + discord: '1097924660055777290', memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.JAVA_SDK], }, { From a667c4306adc2f0abe36e3648a97fc51ecc03ef2 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 15 Jan 2026 14:16:06 +0000 Subject: [PATCH 051/196] Add Discord role inheritance via parent traversal and discordImplies - Add discordImplies field to Role interface for Discord-specific role hierarchy - Add expandDiscordRoles() function to traverse both GitHub parent chain and discordImplies relationships when syncing Discord roles - SDK_MAINTAINERS now implies MAINTAINERS for Discord This ensures that users with SDK roles (GO_SDK, PYTHON_SDK, etc.) automatically get the 'maintainers (synced)' Discord role through: GO_SDK -> SDK_MAINTAINERS (via GitHub parent) -> MAINTAINERS (via discordImplies) Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 5 Claude-Permission-Prompts: 1 Claude-Escapes: 0 --- src/config/roles.ts | 7 +++++++ src/discord.ts | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/config/roles.ts b/src/config/roles.ts index 38515d0..a97d44b 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -38,6 +38,12 @@ export interface Role { github?: GitHubConfig; discord?: DiscordConfig; google?: GoogleConfig; + /** + * Roles that are implied for Discord membership. + * If a user has this role, they automatically get the implied roles' Discord roles too. + * This is separate from GitHub parent relationships and allows Discord-specific hierarchy. + */ + discordImplies?: readonly RoleId[]; } /** @@ -135,6 +141,7 @@ export const ROLES: readonly Role[] = [ description: 'Authors and maintainers of official MCP SDKs', github: { team: 'sdk-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, discord: { role: 'sdk maintainers (synced)' }, + discordImplies: [ROLE_IDS.MAINTAINERS], // SDK maintainers are also general maintainers }, { id: ROLE_IDS.CSHARP_SDK, diff --git a/src/discord.ts b/src/discord.ts index 9ef4f43..d08f17a 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -398,6 +398,38 @@ const roleLookup = buildRoleLookup(); // Discord roles keyed by Discord role name const roles: Record = {}; +/** + * Expand a set of role IDs to include all implied Discord roles. + * This traverses: + * 1. GitHub parent relationships (e.g., GO_SDK -> SDK_MAINTAINERS) + * 2. discordImplies relationships (e.g., SDK_MAINTAINERS -> MAINTAINERS) + */ +function expandDiscordRoles(roleIds: readonly RoleId[]): Set { + const expanded = new Set(); + const toProcess = [...roleIds]; + + while (toProcess.length > 0) { + const roleId = toProcess.pop()!; + if (expanded.has(roleId)) continue; + expanded.add(roleId); + + const role = roleLookup.get(roleId); + if (!role) continue; + + // Follow GitHub parent relationship + if (role.github?.parent) { + toProcess.push(role.github.parent); + } + + // Follow discordImplies relationships + if (role.discordImplies) { + toProcess.push(...role.discordImplies); + } + } + + return expanded; +} + // Only create Discord resources if Discord is enabled if (DISCORD_ENABLED) { // These are guaranteed to be defined when DISCORD_ENABLED is true @@ -424,8 +456,11 @@ if (DISCORD_ENABLED) { MEMBERS.forEach((member) => { if (!member.discord) return; + // Expand roles to include parents and implied roles + const expandedRoleIds = expandDiscordRoles(member.memberOf); + // Get the Discord role IDs this member should have - const expectedRoleIds = member.memberOf + const expectedRoleIds = Array.from(expandedRoleIds) .map((roleId: RoleId) => { const role = roleLookup.get(roleId); if (!role?.discord) return null; From b6eba69c6e336737f2a8a261bb995ca47862d608 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 15 Jan 2026 14:50:40 +0000 Subject: [PATCH 052/196] Remove redundant MAINTAINERS from SDK users (now implied) Since SDK_MAINTAINERS now implies MAINTAINERS via discordImplies, users with SDK roles no longer need explicit MAINTAINERS membership. Removed redundant MAINTAINERS from: - alexhancock (has RUST_SDK) - chr-hertel (has PHP_SDK) - ihrpr (has PYTHON_SDK, TYPESCRIPT_SDK) - liady (has MCP_APPS_SDK) --- src/config/users.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 97e1c7b..e7a3a55 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -49,7 +49,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'alexhancock', discord: '1325885093343924316', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.RUST_SDK], + memberOf: [ROLE_IDS.RUST_SDK], }, { github: 'an-dustin', @@ -104,7 +104,7 @@ export const MEMBERS: readonly Member[] = [ github: 'chr-hertel', email: 'mail@christopher-hertel.de', discord: '633566986827464704', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.PHP_SDK], + memberOf: [ROLE_IDS.PHP_SDK], }, { github: 'cliffhall', @@ -193,12 +193,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'ihrpr', - memberOf: [ - ROLE_IDS.MAINTAINERS, - ROLE_IDS.DOCS_MAINTAINERS, - ROLE_IDS.PYTHON_SDK, - ROLE_IDS.TYPESCRIPT_SDK, - ], + memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.PYTHON_SDK, ROLE_IDS.TYPESCRIPT_SDK], }, { github: 'jamadeo', @@ -254,12 +249,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'liady', discord: '383565833768665088', - memberOf: [ - ROLE_IDS.MAINTAINERS, - ROLE_IDS.WORKING_GROUPS, - ROLE_IDS.MCP_APPS_WG, - ROLE_IDS.MCP_APPS_SDK, - ], + memberOf: [ROLE_IDS.WORKING_GROUPS, ROLE_IDS.MCP_APPS_WG, ROLE_IDS.MCP_APPS_SDK], }, { github: 'localden', From 18253ff1a6bc5ebfe0132fc963b6e5e498bbaa3f Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Thu, 15 Jan 2026 17:01:54 -0500 Subject: [PATCH 053/196] Add maciej to go-sdk maintainers; discord ID for findleyr (#34) * Add maciej to go-sdk maintainers; discord ID for findleyr Maciej is joining the project from Google. Add him as a go-sdk maintainer. Also, add a discord ID for myself. * Update src/config/users.ts Co-authored-by: Maciej Kisiel --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Co-authored-by: Maciej Kisiel --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index e7a3a55..c76a8c9 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -266,6 +266,11 @@ export const MEMBERS: readonly Member[] = [ discord: '1366470072729866252', memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.AGENTS_IG, ROLE_IDS.WORKING_GROUPS], }, + { + github: 'maciej-kisiel', + discord: '936242781733654588', + memberOf: [ROLE_IDS.GO_SDK], + }, { github: 'maheshmurag', memberOf: [ROLE_IDS.MODERATORS], From d12dc886c63d411722aaac23cd027555066490a8 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 15 Jan 2026 22:22:36 +0000 Subject: [PATCH 054/196] Add repo access management for ext-apps, use-mcp, example-remote-client - Add tarekgh to csharp-sdk with push permission - Add ext-apps with mcp-apps-wg, mcp-apps-sdk teams and outside collaborators - Add use-mcp with core-maintainers, moderators and geelen - Add example-remote-client with core-maintainers, moderators and outside collaborators This brings these previously unmanaged repos under centralized access control. --- src/config/repoAccess.ts | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 3434d95..874b24d 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -205,6 +205,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jozkee', permission: 'admin' }, { username: 'localden', permission: 'admin' }, { username: 'PederHP', permission: 'triage' }, + { username: 'tarekgh', permission: 'push' }, ], }, { @@ -307,6 +308,44 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'ext-auth', teams: [{ team: 'auth-wg', permission: 'admin' }], }, + { + repository: 'ext-apps', + teams: [ + { team: 'core-maintainers', permission: 'push' }, + { team: 'moderators', permission: 'triage' }, + { team: 'mcp-apps-wg', permission: 'admin' }, + { team: 'mcp-apps-sdk', permission: 'admin' }, + ], + users: [ + { username: 'liady', permission: 'admin' }, + { username: 'idosal', permission: 'admin' }, + { username: 'ststrong', permission: 'admin' }, + { username: 'martinalong', permission: 'push' }, + { username: 'antonpk1', permission: 'admin' }, + { username: 'conorkel', permission: 'admin' }, + { username: 'alexi-openai', permission: 'admin' }, + ], + }, + { + repository: 'use-mcp', + teams: [ + { team: 'core-maintainers', permission: 'push' }, + { team: 'moderators', permission: 'triage' }, + ], + users: [{ username: 'geelen', permission: 'admin' }], + }, + { + repository: 'example-remote-client', + teams: [ + { team: 'core-maintainers', permission: 'push' }, + { team: 'moderators', permission: 'triage' }, + ], + users: [ + { username: 'geelen', permission: 'push' }, + { username: 'markyfyi', permission: 'push' }, + { username: 'jerryhong1', permission: 'push' }, + ], + }, ]; // GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet From e36544e3afe55b1f2eb3a8ca162520fddc16d94c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 15 Jan 2026 22:32:35 +0000 Subject: [PATCH 055/196] Fix ext-apps: mcp-apps-wg should have push, not admin --- scripts/audit-discord-roles.ts | 220 +++++++++++++++++++++++++++++++++ src/config/repoAccess.ts | 2 +- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 + src/config/users.ts | 11 +- 5 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 scripts/audit-discord-roles.ts diff --git a/scripts/audit-discord-roles.ts b/scripts/audit-discord-roles.ts new file mode 100644 index 0000000..c4115c0 --- /dev/null +++ b/scripts/audit-discord-roles.ts @@ -0,0 +1,220 @@ +/** + * Script to audit Discord roles against the repo configuration. + * + * For each user defined in the repo with a Discord ID, this script: + * 1. Gets their actual Discord roles + * 2. Compares against expected roles based on their memberOf + * 3. Reports discrepancies + * + * Run with: npx ts-node scripts/audit-discord-roles.ts + */ + +import { MEMBERS } from '../src/config/users'; +import { ROLES, getRole } from '../src/config/roles'; +import type { RoleId } from '../src/config/roleIds'; + +const BOT_TOKEN = process.env.DISCORD_BOT_TOKEN || ''; +const GUILD_ID = process.env.DISCORD_GUILD_ID || ''; + +interface DiscordRole { + id: string; + name: string; +} + +interface DiscordMember { + user: { id: string; username: string; global_name: string }; + roles: string[]; +} + +// Build role name -> id lookup from Discord +const discordRoleNameToId = new Map(); +const discordRoleIdToName = new Map(); + +// Build expected role name -> RoleId lookup from config +const syncedRoleNameToRoleId = new Map(); + +async function fetchJson(url: string): Promise { + const response = await fetch(url, { + headers: { Authorization: `Bot ${BOT_TOKEN}` }, + }); + if (!response.ok) { + const error = await response.text(); + throw new Error(`Discord API error: ${response.status} ${error}`); + } + return response.json(); +} + +async function getDiscordRoles(): Promise { + return fetchJson(`https://discord.com/api/v10/guilds/${GUILD_ID}/roles`); +} + +async function getDiscordMember(discordId: string): Promise { + try { + return await fetchJson(`https://discord.com/api/v10/guilds/${GUILD_ID}/members/${discordId}`); + } catch (e) { + return null; + } +} + +/** + * Expand role IDs to include all Discord-implied roles + * (GitHub parent traversal + discordImplies) + */ +function expandDiscordRoles(roleIds: readonly RoleId[]): Set { + const expanded = new Set(); + const queue = [...roleIds]; + + while (queue.length > 0) { + const roleId = queue.shift()!; + if (expanded.has(roleId)) continue; + + const role = getRole(roleId); + if (!role) continue; + + expanded.add(roleId); + + // Add parent role (GitHub hierarchy) + if (role.github?.parent) { + queue.push(role.github.parent); + } + + // Add discord-implied roles + if (role.discordImplies) { + queue.push(...role.discordImplies); + } + } + + return expanded; +} + +async function main() { + if (!BOT_TOKEN || !GUILD_ID) { + console.error('Set DISCORD_BOT_TOKEN and DISCORD_GUILD_ID environment variables'); + process.exit(1); + } + + console.log('Fetching Discord roles...'); + const discordRoles = await getDiscordRoles(); + + // Build role lookups + for (const role of discordRoles) { + discordRoleNameToId.set(role.name, role.id); + discordRoleIdToName.set(role.id, role.name); + } + + // Build synced role name -> RoleId lookup + for (const role of ROLES) { + if (role.discord?.role) { + syncedRoleNameToRoleId.set(role.discord.role, role.id); + } + } + + // Map manual role names to their synced equivalents + const manualToSynced = new Map(); + for (const role of discordRoles) { + const syncedName = `${role.name} (synced)`; + if (discordRoleNameToId.has(syncedName)) { + manualToSynced.set(role.name, syncedName); + } + } + + console.log('\n=== Discord Roles Summary ==='); + console.log(`Total roles: ${discordRoles.length}`); + console.log(`Synced roles: ${discordRoles.filter(r => r.name.endsWith('(synced)')).length}`); + console.log(`Manual roles with synced equivalent: ${manualToSynced.size}`); + + console.log('\n=== Auditing Users ===\n'); + + const issues: string[] = []; + const membersWithDiscord = MEMBERS.filter(m => m.discord); + + for (const member of membersWithDiscord) { + const discordMember = await getDiscordMember(member.discord!); + if (!discordMember) { + issues.push(`❌ ${member.github || member.email}: Discord ID ${member.discord} not found in guild`); + continue; + } + + const username = discordMember.user.global_name || discordMember.user.username; + + // Get actual role names + const actualRoleNames = new Set( + discordMember.roles.map(id => discordRoleIdToName.get(id) || id) + ); + + // Get expected synced role names based on memberOf + const expandedRoleIds = expandDiscordRoles(member.memberOf); + const expectedSyncedRoles = new Set(); + const expectedManualRoles = new Set(); + + for (const roleId of expandedRoleIds) { + const role = getRole(roleId); + if (role?.discord?.role) { + expectedSyncedRoles.add(role.discord.role); + // Also expect the manual equivalent if it exists + const manualName = role.discord.role.replace(' (synced)', ''); + if (discordRoleNameToId.has(manualName)) { + expectedManualRoles.add(manualName); + } + } + } + + // Check for missing synced roles + const missingSyncedRoles: string[] = []; + for (const expectedRole of expectedSyncedRoles) { + if (!actualRoleNames.has(expectedRole)) { + missingSyncedRoles.push(expectedRole); + } + } + + // Check for manual roles without synced equivalent + const manualWithoutSynced: string[] = []; + for (const roleName of actualRoleNames) { + if (roleName.endsWith('(synced)')) continue; + if (roleName === '@everyone') continue; + + const syncedEquivalent = `${roleName} (synced)`; + if (discordRoleNameToId.has(syncedEquivalent)) { + // This is a manual role that has a synced equivalent + if (!actualRoleNames.has(syncedEquivalent)) { + manualWithoutSynced.push(`${roleName} (has no ${syncedEquivalent})`); + } + } + } + + if (missingSyncedRoles.length > 0 || manualWithoutSynced.length > 0) { + console.log(`\n👤 ${username} (${member.github || member.email})`); + console.log(` Discord ID: ${member.discord}`); + + if (missingSyncedRoles.length > 0) { + console.log(` ❌ Missing synced roles:`); + for (const role of missingSyncedRoles) { + console.log(` - ${role}`); + issues.push(`${username}: Missing ${role}`); + } + } + + if (manualWithoutSynced.length > 0) { + console.log(` ⚠️ Manual roles without synced equivalent:`); + for (const role of manualWithoutSynced) { + console.log(` - ${role}`); + issues.push(`${username}: ${role}`); + } + } + } else { + console.log(`✅ ${username} (${member.github || member.email})`); + } + } + + console.log('\n\n=== Summary ==='); + if (issues.length === 0) { + console.log('✅ All users have matching roles!'); + } else { + console.log(`❌ Found ${issues.length} issues:\n`); + for (const issue of issues) { + console.log(` - ${issue}`); + } + } +} + +main().catch(console.error); diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 874b24d..69c6939 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -313,7 +313,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ teams: [ { team: 'core-maintainers', permission: 'push' }, { team: 'moderators', permission: 'triage' }, - { team: 'mcp-apps-wg', permission: 'admin' }, + { team: 'mcp-apps-wg', permission: 'push' }, { team: 'mcp-apps-sdk', permission: 'admin' }, ], users: [ diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index b560bc4..d54be78 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -11,6 +11,7 @@ export const ROLE_IDS = { LEAD_MAINTAINERS: 'lead-maintainers', MODERATORS: 'moderators', ADMINISTRATORS: 'administrators', // Discord only + COMMUNITY_MANAGERS: 'community-managers', // Discord only // =================== // Maintainer Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index a97d44b..2f349da 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -66,6 +66,12 @@ export const ROLES: readonly Role[] = [ discord: { role: 'administrators (synced)' }, // Discord only - no GitHub equivalent }, + { + id: ROLE_IDS.COMMUNITY_MANAGERS, + description: 'Discord community managers', + discord: { role: 'community managers (synced)' }, + // Discord only - no GitHub equivalent + }, { id: ROLE_IDS.LEAD_MAINTAINERS, description: 'Lead core maintainers', diff --git a/src/config/users.ts b/src/config/users.ts index c76a8c9..36f609e 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -111,6 +111,7 @@ export const MEMBERS: readonly Member[] = [ email: 'cliff@futurescale.com', discord: '501498061965754380', memberOf: [ + ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, @@ -163,7 +164,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'evalstate', discord: '779268016121577492', - memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS], + memberOf: [ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS], }, { github: 'fabpot', @@ -271,6 +272,11 @@ export const MEMBERS: readonly Member[] = [ discord: '936242781733654588', memberOf: [ROLE_IDS.GO_SDK], }, + { + github: 'macoughl', + discord: '740279257548193803', + memberOf: [ROLE_IDS.COMMUNITY_MANAGERS], + }, { github: 'maheshmurag', memberOf: [ROLE_IDS.MODERATORS], @@ -334,7 +340,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'olaservo', discord: '1079841769946095620', - memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], + memberOf: [ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], }, { github: 'pcarleton', @@ -399,6 +405,7 @@ export const MEMBERS: readonly Member[] = [ discord: '400092503677599754', email: 'tadas@modelcontextprotocol.io', memberOf: [ + ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.MODERATORS, ROLE_IDS.MAINTAINERS, ROLE_IDS.WORKING_GROUPS, From 94a5b31e0b1e835de85c46c5b2f069ccdc7f107f Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 16 Jan 2026 11:14:23 +0000 Subject: [PATCH 056/196] remove unused scripts --- scripts/audit-discord-roles.ts | 220 --------------------------------- 1 file changed, 220 deletions(-) delete mode 100644 scripts/audit-discord-roles.ts diff --git a/scripts/audit-discord-roles.ts b/scripts/audit-discord-roles.ts deleted file mode 100644 index c4115c0..0000000 --- a/scripts/audit-discord-roles.ts +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Script to audit Discord roles against the repo configuration. - * - * For each user defined in the repo with a Discord ID, this script: - * 1. Gets their actual Discord roles - * 2. Compares against expected roles based on their memberOf - * 3. Reports discrepancies - * - * Run with: npx ts-node scripts/audit-discord-roles.ts - */ - -import { MEMBERS } from '../src/config/users'; -import { ROLES, getRole } from '../src/config/roles'; -import type { RoleId } from '../src/config/roleIds'; - -const BOT_TOKEN = process.env.DISCORD_BOT_TOKEN || ''; -const GUILD_ID = process.env.DISCORD_GUILD_ID || ''; - -interface DiscordRole { - id: string; - name: string; -} - -interface DiscordMember { - user: { id: string; username: string; global_name: string }; - roles: string[]; -} - -// Build role name -> id lookup from Discord -const discordRoleNameToId = new Map(); -const discordRoleIdToName = new Map(); - -// Build expected role name -> RoleId lookup from config -const syncedRoleNameToRoleId = new Map(); - -async function fetchJson(url: string): Promise { - const response = await fetch(url, { - headers: { Authorization: `Bot ${BOT_TOKEN}` }, - }); - if (!response.ok) { - const error = await response.text(); - throw new Error(`Discord API error: ${response.status} ${error}`); - } - return response.json(); -} - -async function getDiscordRoles(): Promise { - return fetchJson(`https://discord.com/api/v10/guilds/${GUILD_ID}/roles`); -} - -async function getDiscordMember(discordId: string): Promise { - try { - return await fetchJson(`https://discord.com/api/v10/guilds/${GUILD_ID}/members/${discordId}`); - } catch (e) { - return null; - } -} - -/** - * Expand role IDs to include all Discord-implied roles - * (GitHub parent traversal + discordImplies) - */ -function expandDiscordRoles(roleIds: readonly RoleId[]): Set { - const expanded = new Set(); - const queue = [...roleIds]; - - while (queue.length > 0) { - const roleId = queue.shift()!; - if (expanded.has(roleId)) continue; - - const role = getRole(roleId); - if (!role) continue; - - expanded.add(roleId); - - // Add parent role (GitHub hierarchy) - if (role.github?.parent) { - queue.push(role.github.parent); - } - - // Add discord-implied roles - if (role.discordImplies) { - queue.push(...role.discordImplies); - } - } - - return expanded; -} - -async function main() { - if (!BOT_TOKEN || !GUILD_ID) { - console.error('Set DISCORD_BOT_TOKEN and DISCORD_GUILD_ID environment variables'); - process.exit(1); - } - - console.log('Fetching Discord roles...'); - const discordRoles = await getDiscordRoles(); - - // Build role lookups - for (const role of discordRoles) { - discordRoleNameToId.set(role.name, role.id); - discordRoleIdToName.set(role.id, role.name); - } - - // Build synced role name -> RoleId lookup - for (const role of ROLES) { - if (role.discord?.role) { - syncedRoleNameToRoleId.set(role.discord.role, role.id); - } - } - - // Map manual role names to their synced equivalents - const manualToSynced = new Map(); - for (const role of discordRoles) { - const syncedName = `${role.name} (synced)`; - if (discordRoleNameToId.has(syncedName)) { - manualToSynced.set(role.name, syncedName); - } - } - - console.log('\n=== Discord Roles Summary ==='); - console.log(`Total roles: ${discordRoles.length}`); - console.log(`Synced roles: ${discordRoles.filter(r => r.name.endsWith('(synced)')).length}`); - console.log(`Manual roles with synced equivalent: ${manualToSynced.size}`); - - console.log('\n=== Auditing Users ===\n'); - - const issues: string[] = []; - const membersWithDiscord = MEMBERS.filter(m => m.discord); - - for (const member of membersWithDiscord) { - const discordMember = await getDiscordMember(member.discord!); - if (!discordMember) { - issues.push(`❌ ${member.github || member.email}: Discord ID ${member.discord} not found in guild`); - continue; - } - - const username = discordMember.user.global_name || discordMember.user.username; - - // Get actual role names - const actualRoleNames = new Set( - discordMember.roles.map(id => discordRoleIdToName.get(id) || id) - ); - - // Get expected synced role names based on memberOf - const expandedRoleIds = expandDiscordRoles(member.memberOf); - const expectedSyncedRoles = new Set(); - const expectedManualRoles = new Set(); - - for (const roleId of expandedRoleIds) { - const role = getRole(roleId); - if (role?.discord?.role) { - expectedSyncedRoles.add(role.discord.role); - // Also expect the manual equivalent if it exists - const manualName = role.discord.role.replace(' (synced)', ''); - if (discordRoleNameToId.has(manualName)) { - expectedManualRoles.add(manualName); - } - } - } - - // Check for missing synced roles - const missingSyncedRoles: string[] = []; - for (const expectedRole of expectedSyncedRoles) { - if (!actualRoleNames.has(expectedRole)) { - missingSyncedRoles.push(expectedRole); - } - } - - // Check for manual roles without synced equivalent - const manualWithoutSynced: string[] = []; - for (const roleName of actualRoleNames) { - if (roleName.endsWith('(synced)')) continue; - if (roleName === '@everyone') continue; - - const syncedEquivalent = `${roleName} (synced)`; - if (discordRoleNameToId.has(syncedEquivalent)) { - // This is a manual role that has a synced equivalent - if (!actualRoleNames.has(syncedEquivalent)) { - manualWithoutSynced.push(`${roleName} (has no ${syncedEquivalent})`); - } - } - } - - if (missingSyncedRoles.length > 0 || manualWithoutSynced.length > 0) { - console.log(`\n👤 ${username} (${member.github || member.email})`); - console.log(` Discord ID: ${member.discord}`); - - if (missingSyncedRoles.length > 0) { - console.log(` ❌ Missing synced roles:`); - for (const role of missingSyncedRoles) { - console.log(` - ${role}`); - issues.push(`${username}: Missing ${role}`); - } - } - - if (manualWithoutSynced.length > 0) { - console.log(` ⚠️ Manual roles without synced equivalent:`); - for (const role of manualWithoutSynced) { - console.log(` - ${role}`); - issues.push(`${username}: ${role}`); - } - } - } else { - console.log(`✅ ${username} (${member.github || member.email})`); - } - } - - console.log('\n\n=== Summary ==='); - if (issues.length === 0) { - console.log('✅ All users have matching roles!'); - } else { - console.log(`❌ Found ${issues.length} issues:\n`); - for (const issue of issues) { - console.log(` - ${issue}`); - } - } -} - -main().catch(console.error); From f9c122e1674770a02666a5587ed69ebcbad403bc Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Mon, 19 Jan 2026 15:15:45 +0000 Subject: [PATCH 057/196] Add MCP_APPS_SDK role for ochafik (ext-apps access) (#35) Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 1 Claude-Permission-Prompts: 0 Claude-Escapes: 0 --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index 36f609e..fc294d0 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -327,6 +327,7 @@ export const MEMBERS: readonly Member[] = [ github: 'ochafik', discord: '1004897332069925024', memberOf: [ + ROLE_IDS.MCP_APPS_SDK, ROLE_IDS.PYTHON_SDK, ROLE_IDS.PYTHON_SDK_AUTH, ROLE_IDS.TYPESCRIPT_SDK, From ee35c8690787a6c516e29bb07329249682f69965 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Wed, 21 Jan 2026 07:41:15 -0600 Subject: [PATCH 058/196] Add MCP_APPS_SDK role for jonathanhefner (ext-apps access) (#37) Adds the `MCP_APPS_SDK` role for `jonathanhefner` to grant admin access to the `ext-apps` repository. --- src/config/users.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index fc294d0..d394c9b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -222,7 +222,12 @@ export const MEMBERS: readonly Member[] = [ { github: 'jonathanhefner', discord: '1301960963087663186', - memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.RUBY_SDK], + memberOf: [ + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.MODERATORS, + ROLE_IDS.RUBY_SDK, + ROLE_IDS.MCP_APPS_SDK, + ], }, { github: 'jspahrsummers', From 050c9b09f4e845022ec9d38d46bd4275842d4e9c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 22 Jan 2026 11:10:33 +0000 Subject: [PATCH 059/196] Add GitHub user 'dsp' with same access rights as dsp-ant --- src/config/users.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index d394c9b..c5b5e29 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -153,6 +153,23 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.TYPESCRIPT_SDK, ], }, + { + github: 'dsp', + memberOf: [ + ROLE_IDS.AUTH_WG, + ROLE_IDS.LEAD_MAINTAINERS, + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.GO_SDK, + ROLE_IDS.FINANCIAL_SERVICES_IG, + ROLE_IDS.MODERATORS, + ROLE_IDS.PHP_SDK, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.SECURITY_WG, + ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.TYPESCRIPT_SDK, + ], + }, { github: 'e5l', memberOf: [ROLE_IDS.KOTLIN_SDK], From 46c864c0bccb6a7bce1c60e04c11cfa7f655867f Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Thu, 22 Jan 2026 20:21:27 +0000 Subject: [PATCH 060/196] Add pja-ant (Peter Alexander) as core maintainer (#40) * Add pja-ant (Peter Alexander) as core maintainer * Add validation for alphabetical member ordering in users.ts - Added validation in validate-config.ts that checks: 1. GitHub members are sorted alphabetically (case-insensitive) 2. Email-only members come after all GitHub members - Sorted existing users.ts entries to pass the new validation --- scripts/validate-config.ts | 43 +++++++++++++++ src/config/users.ts | 105 ++++++++++++++++++++----------------- 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/scripts/validate-config.ts b/scripts/validate-config.ts index 59d8a78..b824b3c 100644 --- a/scripts/validate-config.ts +++ b/scripts/validate-config.ts @@ -55,6 +55,49 @@ for (const member of MEMBERS) { } } +// Validate member ordering in users.ts +console.log('Validating member ordering in users.ts...'); +{ + // Split members into github members and email-only members + const githubMembers: (typeof MEMBERS)[number][] = []; + const emailOnlyMembers: (typeof MEMBERS)[number][] = []; + + for (const member of MEMBERS) { + if (member.github) { + githubMembers.push(member); + } else if (member.email) { + emailOnlyMembers.push(member); + } + } + + // Check that github members come before email-only members + let foundEmailOnly = false; + for (const member of MEMBERS) { + if (!member.github && member.email) { + foundEmailOnly = true; + } else if (member.github && foundEmailOnly) { + console.error( + `ERROR: Member "${member.github}" appears after email-only members. GitHub members should come before email-only members.` + ); + hasErrors = true; + break; + } + } + + // Check that github members are sorted alphabetically (case-insensitive) + for (let i = 1; i < githubMembers.length; i++) { + const prev = githubMembers[i - 1].github!.toLowerCase(); + const curr = githubMembers[i].github!.toLowerCase(); + if (prev > curr) { + console.error( + `ERROR: Members are not sorted alphabetically. "${githubMembers[i - 1].github}" should come after "${githubMembers[i].github}".` + ); + hasErrors = true; + break; + } + } +} + // Validate parent role references in roles.ts console.log('Validating parent role references in roles.ts...'); for (const role of ROLES) { diff --git a/src/config/users.ts b/src/config/users.ts index c5b5e29..1f7a7fe 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -7,35 +7,6 @@ export const MEMBERS: readonly Member[] = [ discord: '1360717264051241071', memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, - { - github: 'CodeWithKyrian', - discord: '951883230250946633', - memberOf: [ROLE_IDS.PHP_SDK], - }, - { - github: 'D-McAdams', - discord: '1364696680980545697', - memberOf: [ROLE_IDS.AUTH_WG], - }, - { - github: 'Kehrlann', - discord: '1112624611901837373', - memberOf: [ROLE_IDS.JAVA_SDK], - }, - { - github: 'Kludex', - discord: '247021664624312322', - memberOf: [ROLE_IDS.PYTHON_SDK], - }, - { - github: 'Nyholm', - discord: '466593085984342016', - memberOf: [ROLE_IDS.PHP_SDK], - }, - { - github: 'Ololoshechkin', - memberOf: [ROLE_IDS.KOTLIN_SDK], - }, { github: 'a-akimov', discord: '1365254196621738116', @@ -120,10 +91,20 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.WORKING_GROUPS, ], }, + { + github: 'CodeWithKyrian', + discord: '951883230250946633', + memberOf: [ROLE_IDS.PHP_SDK], + }, { github: 'crondinini-ant', memberOf: [], }, + { + github: 'D-McAdams', + discord: '1364696680980545697', + memberOf: [ROLE_IDS.AUTH_WG], + }, { github: 'devcrocod', memberOf: [ROLE_IDS.KOTLIN_SDK], @@ -135,9 +116,7 @@ export const MEMBERS: readonly Member[] = [ memberOf: [ROLE_IDS.MCPB_MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { - github: 'dsp-ant', - email: 'david@modelcontextprotocol.io', - discord: '166107790262272000', // Example Discord user ID - replace with real ID + github: 'dsp', memberOf: [ ROLE_IDS.AUTH_WG, ROLE_IDS.LEAD_MAINTAINERS, @@ -154,7 +133,9 @@ export const MEMBERS: readonly Member[] = [ ], }, { - github: 'dsp', + github: 'dsp-ant', + email: 'david@modelcontextprotocol.io', + discord: '166107790262272000', memberOf: [ ROLE_IDS.AUTH_WG, ROLE_IDS.LEAD_MAINTAINERS, @@ -251,10 +232,20 @@ export const MEMBERS: readonly Member[] = [ email: 'justin@modelcontextprotocol.io', memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, + { + github: 'Kehrlann', + discord: '1112624611901837373', + memberOf: [ROLE_IDS.JAVA_SDK], + }, { github: 'KKonstantinov', memberOf: [ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.TYPESCRIPT_SDK], }, + { + github: 'Kludex', + discord: '247021664624312322', + memberOf: [ROLE_IDS.PYTHON_SDK], + }, { github: 'koic', discord: '880937364208361483', @@ -315,27 +306,27 @@ export const MEMBERS: readonly Member[] = [ github: 'mattt', memberOf: [ROLE_IDS.SWIFT_SDK], }, - { - github: 'maxisbey', - memberOf: [ROLE_IDS.PYTHON_SDK], - }, { github: 'mattzcarey', memberOf: [ROLE_IDS.TYPESCRIPT_SDK], }, { - github: 'michaelneale', - memberOf: [ROLE_IDS.RUST_SDK], + github: 'maxisbey', + memberOf: [ROLE_IDS.PYTHON_SDK], }, { - github: 'movetz', - memberOf: [ROLE_IDS.SWIFT_SDK], + github: 'michaelneale', + memberOf: [ROLE_IDS.RUST_SDK], }, { github: 'mikekistler', discord: '915345005982408754', memberOf: [ROLE_IDS.CSHARP_SDK], }, + { + github: 'movetz', + memberOf: [ROLE_IDS.SWIFT_SDK], + }, { github: 'nickcoai', discord: '1153783469860732968', @@ -345,6 +336,11 @@ export const MEMBERS: readonly Member[] = [ github: 'nicolas-grekas', memberOf: [ROLE_IDS.PHP_SDK], }, + { + github: 'Nyholm', + discord: '466593085984342016', + memberOf: [ROLE_IDS.PHP_SDK], + }, { github: 'ochafik', discord: '1004897332069925024', @@ -363,7 +359,16 @@ export const MEMBERS: readonly Member[] = [ { github: 'olaservo', discord: '1079841769946095620', - memberOf: [ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MODERATORS], + memberOf: [ + ROLE_IDS.COMMUNITY_MANAGERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.INSPECTOR_MAINTAINERS, + ROLE_IDS.MODERATORS, + ], + }, + { + github: 'Ololoshechkin', + memberOf: [ROLE_IDS.KOTLIN_SDK], }, { github: 'pcarleton', @@ -386,6 +391,10 @@ export const MEMBERS: readonly Member[] = [ github: 'petery-ant', memberOf: [ROLE_IDS.SECURITY_WG], }, + { + github: 'pja-ant', + memberOf: [ROLE_IDS.CORE_MAINTAINERS], + }, { github: 'pronskiy', memberOf: [ROLE_IDS.PHP_SDK], @@ -397,8 +406,8 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'rdimitrov', - discord: '1088231882979815424', email: 'radoslav@modelcontextprotocol.io', + discord: '1088231882979815424', memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { @@ -414,19 +423,19 @@ export const MEMBERS: readonly Member[] = [ github: 'sdubov', memberOf: [ROLE_IDS.KOTLIN_SDK], }, - { - github: 'stephentoub', - memberOf: [ROLE_IDS.CSHARP_SDK], - }, { github: 'stallent', discord: '1137898074086314136', memberOf: [ROLE_IDS.SWIFT_SDK], }, + { + github: 'stephentoub', + memberOf: [ROLE_IDS.CSHARP_SDK], + }, { github: 'tadasant', - discord: '400092503677599754', email: 'tadas@modelcontextprotocol.io', + discord: '400092503677599754', memberOf: [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.MODERATORS, From d97d2e1770b0336790bf92b3c54b4df5c55af06f Mon Sep 17 00:00:00 2001 From: Anton Pidkuiko <105124934+antonpk1@users.noreply.github.com> Date: Mon, 26 Jan 2026 12:22:11 +0000 Subject: [PATCH 061/196] Add MCP_APPS_SDK role for antonpk1 (ext-apps access) (#36) --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 1f7a7fe..ebe3c81 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -30,6 +30,11 @@ export const MEMBERS: readonly Member[] = [ github: 'ansaba', memberOf: [ROLE_IDS.GO_SDK], }, + { + github: 'antonpk1', + discord: '738474760480227358', + memberOf: [ROLE_IDS.MCP_APPS_SDK], + }, { github: 'asklar', discord: '633837375734153216', From 0b8f34a0bcf531d6b2ceedf3bfe8b345a2f9263c Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Mon, 26 Jan 2026 12:25:58 +0000 Subject: [PATCH 062/196] Add pja-ant to transports WG (#23) * Add pja-ant to transports WG Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 1 Claude-Permission-Prompts: 1 Claude-Escapes: 0 * Update src/config/users.ts * format --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Co-authored-by: David Soria Parra --- src/config/users.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index ebe3c81..a8e90d3 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -398,7 +398,8 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'pja-ant', - memberOf: [ROLE_IDS.CORE_MAINTAINERS], + discord: '328628782497923072', + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { github: 'pronskiy', From 6f30c02ee1d00eaebe996660b88c839c41bdfc53 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:27:08 -0800 Subject: [PATCH 063/196] Add aaronpk to maintainers (#39) :house: Remote-Dev: homespace Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index a8e90d3..4bdb0c2 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -15,7 +15,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'aaronpk', discord: '324624369428987905', - memberOf: [ROLE_IDS.AUTH_WG], + memberOf: [ROLE_IDS.AUTH_WG, ROLE_IDS.MAINTAINERS], }, { github: 'alexhancock', From be908cba66d0b25ec7ea3dc0876924a4b4740315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20J=C4=99drzejczyk?= Date: Mon, 26 Jan 2026 13:27:27 +0100 Subject: [PATCH 064/196] Add Discord ID to Java SDK contributor (chemicL) (#41) --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index 4bdb0c2..4175454 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -74,6 +74,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'chemicL', + discord: '1346243721271971923', memberOf: [ROLE_IDS.JAVA_SDK], }, { From 9d88f97a4dd3e15d18b9a4a64dae6206683c2b2d Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 26 Jan 2026 12:26:44 +0000 Subject: [PATCH 065/196] package.json: add format check to "npm run check" --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e23b708..bb4186b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "build": "tsc", "validate": "npx ts-node scripts/validate-config.ts", "test": "npx ts-node scripts/test-config.ts", - "check": "npm run validate && npm run test", + "check": "npm run format:check && npm run validate && npm run test", "format": "prettier --write .", "format:check": "prettier --check ." }, From 768dd1831adf806fd41d269a6b9823ce079bcbb1 Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Tue, 27 Jan 2026 15:05:43 +0000 Subject: [PATCH 066/196] Update role id for FSIG (#45) --- src/config/users.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 4175454..228f199 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -391,7 +391,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'pederhp', discord: '166255967665651713', - memberOf: [ROLE_IDS.FINANCIAL_SERVICES_IG], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG], }, { github: 'petery-ant', @@ -419,8 +419,9 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'sambhav', + email: 'sambhavs.email@gmail.com', discord: '840109459212206090', - memberOf: [ROLE_IDS.FINANCIAL_SERVICES_IG], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG], }, { github: 'samthanawalla', From 0fee2ddc31233a5d5f9778b06da143a81738d178 Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:50:33 +0000 Subject: [PATCH 067/196] Add Discord ID for maxisbey (#44) --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index 228f199..01e2e5e 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -318,6 +318,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'maxisbey', + discord: '1404871241738748058', memberOf: [ROLE_IDS.PYTHON_SDK], }, { From a5663db94f8bffb7201a7c621258465f798c52f9 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:53:20 +0000 Subject: [PATCH 068/196] Update ci.yml --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92415c9..65ea36e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,6 @@ name: CI on: pull_request: - branches: [ main ] permissions: contents: read @@ -49,4 +48,4 @@ jobs: run: npm run validate - name: Check formatting - run: npm run format:check \ No newline at end of file + run: npm run format:check From d06127f4103b036ee46d2fce5fec6eaed2c440d4 Mon Sep 17 00:00:00 2001 From: Ido Salomon Date: Wed, 28 Jan 2026 17:29:50 +0200 Subject: [PATCH 069/196] Add Ido Salomon to maintainers (#46) * Add Ido Salomon to maintainers * rebase --------- Co-authored-by: David Soria Parra --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 01e2e5e..cc37528 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -192,6 +192,11 @@ export const MEMBERS: readonly Member[] = [ github: 'halter73', memberOf: [ROLE_IDS.CSHARP_SDK], }, + { + github: 'idosal', + discord: '593070927202484244', + memberOf: [ROLE_IDS.WORKING_GROUPS, ROLE_IDS.MCP_APPS_WG, ROLE_IDS.MCP_APPS_SDK], + }, { github: 'ignatov', memberOf: [ROLE_IDS.KOTLIN_SDK], From 8077a0cb7a7a5a9dd30ddafded10c4a53fc35ad3 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:45:18 +0000 Subject: [PATCH 070/196] Add CODEOWNERS: require core-maintainers for all changes (#47) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9cf3b8b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# All files require approval from core-maintainers +* @modelcontextprotocol/core-maintainers From e44aeebc8a6bd30d0c8a28af5ea6def0eb7c9518 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Wed, 28 Jan 2026 15:47:13 +0000 Subject: [PATCH 071/196] Add WG/IG facilitators role for calendar access (#42) * Add WG/IG facilitators role for calendar access Add a new Discord-only role for Working Group and Interest Group facilitators who need access to manage meetings on meet.modelcontextprotocol.io. Initial members: - Emily Lauber (@EmLauber) - Auth IG OAuth Mixup sub-working group - Nate Barbettini (@nbarbettini) * Fix member ordering - sort alphabetically --- src/config/roleIds.ts | 5 +++++ src/config/roles.ts | 10 ++++++++++ src/config/users.ts | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index d54be78..51129a2 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -62,6 +62,11 @@ export const ROLE_IDS = { FINANCIAL_SERVICES_IG: 'financial-services-ig', GATEWAYS_IG: 'gateways-ig', + // =================== + // WG/IG Facilitators (Discord only) + // =================== + WG_IG_FACILITATORS: 'wg-ig-facilitators', + // =================== // Email Groups (Google only) // =================== diff --git a/src/config/roles.ts b/src/config/roles.ts index 2f349da..84e834b 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -308,6 +308,16 @@ export const ROLES: readonly Role[] = [ discord: { role: 'gateways interest group (synced)' }, }, + // =================== + // WG/IG Facilitators (Discord only) + // =================== + { + id: ROLE_IDS.WG_IG_FACILITATORS, + description: 'Working Group and Interest Group facilitators with calendar access', + discord: { role: 'wg/ig facilitators (synced)' }, + // Discord only - grants meet.modelcontextprotocol.io calendar access + }, + // =================== // Email Groups (Google only) // =================== diff --git a/src/config/users.ts b/src/config/users.ts index cc37528..fcf7fdb 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -165,6 +165,11 @@ export const MEMBERS: readonly Member[] = [ github: 'eiriktsarpalis', memberOf: [ROLE_IDS.CSHARP_SDK], }, + { + github: 'EmLauber', + discord: '1408222390361657426', + memberOf: [ROLE_IDS.WG_IG_FACILITATORS], + }, { github: 'evalstate', discord: '779268016121577492', @@ -339,6 +344,11 @@ export const MEMBERS: readonly Member[] = [ github: 'movetz', memberOf: [ROLE_IDS.SWIFT_SDK], }, + { + github: 'nbarbettini', + discord: '784552628930478090', + memberOf: [ROLE_IDS.WG_IG_FACILITATORS], + }, { github: 'nickcoai', discord: '1153783469860732968', From 850ba722da934bd3fce34fe3171936d8aae1a7e7 Mon Sep 17 00:00:00 2001 From: Konstantin Konstantinov Date: Wed, 28 Jan 2026 19:30:24 +0200 Subject: [PATCH 072/196] add discord id for kkonstantinov (#48) --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index fcf7fdb..4eb2272 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -255,6 +255,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'KKonstantinov', + discord: '390932438903422987', memberOf: [ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.TYPESCRIPT_SDK], }, { From f585e22f5e5d51c84620e9a84823c386f6d81f5d Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 29 Jan 2026 17:30:31 +0000 Subject: [PATCH 073/196] Rename auth-wg to auth-maintainers (#50) --- src/config/repoAccess.ts | 14 +++++++------- src/config/roleIds.ts | 2 +- src/config/roles.ts | 8 ++++---- src/config/users.ts | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 69c6939..d201056 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -18,7 +18,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'docs', users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ - { team: 'auth-wg', permission: 'push' }, + { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintainers', permission: 'push' }, @@ -48,7 +48,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: '.github', users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ - { team: 'auth-wg', permission: 'triage' }, + { team: 'auth-maintainers', permission: 'triage' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'triage' }, { team: 'docs-maintainers', permission: 'triage' }, @@ -84,7 +84,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], teams: [ { team: 'inspector-maintainers', permission: 'push' }, - { team: 'auth-wg', permission: 'push' }, + { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'go-sdk', permission: 'push' }, @@ -110,7 +110,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'modelcontextprotocol', users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ - { team: 'auth-wg', permission: 'push' }, + { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'triage' }, { team: 'docs-maintainers', permission: 'push' }, @@ -140,7 +140,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'quickstart-resources', users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ - { team: 'auth-wg', permission: 'push' }, + { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintainers', permission: 'push' }, @@ -173,7 +173,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'slimslenderslacks', permission: 'push' }, ], teams: [ - { team: 'auth-wg', permission: 'push' }, + { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'admin' }, { team: 'csharp-sdk', permission: 'push' }, { team: 'docs-maintainers', permission: 'push' }, @@ -306,7 +306,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'ext-auth', - teams: [{ team: 'auth-wg', permission: 'admin' }], + teams: [{ team: 'auth-maintainers', permission: 'admin' }], }, { repository: 'ext-apps', diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 51129a2..c7384b4 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -46,7 +46,7 @@ export const ROLE_IDS = { // Working Groups // =================== WORKING_GROUPS: 'working-groups', - AUTH_WG: 'auth-wg', + AUTH_MAINTAINERS: 'auth-maintainers', SECURITY_WG: 'security-wg', SERVER_IDENTITY_WG: 'server-identity-wg', TRANSPORT_WG: 'transport-wg', diff --git a/src/config/roles.ts b/src/config/roles.ts index 84e834b..88577e2 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -238,9 +238,9 @@ export const ROLES: readonly Role[] = [ // No discord - organizational container }, { - id: ROLE_IDS.AUTH_WG, - description: 'Authentication Working Group', - github: { team: 'auth-wg', parent: ROLE_IDS.WORKING_GROUPS }, + id: ROLE_IDS.AUTH_MAINTAINERS, + description: 'Auth Maintainers', + github: { team: 'auth-maintainers', parent: ROLE_IDS.WORKING_GROUPS }, // See AUTH_IG for Discord role }, { @@ -287,7 +287,7 @@ export const ROLES: readonly Role[] = [ id: ROLE_IDS.AUTH_IG, description: 'Auth Interest Group', discord: { role: 'auth interest group (synced)' }, - // Discord only - separate from AUTH_WG which is GitHub + // Discord only - separate from AUTH_MAINTAINERS which is GitHub }, { id: ROLE_IDS.CLIENT_IMPLEMENTOR_IG, diff --git a/src/config/users.ts b/src/config/users.ts index 4eb2272..4689d63 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -15,7 +15,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'aaronpk', discord: '324624369428987905', - memberOf: [ROLE_IDS.AUTH_WG, ROLE_IDS.MAINTAINERS], + memberOf: [ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.MAINTAINERS], }, { github: 'alexhancock', @@ -109,7 +109,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'D-McAdams', discord: '1364696680980545697', - memberOf: [ROLE_IDS.AUTH_WG], + memberOf: [ROLE_IDS.AUTH_MAINTAINERS], }, { github: 'devcrocod', @@ -124,7 +124,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'dsp', memberOf: [ - ROLE_IDS.AUTH_WG, + ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, @@ -143,7 +143,7 @@ export const MEMBERS: readonly Member[] = [ email: 'david@modelcontextprotocol.io', discord: '166107790262272000', memberOf: [ - ROLE_IDS.AUTH_WG, + ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, @@ -286,7 +286,7 @@ export const MEMBERS: readonly Member[] = [ github: 'localden', discord: '1351224014143754260', memberOf: [ - ROLE_IDS.AUTH_WG, + ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.CSHARP_SDK, ROLE_IDS.DOCS_MAINTAINERS, @@ -402,7 +402,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.PYTHON_SDK_AUTH, ROLE_IDS.TYPESCRIPT_SDK, ROLE_IDS.TYPESCRIPT_SDK_AUTH, - ROLE_IDS.AUTH_WG, + ROLE_IDS.AUTH_MAINTAINERS, ], }, { From 69deafeb445428a7bd7405857998986ec47d53ed Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Thu, 29 Jan 2026 17:44:38 +0000 Subject: [PATCH 074/196] Add SamMorrowDrums and AUTH_TOOL_SCOPES_WG (#49) * Add SamMorrowDrums and AUTH_TOOL_SCOPES_WG - Add SamMorrowDrums to WORKING_GROUPS, WG_IG_FACILITATORS, INTEREST_GROUPS - Add AUTH_TOOL_SCOPES_WG role for #auth-wg-tool-scopes Discord channel - Add olaservo to AUTH_TOOL_SCOPES_WG * Initial plan * Remove AUTH_TOOL_SCOPES_WG role and update user memberships Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com> --- src/config/users.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 4689d63..5f4e556 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -440,6 +440,12 @@ export const MEMBERS: readonly Member[] = [ discord: '840109459212206090', memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG], }, + { + github: 'SamMorrowDrums', + email: 'sammorrowdrums@github.com', + discord: '782948163694493696', + memberOf: [ROLE_IDS.WG_IG_FACILITATORS], + }, { github: 'samthanawalla', memberOf: [ROLE_IDS.GO_SDK], From 562a8364954450a32c84b68c3042546e04b4c4af Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 2 Feb 2026 19:32:25 +0000 Subject: [PATCH 075/196] Remove bhosmer-ant from core maintainers --- src/config/users.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 5f4e556..5898d69 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -54,7 +54,6 @@ export const MEMBERS: readonly Member[] = [ github: 'bhosmer-ant', discord: '1272295077074567242', memberOf: [ - ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.PYTHON_SDK, From 003fcf41beede775270983f2774ce44f00e6176e Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:09:02 -0800 Subject: [PATCH 076/196] Fix permissions gaps (#51) --- src/config/users.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 5898d69..5cf1a95 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -287,8 +287,17 @@ export const MEMBERS: readonly Member[] = [ memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, - ROLE_IDS.CSHARP_SDK, ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.CSHARP_SDK, + ROLE_IDS.ADMINISTRATORS, + ROLE_IDS.GO_SDK, + ROLE_IDS.FINANCIAL_SERVICES_IG, + ROLE_IDS.MODERATORS, + ROLE_IDS.PHP_SDK, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.SECURITY_WG, + ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.TYPESCRIPT_SDK, ], }, { From ed25517973d2fdebff9d6732f85699c40e64a0bd Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Tue, 3 Feb 2026 06:50:40 -0700 Subject: [PATCH 077/196] Fix permissions (#52) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add evalstate and cliffhall to docs-maintaners team 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Add maintainers, reference-servers, and working-groups roles to olaservo Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 5cf1a95..05717a3 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -394,7 +394,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, + ROLE_IDS.MAINTAINERS, ROLE_IDS.MODERATORS, + ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, + ROLE_IDS.WORKING_GROUPS, ], }, { From 72167e4c52a7e678366b0f814a747c844d83084c Mon Sep 17 00:00:00 2001 From: Maciej Kisiel Date: Tue, 3 Feb 2026 14:52:08 +0100 Subject: [PATCH 078/196] Add herczyn to Go SDK team. (#43) * Add herczyn to Go SDK team. Kuba is another lead from the new team at Google that took over the Go SDK maintenance. * Add discord for herczyn * Clean up inactive maintainers of Go SDK --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 05717a3..f118802 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -26,10 +26,6 @@ export const MEMBERS: readonly Member[] = [ github: 'an-dustin', memberOf: [ROLE_IDS.SECURITY_WG], }, - { - github: 'ansaba', - memberOf: [ROLE_IDS.GO_SDK], - }, { github: 'antonpk1', discord: '738474760480227358', @@ -196,6 +192,11 @@ export const MEMBERS: readonly Member[] = [ github: 'halter73', memberOf: [ROLE_IDS.CSHARP_SDK], }, + { + github: 'herczyn', + discord: '1001427188068917279', + memberOf: [ROLE_IDS.GO_SDK], + }, { github: 'idosal', discord: '593070927202484244', @@ -457,10 +458,6 @@ export const MEMBERS: readonly Member[] = [ discord: '782948163694493696', memberOf: [ROLE_IDS.WG_IG_FACILITATORS], }, - { - github: 'samthanawalla', - memberOf: [ROLE_IDS.GO_SDK], - }, { github: 'sdubov', memberOf: [ROLE_IDS.KOTLIN_SDK], From 8f29a9f0ef3240ea35049d76471645edd20edb6c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:00:08 +0000 Subject: [PATCH 079/196] Import auth-maintainers team into Pulumi state The team exists on GitHub but was removed from Pulumi state, causing deployment failures. This adds a one-time import step. --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ae2b7c8..31dcfbc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -70,4 +70,6 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod + # Import auth-maintainers team that exists on GitHub but was removed from state + pulumi import github:index/team:Team auth-maintainers 14435880 --stack prod --yes || true make up \ No newline at end of file From 0827b6049cdf5f7870d81b431e868f2906b4ea9e Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:02:16 +0000 Subject: [PATCH 080/196] Add workflow_dispatch trigger to deploy workflow --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 31dcfbc..fdd433a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,7 @@ on: push: branches: - main + workflow_dispatch: permissions: contents: read From 6627a01b810cd875dfa4ef2858c01bc25035a4ab Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:04:44 +0000 Subject: [PATCH 081/196] Remove one-time import command (auth-maintainers now in state) --- .github/workflows/deploy.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fdd433a..759c948 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,6 +71,4 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod - # Import auth-maintainers team that exists on GitHub but was removed from state - pulumi import github:index/team:Team auth-maintainers 14435880 --stack prod --yes || true make up \ No newline at end of file From 2d5b2f4f71acc0c4cd0a9fecad2b3a635cbbcf9c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:09:29 +0000 Subject: [PATCH 082/196] Delete stale auth-maintainers resources from Pulumi state The team was accidentally deleted when both auth-wg and auth-maintainers existed in state pointing to the same GitHub team. Need to clear stale state so Pulumi can create fresh resources. --- .github/workflows/deploy.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 759c948..cadd1d1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,4 +71,12 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod + # Delete stale auth-maintainers resources from state (team was deleted, need fresh creation) + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/team:Team::auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::pcarleton-auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::aaronpk-auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::localden-auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::D-McAdams-auth-maintainers' --stack prod --yes || true make up \ No newline at end of file From d694bbbf7de5a9213ce574ba38b1ea7487796080 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:11:36 +0000 Subject: [PATCH 083/196] Fix state delete order and add --force flag --- .github/workflows/deploy.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cadd1d1..5762a09 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -72,11 +72,12 @@ jobs: pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod # Delete stale auth-maintainers resources from state (team was deleted, need fresh creation) - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/team:Team::auth-maintainers' --stack prod --yes || true + # Delete memberships first, then team (with --force for any remaining deps) pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::pcarleton-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::aaronpk-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::localden-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::D-McAdams-auth-maintainers' --stack prod --yes || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/team:Team::auth-maintainers' --stack prod --yes --force || true make up \ No newline at end of file From c147e113f1552bfdd9ed6278b1fc6d7c31947083 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:13:23 +0000 Subject: [PATCH 084/196] Use --target-dependents flag for state delete --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5762a09..c27f356 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -79,5 +79,5 @@ jobs: pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::aaronpk-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::localden-auth-maintainers' --stack prod --yes || true pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::D-McAdams-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/team:Team::auth-maintainers' --stack prod --yes --force || true + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/team:Team::auth-maintainers' --stack prod --yes --target-dependents || true make up \ No newline at end of file From 8dfcc359a06e2ffd60234541df82bce3404aea51 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:17:18 +0000 Subject: [PATCH 085/196] Remove one-time state cleanup commands (auth-maintainers now created) --- .github/workflows/deploy.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c27f356..759c948 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,13 +71,4 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod - # Delete stale auth-maintainers resources from state (team was deleted, need fresh creation) - # Delete memberships first, then team (with --force for any remaining deps) - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::dsp-ant-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::pcarleton-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::aaronpk-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::localden-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/teamMembership:TeamMembership::D-McAdams-auth-maintainers' --stack prod --yes || true - pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/team:Team::auth-maintainers' --stack prod --yes --target-dependents || true make up \ No newline at end of file From 795a74402e516ab72ee483e6f18f9b3da7be2fec Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 3 Feb 2026 15:45:30 +0000 Subject: [PATCH 086/196] Add MAINTAINERS role to evalstate --- src/config/users.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index f118802..d88486d 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -168,7 +168,12 @@ export const MEMBERS: readonly Member[] = [ { github: 'evalstate', discord: '779268016121577492', - memberOf: [ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS], + memberOf: [ + ROLE_IDS.COMMUNITY_MANAGERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.MAINTAINERS, + ROLE_IDS.MODERATORS, + ], }, { github: 'fabpot', From 0de09d770b82e361fc6357f16da42c66440603e0 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 4 Feb 2026 10:51:10 +0000 Subject: [PATCH 087/196] makes ure justin has lead maintainer roles --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index d88486d..f715813 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -251,7 +251,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'jspahrsummers', email: 'justin@modelcontextprotocol.io', - memberOf: [ROLE_IDS.CORE_MAINTAINERS], + memberOf: [ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS], }, { github: 'Kehrlann', From 3b920558b57752d12fc700d715494b610db852e8 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Wed, 4 Feb 2026 11:02:40 +0000 Subject: [PATCH 088/196] Import auth-maintainers team into Pulumi state with correct ID (#55) The auth-wg -> auth-maintainers rename (PR #50) caused the team to be lost from Pulumi state. A previous import attempt used the wrong team ID (14435880). The actual GitHub team ID is 16083315. Every deploy since has failed with: 422 Validation Failed: Name must be unique for this org This adds a one-time import step to reconcile state. The import line should be removed after the next successful deploy. --- .github/workflows/deploy.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 759c948..6397b30 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,4 +71,8 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod + # One-time import: auth-maintainers team exists on GitHub (ID 16083315) + # but was lost from Pulumi state during the auth-wg -> auth-maintainers rename. + # Remove this import line after the next successful deploy. + pulumi import github:index/team:Team auth-maintainers 16083315 --stack prod --yes || true make up \ No newline at end of file From c858a8f5de0c1d40e048584526c209731cbe5906 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Wed, 4 Feb 2026 14:38:04 +0000 Subject: [PATCH 089/196] Remove one-time auth-maintainers import from deploy workflow (#57) * Import auth-maintainers team into Pulumi state with correct ID The auth-wg -> auth-maintainers rename (PR #50) caused the team to be lost from Pulumi state. A previous import attempt used the wrong team ID (14435880). The actual GitHub team ID is 16083315. Every deploy since has failed with: 422 Validation Failed: Name must be unique for this org This adds a one-time import step to reconcile state. The import line should be removed after the next successful deploy. * Remove one-time auth-maintainers import from deploy workflow The import in the previous commit successfully reconciled the auth-maintainers team (ID 16083315) into Pulumi state. This removes the one-time import line now that state is consistent. --- .github/workflows/deploy.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6397b30..6bbf71f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,8 +71,4 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod - # One-time import: auth-maintainers team exists on GitHub (ID 16083315) - # but was lost from Pulumi state during the auth-wg -> auth-maintainers rename. - # Remove this import line after the next successful deploy. - pulumi import github:index/team:Team auth-maintainers 16083315 --stack prod --yes || true - make up \ No newline at end of file + make up From f816dbc40cd54968701e7adb9b82d439ba5cb3ad Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Wed, 4 Feb 2026 09:28:44 -0700 Subject: [PATCH 090/196] Add Skills Over MCP Interest Group role and users (#56) * Add Skills Over MCP Interest Group and experimental-ext-skills repo - Add SKILLS_OVER_MCP_IG role under interest-groups (GitHub team only) - Add repo access for experimental-ext-skills per SEP-2133 governance: - core-maintainers: admin (oversight) - moderators: triage - skills-over-mcp-ig: admin (day-to-day governance) - Add IG membership for: cliffhall, erain, JAORMX, kaxil, keithagroves, olaservo, pederhp, SamMorrowDrums, TeamSparkAI Co-Authored-By: Claude Opus 4.5 * Add SKILLS_OVER_MCP_IG role to user membership * Fix alphabetical sorting of users --------- Co-authored-by: Claude Opus 4.5 Co-authored-by: Paul Carleton --- src/config/repoAccess.ts | 8 ++++++++ src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 28 +++++++++++++++++++++++++--- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index d201056..63a735e 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -346,6 +346,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jerryhong1', permission: 'push' }, ], }, + { + repository: 'experimental-ext-skills', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'triage' }, + { team: 'skills-over-mcp-ig', permission: 'admin' }, + ], + }, ]; // GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index c7384b4..dc91f8b 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -61,6 +61,7 @@ export const ROLE_IDS = { CLIENT_IMPLEMENTOR_IG: 'client-implementor-ig', FINANCIAL_SERVICES_IG: 'financial-services-ig', GATEWAYS_IG: 'gateways-ig', + SKILLS_OVER_MCP_IG: 'skills-over-mcp-ig', // =================== // WG/IG Facilitators (Discord only) diff --git a/src/config/roles.ts b/src/config/roles.ts index 88577e2..f3d50a3 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -307,6 +307,12 @@ export const ROLES: readonly Role[] = [ // No GitHub role yet discord: { role: 'gateways interest group (synced)' }, }, + { + id: ROLE_IDS.SKILLS_OVER_MCP_IG, + description: 'Skills Over MCP Interest Group', + github: { team: 'skills-over-mcp-ig', parent: ROLE_IDS.INTEREST_GROUPS }, + // No Discord role yet + }, // =================== // WG/IG Facilitators (Discord only) diff --git a/src/config/users.ts b/src/config/users.ts index f715813..ec2f276 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -89,6 +89,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, ROLE_IDS.MODERATORS, + ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.WORKING_GROUPS, ], }, @@ -165,6 +166,10 @@ export const MEMBERS: readonly Member[] = [ discord: '1408222390361657426', memberOf: [ROLE_IDS.WG_IG_FACILITATORS], }, + { + github: 'erain', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'evalstate', discord: '779268016121577492', @@ -219,6 +224,10 @@ export const MEMBERS: readonly Member[] = [ github: 'jamadeo', memberOf: [ROLE_IDS.RUST_SDK], }, + { + github: 'JAORMX', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'jba', discord: '773276903364755518', @@ -253,11 +262,19 @@ export const MEMBERS: readonly Member[] = [ email: 'justin@modelcontextprotocol.io', memberOf: [ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS], }, + { + github: 'kaxil', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'Kehrlann', discord: '1112624611901837373', memberOf: [ROLE_IDS.JAVA_SDK], }, + { + github: 'keithagroves', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'KKonstantinov', discord: '390932438903422987', @@ -403,6 +420,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, + ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.WORKING_GROUPS, ], }, @@ -425,7 +443,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'pederhp', discord: '166255967665651713', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.SKILLS_OVER_MCP_IG], }, { github: 'petery-ant', @@ -449,7 +467,7 @@ export const MEMBERS: readonly Member[] = [ github: 'rdimitrov', email: 'radoslav@modelcontextprotocol.io', discord: '1088231882979815424', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG], }, { github: 'sambhav', @@ -461,7 +479,7 @@ export const MEMBERS: readonly Member[] = [ github: 'SamMorrowDrums', email: 'sammorrowdrums@github.com', discord: '782948163694493696', - memberOf: [ROLE_IDS.WG_IG_FACILITATORS], + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.WG_IG_FACILITATORS], }, { github: 'sdubov', @@ -490,6 +508,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.ADMINISTRATORS, ], }, + { + github: 'TeamSparkAI', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'tiginamaria', memberOf: [ROLE_IDS.KOTLIN_SDK], From 8e261424a52469ddc7637e88b690d6b9ed628e02 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Tue, 3 Feb 2026 10:41:22 -0600 Subject: [PATCH 091/196] Add `MAINTAINERS` and `COMMUNITY_MANAGERS` roles for jonathanhefner Adds `ROLE_IDS.MAINTAINERS` and `ROLE_IDS.COMMUNITY_MANAGERS` to `jonathanhefner` to match other community maintainers. --- src/config/users.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index ec2f276..5ba68aa 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -251,7 +251,9 @@ export const MEMBERS: readonly Member[] = [ github: 'jonathanhefner', discord: '1301960963087663186', memberOf: [ + ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.RUBY_SDK, ROLE_IDS.MCP_APPS_SDK, From 3cb142ac7a58056810c1f15616f6b4ab32d3abbc Mon Sep 17 00:00:00 2001 From: Cliff Hall Date: Wed, 11 Feb 2026 09:44:13 -0500 Subject: [PATCH 092/196] Add Tapan Chugh as facilitator for Primitive Grouping Interest Group (#58) * Add Tapan Chugh as facilitator for Primitive Grouping Interest Group. Also npm audit fix to remove 14 high vulnerabilities. * In package-lock.json - npm audit fix. - 0 vulnerabilities now * In roles.ts - add PRIMITIVE_GROUPING_IG * In roles.ts - add block for ROLE_IDS.PRIMITIVE_GROUPING_IG - **NOTE**: the github team and discord role are forward-looking and still need to be created * In users.ts - add block for Tapan with role ids for - INTEREST_GROUPS - PRIMITIVE_GROUPING_IG - WG_IG_FACILITATORS * In users.ts - add ROLE_IDS.MAINTAINERS to Tapan's entry as per DSP --- package-lock.json | 1387 ++++++++++++++++------------------------- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 + src/config/users.ts | 11 + 4 files changed, 540 insertions(+), 865 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00cf211..fbe8d2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,21 +50,37 @@ "node": ">=6" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "license": "ISC", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "minipass": "^7.0.4" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" } }, "node_modules/@isaacs/string-locale-compare": { @@ -93,242 +109,239 @@ } }, "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", + "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/arborist": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-7.5.4.tgz", - "integrity": "sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.2.0.tgz", + "integrity": "sha512-FRvpUoL5RUaMZ+CNJAZAegHymbamcGptjktD72T8Td94OllbJcBlOZQ69cB/DiNTYDvGBU0uvmcBWivlip1h+Q==", "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/fs": "^3.1.1", - "@npmcli/installed-package-contents": "^2.1.0", - "@npmcli/map-workspaces": "^3.0.2", - "@npmcli/metavuln-calculator": "^7.1.1", - "@npmcli/name-from-folder": "^2.0.0", - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.1.0", - "@npmcli/query": "^3.1.0", - "@npmcli/redact": "^2.0.0", - "@npmcli/run-script": "^8.1.0", - "bin-links": "^4.0.4", - "cacache": "^18.0.3", - "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^7.0.2", - "json-parse-even-better-errors": "^3.0.2", + "@npmcli/fs": "^5.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/map-workspaces": "^5.0.0", + "@npmcli/metavuln-calculator": "^9.0.2", + "@npmcli/name-from-folder": "^4.0.0", + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/query": "^5.0.0", + "@npmcli/redact": "^4.0.0", + "@npmcli/run-script": "^10.0.0", + "bin-links": "^6.0.0", + "cacache": "^20.0.1", + "common-ancestor-path": "^2.0.0", + "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", - "lru-cache": "^10.2.2", - "minimatch": "^9.0.4", - "nopt": "^7.2.1", - "npm-install-checks": "^6.2.0", - "npm-package-arg": "^11.0.2", - "npm-pick-manifest": "^9.0.1", - "npm-registry-fetch": "^17.0.1", - "pacote": "^18.0.6", - "parse-conflict-json": "^3.0.0", - "proc-log": "^4.2.0", - "proggy": "^2.0.0", + "lru-cache": "^11.2.1", + "minimatch": "^10.0.3", + "nopt": "^9.0.0", + "npm-install-checks": "^8.0.0", + "npm-package-arg": "^13.0.0", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "pacote": "^21.0.2", + "parse-conflict-json": "^5.0.1", + "proc-log": "^6.0.0", + "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", - "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", - "ssri": "^10.0.6", + "ssri": "^13.0.0", "treeverse": "^3.0.0", - "walk-up-path": "^3.0.1" + "walk-up-path": "^4.0.0" }, "bin": { "arborist": "bin/index.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/git": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", - "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.1.tgz", + "integrity": "sha512-+XTFxK2jJF/EJJ5SoAzXk3qwIDfvFc5/g+bD274LZ7uY7LE8sTfG6Z8rOanPl2ZEvZWqNvmEdtXC25cE54VcoA==", "license": "ISC", "dependencies": { - "@npmcli/promise-spawn": "^7.0.0", - "ini": "^4.1.3", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", - "promise-inflight": "^1.0.1", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^4.0.0" + "which": "^6.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/git/node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", - "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", + "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", "license": "ISC", "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" + "npm-bundled": "^5.0.0", + "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/map-workspaces": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.6.tgz", - "integrity": "sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-5.0.3.tgz", + "integrity": "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==", "license": "ISC", "dependencies": { - "@npmcli/name-from-folder": "^2.0.0", - "glob": "^10.2.2", - "minimatch": "^9.0.0", - "read-package-json-fast": "^3.0.0" + "@npmcli/name-from-folder": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "glob": "^13.0.0", + "minimatch": "^10.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/metavuln-calculator": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-7.1.1.tgz", - "integrity": "sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.3.tgz", + "integrity": "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==", "license": "ISC", "dependencies": { - "cacache": "^18.0.0", - "json-parse-even-better-errors": "^3.0.0", - "pacote": "^18.0.0", - "proc-log": "^4.1.0", + "cacache": "^20.0.0", + "json-parse-even-better-errors": "^5.0.0", + "pacote": "^21.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/name-from-folder": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", - "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-4.0.0.tgz", + "integrity": "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/package-json": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", - "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.4.tgz", + "integrity": "sha512-0wInJG3j/K40OJt/33ax47WfWMzZTm6OQxB9cDhTt5huCP2a9g2GnlsxmfN+PulItNPIpPrZ+kfwwUil7eHcZQ==", "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^7.0.0", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "proc-log": "^4.0.0", - "semver": "^7.5.3" + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/promise-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", - "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", "license": "ISC", "dependencies": { - "which": "^4.0.0" + "which": "^6.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/query": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-3.1.0.tgz", - "integrity": "sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-5.0.0.tgz", + "integrity": "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==", "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.10" + "postcss-selector-parser": "^7.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/redact": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", - "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", "license": "ISC", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/run-script": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", - "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.3.tgz", + "integrity": "sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==", "license": "ISC", "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "node-gyp": "^10.0.0", - "proc-log": "^4.0.0", - "which": "^4.0.0" + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0", + "which": "^6.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@opentelemetry/api": { @@ -336,6 +349,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -534,16 +548,6 @@ "node": ">=14" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -622,14 +626,14 @@ "link": true }, "node_modules/@pulumi/pulumi": { - "version": "3.212.0", - "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.212.0.tgz", - "integrity": "sha512-UXV6UQLS2elP0yQNWCQWKjY+dc8w0TXC9uJLIiybzEpFyeKdPhuA0zJrI1zOql5Y7V9q5xtF2sqmHh52HLJVKg==", + "version": "3.219.0", + "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.219.0.tgz", + "integrity": "sha512-0AO1ZBJooKALToZYvPj/93PpToGA2rEKvIGQi95zNHevTTC3ibQrKXJDLwQzjHy6HZrON2jbxzakKrU4C2w+hA==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.10.1", "@logdna/tail-file": "^2.0.6", - "@npmcli/arborist": "^7.3.1", + "@npmcli/arborist": "^9.0.0", "@opentelemetry/api": "^1.9", "@opentelemetry/exporter-zipkin": "^1.28", "@opentelemetry/instrumentation": "^0.55", @@ -641,7 +645,7 @@ "@types/semver": "^7.5.6", "@types/tmp": "^0.2.6", "execa": "^5.1.0", - "fdir": "^6.1.1", + "fdir": "^6.5.0", "google-protobuf": "^3.21.4", "got": "^11.8.6", "ini": "^2.0.0", @@ -673,77 +677,77 @@ } }, "node_modules/@sigstore/bundle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", - "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2" + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", - "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.1.0.tgz", + "integrity": "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A==", "license": "Apache-2.0", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/protobuf-specs": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz", - "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz", + "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==", "license": "Apache-2.0", "engines": { "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sigstore/sign": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", - "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.0.tgz", + "integrity": "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg==", "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "make-fetch-happen": "^13.0.1", - "proc-log": "^4.2.0", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.3", + "proc-log": "^6.1.0", "promise-retry": "^2.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/tuf": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", - "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.1.tgz", + "integrity": "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw==", "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2", - "tuf-js": "^2.2.1" + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/verify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", - "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", + "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.1.0", - "@sigstore/protobuf-specs": "^0.3.2" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sindresorhus/is": { @@ -780,16 +784,16 @@ } }, "node_modules/@tufjs/models": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", - "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.4" + "minimatch": "^10.1.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@types/cacheable-request": { @@ -862,12 +866,12 @@ "license": "MIT" }, "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/acorn": { @@ -875,6 +879,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -900,43 +905,6 @@ "node": ">= 14" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -946,34 +914,20 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, "node_modules/bin-links": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.4.tgz", - "integrity": "sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", + "integrity": "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w==", "license": "ISC", "dependencies": { - "cmd-shim": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "read-cmd-shim": "^4.0.0", - "write-file-atomic": "^5.0.0" + "cmd-shim": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "proc-log": "^6.0.0", + "read-cmd-shim": "^6.0.0", + "write-file-atomic": "^7.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/buffer-from": { @@ -983,26 +937,25 @@ "license": "MIT" }, "node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz", + "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==", "license": "ISC", "dependencies": { - "@npmcli/fs": "^3.1.0", + "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "p-map": "^7.0.2", + "ssri": "^13.0.0", + "unique-filename": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/cacheable-lookup": { @@ -1048,12 +1001,12 @@ } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/cjs-module-lexer": { @@ -1062,15 +1015,6 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "license": "MIT" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1171,12 +1115,12 @@ } }, "node_modules/cmd-shim": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.3.tgz", - "integrity": "sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-8.0.0.tgz", + "integrity": "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/color-convert": { @@ -1198,10 +1142,13 @@ "license": "MIT" }, "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -1303,18 +1250,6 @@ "node": ">=10" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -1429,34 +1364,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -1500,20 +1407,17 @@ } }, "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "license": "ISC", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.1.tgz", + "integrity": "sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w==", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "minimatch": "^10.1.2", "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1569,15 +1473,15 @@ } }, "node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "license": "ISC", "dependencies": { - "lru-cache": "^10.0.1" + "lru-cache": "^11.1.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/http-cache-semantics": { @@ -1648,15 +1552,15 @@ } }, "node_modules/ignore-walk": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", - "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", "license": "ISC", "dependencies": { - "minimatch": "^9.0.0" + "minimatch": "^10.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/import-in-the-middle": { @@ -1680,15 +1584,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", @@ -1731,12 +1626,6 @@ "node": ">=8" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT" - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -1750,27 +1639,12 @@ } }, "node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.2.tgz", + "integrity": "sha512-mIcis6w+JiQf3P7t7mg/35GKB4T1FQsBOtMIvuKw4YErj5RjtbhcTd5/I30fmkmGMwvI0WlzSNN+27K0QCMkAw==", "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "engines": { + "node": ">=20" } }, "node_modules/js-yaml": { @@ -1793,12 +1667,12 @@ "license": "MIT" }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", - "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/json-stringify-nice": { @@ -1862,32 +1736,34 @@ } }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } }, "node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", + "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", "license": "ISC", "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", + "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", - "ssri": "^10.0.0" + "ssri": "^13.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/merge-stream": { @@ -1915,15 +1791,15 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1960,17 +1836,17 @@ } }, "node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.1.tgz", + "integrity": "sha512-yHK8pb0iCGat0lDrs/D6RZmCdaBT64tULXjdxjSMAqoDi18Q3qKEUTHypHQZQd9+FYpIS+lkvpq6C/R6SbUeRw==", "license": "MIT", "dependencies": { "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { "encoding": "^0.1.13" @@ -2000,6 +1876,12 @@ "node": ">=8" } }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -2024,65 +1906,34 @@ "node": ">=8" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", "license": "ISC", "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.1.2" }, "engines": { "node": ">=8" } }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/module-details-from-path": { @@ -2098,51 +1949,51 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/node-gyp": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz", - "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==", + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.2.0.tgz", + "integrity": "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==", "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^4.1.0", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.2.1", - "which": "^4.0.0" + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "license": "ISC", "dependencies": { - "abbrev": "^2.0.0" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/normalize-package-data": { @@ -2159,6 +2010,24 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -2172,97 +2041,98 @@ } }, "node_modules/npm-bundled": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", - "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", + "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^3.0.0" + "npm-normalize-package-bin": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-package-arg": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", + "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", "license": "ISC", "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", + "hosted-git-info": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "validate-npm-package-name": "^7.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-packlist": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", - "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", "license": "ISC", "dependencies": { - "ignore-walk": "^6.0.4" + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-pick-manifest": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", - "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", "license": "ISC", "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-registry-fetch": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", - "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", "license": "ISC", "dependencies": { - "@npmcli/redact": "^2.0.0", + "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", - "make-fetch-happen": "^13.0.0", + "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" + "minipass-fetch": "^5.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-run-path": { @@ -2311,15 +2181,12 @@ } }, "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2340,55 +2207,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, "node_modules/pacote": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", - "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.2.0.tgz", + "integrity": "sha512-OwidJA8uHuGYxoZhe4DBv3JJqGg4ojjVV5dwvVxRKq+bOBpgYMbYd/onIvSU1sv5nQIsb+zp4/0uqv6giFQVjg==", "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/package-json": "^5.1.0", - "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^8.0.0", - "cacache": "^18.0.0", + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", - "npm-package-arg": "^11.0.0", - "npm-packlist": "^8.0.0", - "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^17.0.0", - "proc-log": "^4.0.0", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", - "sigstore": "^2.2.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" + "sigstore": "^4.0.0", + "ssri": "^13.0.0", + "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/parse-conflict-json": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz", - "integrity": "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-5.0.1.tgz", + "integrity": "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==", "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^3.0.0", + "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/path-key": { @@ -2407,16 +2268,16 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2427,6 +2288,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -2435,9 +2297,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -2464,21 +2326,21 @@ } }, "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/proggy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/proggy/-/proggy-2.0.0.tgz", - "integrity": "sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/proggy/-/proggy-4.0.0.tgz", + "integrity": "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-all-reject-late": { @@ -2499,12 +2361,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "license": "ISC" - }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -2565,25 +2421,12 @@ } }, "node_modules/read-cmd-shim": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", - "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-6.0.0.tgz", + "integrity": "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==", "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/require-directory": { @@ -2718,20 +2561,20 @@ "license": "ISC" }, "node_modules/sigstore": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", - "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz", + "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==", "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "@sigstore/sign": "^2.3.2", - "@sigstore/tuf": "^2.3.4", - "@sigstore/verify": "^1.2.1" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.0", + "@sigstore/tuf": "^4.0.1", + "@sigstore/verify": "^3.1.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/smart-buffer": { @@ -2830,111 +2673,15 @@ "license": "BSD-3-Clause" }, "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", + "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/strip-final-newline": { @@ -2959,53 +2706,47 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" + "node": ">=12.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/tmp": { @@ -3027,17 +2768,17 @@ } }, "node_modules/tuf-js": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", - "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", "license": "MIT", "dependencies": { - "@tufjs/models": "2.0.1", - "debug": "^4.3.4", - "make-fetch-happen": "^13.0.1" + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/typescript": { @@ -3046,6 +2787,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3061,27 +2803,27 @@ "license": "MIT" }, "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", + "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==", "license": "ISC", "dependencies": { - "unique-slug": "^4.0.0" + "unique-slug": "^6.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz", + "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==", "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/upath": { @@ -3111,24 +2853,27 @@ } }, "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/walk-up-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", - "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", - "license": "ISC" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -3137,98 +2882,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/wrappy": { @@ -3238,16 +2892,16 @@ "license": "ISC" }, "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.0.tgz", + "integrity": "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==", "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/write-file-atomic/node_modules/signal-exit": { @@ -3272,10 +2926,13 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } }, "node_modules/yargs": { "version": "17.7.2", diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index dc91f8b..9ae3186 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -62,6 +62,7 @@ export const ROLE_IDS = { FINANCIAL_SERVICES_IG: 'financial-services-ig', GATEWAYS_IG: 'gateways-ig', SKILLS_OVER_MCP_IG: 'skills-over-mcp-ig', + PRIMITIVE_GROUPING_IG: 'primitive-grouping-ig', // =================== // WG/IG Facilitators (Discord only) diff --git a/src/config/roles.ts b/src/config/roles.ts index f3d50a3..9629bdd 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -307,6 +307,12 @@ export const ROLES: readonly Role[] = [ // No GitHub role yet discord: { role: 'gateways interest group (synced)' }, }, + { + id: ROLE_IDS.PRIMITIVE_GROUPING_IG, + description: 'Primitive Grouping Interest Group', + github: { team: 'primitive-grouping-ig', parent: ROLE_IDS.INTEREST_GROUPS }, + discord: { role: 'primitive grouping interest group (synced)' }, + }, { id: ROLE_IDS.SKILLS_OVER_MCP_IG, description: 'Skills Over MCP Interest Group', diff --git a/src/config/users.ts b/src/config/users.ts index 5ba68aa..f008b1e 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -78,6 +78,17 @@ export const MEMBERS: readonly Member[] = [ discord: '633566986827464704', memberOf: [ROLE_IDS.PHP_SDK], }, + { + github: 'chughtapan', + email: 'chugh.tapan@gmail.com', + discord: '941245973357793340', + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.INTEREST_GROUPS, + ROLE_IDS.PRIMITIVE_GROUPING_IG, + ROLE_IDS.WG_IG_FACILITATORS, + ], + }, { github: 'cliffhall', email: 'cliff@futurescale.com', From 0b0e320d3eb5be04261c5cb395805d55edf01437 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:45:55 +0000 Subject: [PATCH 093/196] Add primitive-grouping-ig interest group (#59) * Add primitive-grouping-ig interest group Create the primitive-grouping-ig interest group team under interest-groups and grant it push access to the experimental-ext-grouping repository. Members: cliffhall, chughtapan, SamMorrowDrums. Co-Authored-By: Claude Opus 4.6 (1M context) * Fix formatting * Give core-maintainers and moderators access to experimental-ext-grouping --------- Co-authored-by: Claude Opus 4.6 (1M context) --- src/config/repoAccess.ts | 8 ++++++++ src/config/roleIds.ts | 2 +- src/config/users.ts | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 63a735e..622f811 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -346,6 +346,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jerryhong1', permission: 'push' }, ], }, + { + repository: 'experimental-ext-grouping', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'triage' }, + { team: 'primitive-grouping-ig', permission: 'admin' }, + ], + }, { repository: 'experimental-ext-skills', teams: [ diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 9ae3186..b63b54b 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -61,8 +61,8 @@ export const ROLE_IDS = { CLIENT_IMPLEMENTOR_IG: 'client-implementor-ig', FINANCIAL_SERVICES_IG: 'financial-services-ig', GATEWAYS_IG: 'gateways-ig', - SKILLS_OVER_MCP_IG: 'skills-over-mcp-ig', PRIMITIVE_GROUPING_IG: 'primitive-grouping-ig', + SKILLS_OVER_MCP_IG: 'skills-over-mcp-ig', // =================== // WG/IG Facilitators (Discord only) diff --git a/src/config/users.ts b/src/config/users.ts index f008b1e..fd211f9 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -98,6 +98,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, + ROLE_IDS.PRIMITIVE_GROUPING_IG, ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.SKILLS_OVER_MCP_IG, @@ -492,7 +493,11 @@ export const MEMBERS: readonly Member[] = [ github: 'SamMorrowDrums', email: 'sammorrowdrums@github.com', discord: '782948163694493696', - memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.WG_IG_FACILITATORS], + memberOf: [ + ROLE_IDS.PRIMITIVE_GROUPING_IG, + ROLE_IDS.SKILLS_OVER_MCP_IG, + ROLE_IDS.WG_IG_FACILITATORS, + ], }, { github: 'sdubov', From 2f1ebbd7a1daa8897ea83abd1fe0ff9f55ad6792 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 11 Feb 2026 16:52:15 +0000 Subject: [PATCH 094/196] Remove redundant direct repository collaborators already covered by team access Remove jspahrsummers (org admin) from all direct repo access. Remove redundant direct access for users who already have equal or higher permissions via team membership: localden (csharp-sdk), an-dustin (inspector via security-wg), jamadeo and alexhancock (rust-sdk), mattt/movetz/stallent (swift-sdk), rdimitrov (registry via registry-wg), sambhav (financial-services-ig), and liady/idosal/antonpk1 (ext-apps via mcp-apps-sdk). Upgrade swift-sdk team permission from push to admin. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/config/repoAccess.ts | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 622f811..2f40c05 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -16,7 +16,6 @@ export interface RepositoryAccess { export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'docs', - users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -46,7 +45,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: '.github', - users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-maintainers', permission: 'triage' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -78,8 +76,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'inspector', users: [ { username: 'richardkmichael', permission: 'triage' }, - { username: 'jspahrsummers', permission: 'admin' }, - { username: 'an-dustin', permission: 'admin' }, { username: 'ashwin-ant', permission: 'admin' }, ], teams: [ @@ -108,7 +104,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'modelcontextprotocol', - users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -138,7 +133,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'quickstart-resources', - users: [{ username: 'jspahrsummers', permission: 'admin' }], teams: [ { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'maintain' }, @@ -168,10 +162,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'servers', - users: [ - { username: 'jspahrsummers', permission: 'admin' }, - { username: 'slimslenderslacks', permission: 'push' }, - ], + users: [{ username: 'slimslenderslacks', permission: 'push' }], teams: [ { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'admin' }, @@ -203,7 +194,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jeffhandley', permission: 'admin' }, { username: 'MackinnonBuck', permission: 'admin' }, { username: 'jozkee', permission: 'admin' }, - { username: 'localden', permission: 'admin' }, { username: 'PederHP', permission: 'triage' }, { username: 'tarekgh', permission: 'push' }, ], @@ -236,7 +226,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'python-sdk', permission: 'admin' }, { team: 'python-sdk-auth', permission: 'admin' }, ], - users: [{ username: 'jspahrsummers', permission: 'admin' }], }, { repository: 'ruby-sdk', @@ -246,21 +235,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'rust-sdk', teams: [{ team: 'rust-sdk', permission: 'admin' }], users: [ - { username: 'jamadeo', permission: 'admin' }, { username: 'jokemanfire', permission: 'push' }, { username: '4t145', permission: 'push' }, { username: 'bolinfest', permission: 'push' }, - { username: 'alexhancock', permission: 'push' }, ], }, { repository: 'swift-sdk', - teams: [{ team: 'swift-sdk', permission: 'push' }], - users: [ - { username: 'mattt', permission: 'admin' }, - { username: 'movetz', permission: 'admin' }, - { username: 'stallent', permission: 'admin' }, - ], + teams: [{ team: 'swift-sdk', permission: 'admin' }], }, { repository: 'typescript-sdk', @@ -268,7 +250,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'typescript-sdk', permission: 'admin' }, { team: 'typescript-sdk-auth', permission: 'admin' }, ], - users: [{ username: 'jspahrsummers', permission: 'admin' }], }, { repository: 'create-python-server', @@ -287,7 +268,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'registry', teams: [{ team: 'registry-wg', permission: 'admin' }], - users: [{ username: 'rdimitrov', permission: 'admin' }], }, { repository: 'static', @@ -299,7 +279,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ users: [ { username: 'aniabot', permission: 'pull' }, { username: 'imfing', permission: 'triage' }, - { username: 'sambhav', permission: 'admin' }, { username: 'KengoA', permission: 'triage' }, { username: 'nitsanh', permission: 'pull' }, ], @@ -317,11 +296,8 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'mcp-apps-sdk', permission: 'admin' }, ], users: [ - { username: 'liady', permission: 'admin' }, - { username: 'idosal', permission: 'admin' }, { username: 'ststrong', permission: 'admin' }, { username: 'martinalong', permission: 'push' }, - { username: 'antonpk1', permission: 'admin' }, { username: 'conorkel', permission: 'admin' }, { username: 'alexi-openai', permission: 'admin' }, ], From 75c9b9c754578e67f98b702a0567b2b90b41a9bd Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 11 Feb 2026 16:58:09 +0000 Subject: [PATCH 095/196] Remove direct collaborators from inspector and servers repos Remove richardkmichael and ashwin-ant from inspector, and slimslenderslacks from servers. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/config/repoAccess.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 2f40c05..f8cff1d 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -74,10 +74,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'inspector', - users: [ - { username: 'richardkmichael', permission: 'triage' }, - { username: 'ashwin-ant', permission: 'admin' }, - ], teams: [ { team: 'inspector-maintainers', permission: 'push' }, { team: 'auth-maintainers', permission: 'push' }, @@ -162,7 +158,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'servers', - users: [{ username: 'slimslenderslacks', permission: 'push' }], teams: [ { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'admin' }, From 6be294581dd40d6dd058f3b0a1af729f943f5911 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 11 Feb 2026 17:02:09 +0000 Subject: [PATCH 096/196] remove TeamSparkAI to unblock deployment --- src/config/users.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index fd211f9..55a12b8 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -526,10 +526,6 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.ADMINISTRATORS, ], }, - { - github: 'TeamSparkAI', - memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], - }, { github: 'tiginamaria', memberOf: [ROLE_IDS.KOTLIN_SDK], From 9f50626ddce88488a56583b91e4d83964d7998e7 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Wed, 11 Feb 2026 17:05:49 +0000 Subject: [PATCH 097/196] Add bolinfest and jokemanfire to rust-sdk team, remove direct access Move bolinfest and jokemanfire from direct repo collaborators to the rust-sdk team in users.ts. Remove 4t145 entirely. The rust-sdk repo now has no direct collaborators. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/config/repoAccess.ts | 5 ----- src/config/users.ts | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index f8cff1d..30bcc8b 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -229,11 +229,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'rust-sdk', teams: [{ team: 'rust-sdk', permission: 'admin' }], - users: [ - { username: 'jokemanfire', permission: 'push' }, - { username: '4t145', permission: 'push' }, - { username: 'bolinfest', permission: 'push' }, - ], }, { repository: 'swift-sdk', diff --git a/src/config/users.ts b/src/config/users.ts index 55a12b8..c6095cd 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -56,6 +56,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.TYPESCRIPT_SDK, ], }, + { + github: 'bolinfest', + memberOf: [ROLE_IDS.RUST_SDK], + }, { github: 'caitiem20', email: 'caitie.mccaffrey@microsoft.com', @@ -259,6 +263,10 @@ export const MEMBERS: readonly Member[] = [ discord: '1398403578892128437', memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, + { + github: 'jokemanfire', + memberOf: [ROLE_IDS.RUST_SDK], + }, { github: 'jonathanhefner', discord: '1301960963087663186', From a99cffff89272fd587539d922c2b7ed9c3ef1fc9 Mon Sep 17 00:00:00 2001 From: Maciej Kisiel Date: Wed, 11 Feb 2026 20:45:56 +0100 Subject: [PATCH 098/196] Restrict go-sdk repository access to go-sdk team. (#60) --- src/config/repoAccess.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 30bcc8b..2f75181 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -196,12 +196,6 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'go-sdk', teams: [{ team: 'go-sdk', permission: 'admin' }], - users: [ - { username: 'neild', permission: 'push' }, - { username: 'rsc', permission: 'push' }, - { username: 'rolandshoemaker', permission: 'push' }, - { username: 'h9jiang', permission: 'maintain' }, - ], }, { repository: 'java-sdk', From 80f888d34b1c626acf7ea24d08336bfe2cee00e1 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Wed, 11 Feb 2026 17:44:16 -0800 Subject: [PATCH 099/196] Add pederhp as community moderator and community manager (#61) --- src/config/users.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index c6095cd..d4ffdbb 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -465,7 +465,13 @@ export const MEMBERS: readonly Member[] = [ { github: 'pederhp', discord: '166255967665651713', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.SKILLS_OVER_MCP_IG], + memberOf: [ + ROLE_IDS.COMMUNITY_MANAGERS, + ROLE_IDS.MAINTAINERS, + ROLE_IDS.FINANCIAL_SERVICES_IG, + ROLE_IDS.MODERATORS, + ROLE_IDS.SKILLS_OVER_MCP_IG, + ], }, { github: 'petery-ant', From e2edbb413dd115e271dd0e27c5e8c98287896052 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Fri, 13 Feb 2026 07:41:21 -0700 Subject: [PATCH 100/196] Add BobDickinson to SKILLS_OVER_MCP_IG role (#63) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add evalstate and cliffhall to docs-maintaners team 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Add BobDickinson to SKILLS_OVER_MCP_IG role Closes #62 Co-Authored-By: Claude Opus 4.6 * Fix alphabetical sorting of BobDickinson entry Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude --- src/config/users.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index d4ffdbb..46f2a10 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -56,6 +56,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.TYPESCRIPT_SDK, ], }, + { + github: 'BobDickinson', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'bolinfest', memberOf: [ROLE_IDS.RUST_SDK], From fc1488864a0c7c11878206adb2a38363ed278f72 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 13 Feb 2026 18:33:38 +0000 Subject: [PATCH 101/196] Add governance repo access for lead/core maintainers and sambhav admin Co-Authored-By: Claude Opus 4.6 --- src/config/repoAccess.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 2f75181..ca008a0 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -322,6 +322,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'skills-over-mcp-ig', permission: 'admin' }, ], }, + { + repository: 'governance', + teams: [ + { team: 'lead-maintainers', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'maintain' }, + ], + users: [{ username: 'sambhav', permission: 'admin' }], + }, ]; // GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet From ca9a57c5b94aadfd26bf6bf8abafa928410dd6d6 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 13 Feb 2026 18:34:54 +0000 Subject: [PATCH 102/196] Add steering-committee maintain access to governance repo Co-Authored-By: Claude Opus 4.6 --- src/config/repoAccess.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index ca008a0..18fb408 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -327,6 +327,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ teams: [ { team: 'lead-maintainers', permission: 'maintain' }, { team: 'core-maintainers', permission: 'maintain' }, + { team: 'steering-committee', permission: 'maintain' }, ], users: [{ username: 'sambhav', permission: 'admin' }], }, From 3441bb2391c91f576ec685993eff23d3e568c3f8 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 13 Feb 2026 19:39:08 +0000 Subject: [PATCH 103/196] Rename governance repo to maintainer-docs Co-Authored-By: Claude Opus 4.6 --- src/config/repoAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 18fb408..d5a55a5 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -323,7 +323,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], }, { - repository: 'governance', + repository: 'maintainer-docs', teams: [ { team: 'lead-maintainers', permission: 'maintain' }, { team: 'core-maintainers', permission: 'maintain' }, From df188df984f57d5f4acf216d512179ff1c79c6e7 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 13 Feb 2026 19:42:34 +0000 Subject: [PATCH 104/196] Give core-maintainers admin access to maintainer-docs Co-Authored-By: Claude Opus 4.6 --- src/config/repoAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index d5a55a5..7f53e68 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -326,7 +326,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'maintainer-docs', teams: [ { team: 'lead-maintainers', permission: 'maintain' }, - { team: 'core-maintainers', permission: 'maintain' }, + { team: 'core-maintainers', permission: 'admin' }, { team: 'steering-committee', permission: 'maintain' }, ], users: [{ username: 'sambhav', permission: 'admin' }], From 182324da5cb1b7e857e8a44bf30ec0df0132fe1e Mon Sep 17 00:00:00 2001 From: Cliff Hall Date: Mon, 16 Feb 2026 11:28:38 -0500 Subject: [PATCH 105/196] In users.ts (#64) --- src/config/users.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 46f2a10..fc18c0e 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -58,7 +58,9 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'BobDickinson', - memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + email: 'bob.dickinson@gmail.com', + discord: '1175893001202045139', + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG], }, { github: 'bolinfest', From 5c258fb4e3784c3b392d249f9ab5ebdbfb7dfd9f Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:39:21 +0000 Subject: [PATCH 106/196] update deps (#67) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index bb4186b..bc00b33 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ }, "license": "MIT", "dependencies": { - "@pulumi/github": "^6.7.3", + "@pulumi/github": "^6.11.0", "@pulumi/googleworkspace": "file:sdks/googleworkspace", - "@pulumi/pulumi": "^3.197.0" + "@pulumi/pulumi": "^3.218.0" }, "devDependencies": { "@types/node": "^22.18.6", From acafe74caf1a6cf7c823cd48ef8bcb6ee014bcbb Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Wed, 18 Feb 2026 14:38:29 -0800 Subject: [PATCH 107/196] Add BobDickinson as registry maintainer (#68) Approved by unanimous vote of current registry maintainers. Co-authored-by: Claude Opus 4.6 --- src/config/users.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index fc18c0e..5c757b8 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -60,7 +60,12 @@ export const MEMBERS: readonly Member[] = [ github: 'BobDickinson', email: 'bob.dickinson@gmail.com', discord: '1175893001202045139', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG], + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.INSPECTOR_MAINTAINERS, + ROLE_IDS.REGISTRY_MAINTAINERS, + ROLE_IDS.SKILLS_OVER_MCP_IG, + ], }, { github: 'bolinfest', From d365a398cf3521620d0152d977280edfff066d42 Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Thu, 19 Feb 2026 12:03:58 -0500 Subject: [PATCH 108/196] access: add Dale Seo as Rust SDK maintainer (#69) --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 5c757b8..bbc4895 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -134,6 +134,11 @@ export const MEMBERS: readonly Member[] = [ discord: '1364696680980545697', memberOf: [ROLE_IDS.AUTH_MAINTAINERS], }, + { + github: 'daleseo', + discord: '267646459187298305', + memberOf: [ROLE_IDS.RUST_SDK], + }, { github: 'devcrocod', memberOf: [ROLE_IDS.KOTLIN_SDK], From 2de77b86849592847c14df8621be107e0eca3feb Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:17:25 +0000 Subject: [PATCH 109/196] Add Google Workspace user account provisioning (#66) * Add Google Workspace user account provisioning * Fix validation: remove stale skipGoogleUserProvisioning from a-akimov, add it for BobDickinson - a-akimov: had skipGoogleUserProvisioning but is not in a provisionUser role (DOCS_MAINTAINERS) - BobDickinson: is in REGISTRY_MAINTAINERS (provisionUser role) but missing Google user fields --- .gitignore | 3 + devenv.nix | 8 ++ package-lock.json | 246 ++++++++++++++++++++++++++++++++++++- package.json | 4 +- scripts/test-config.ts | 45 +++++++ scripts/validate-config.ts | 66 ++++++++++ src/config/roles.ts | 7 +- src/config/users.ts | 50 ++++++-- src/config/utils.ts | 10 ++ src/google.ts | 86 ++++++++++++- 10 files changed, 502 insertions(+), 23 deletions(-) create mode 100644 devenv.nix diff --git a/.gitignore b/.gitignore index 84a9b0d..4e71df0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ node_modules/ bin/ *.log +.devenv/ +.devenv.flake.nix +devenv.lock # Pulumi .pulumi/ diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..a9ec9e4 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,8 @@ +{ pkgs, ... }: +{ + packages = [ + pkgs.nodejs_22 + pkgs.pulumi-bin + pkgs.pulumiPackages.pulumi-nodejs + ]; +} diff --git a/package-lock.json b/package-lock.json index fbe8d2c..e7602a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,14 +11,29 @@ "dependencies": { "@pulumi/github": "^6.7.3", "@pulumi/googleworkspace": "file:sdks/googleworkspace", - "@pulumi/pulumi": "^3.197.0" + "@pulumi/pulumi": "^3.218.0", + "@pulumi/random": "^4.14.0" }, "devDependencies": { "@types/node": "^22.18.6", "prettier": "^3.7.2", + "ts-node": "^10.9.2", "typescript": "^5.9.2" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.14.3", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", @@ -89,6 +104,34 @@ "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "license": "ISC" }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@js-sdsl/ordered-map": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", @@ -349,7 +392,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -676,6 +718,15 @@ } } }, + "node_modules/@pulumi/random": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@pulumi/random/-/random-4.19.1.tgz", + "integrity": "sha512-gDotQyxtl+pcJb4oaGfbi6PsK8OtzjBmF1D8PYLvr13kCup98KTIIv/8wF2xbgbFf7ljJn3EblADxZ0u8AC/Dg==", + "license": "Apache-2.0", + "dependencies": { + "@pulumi/pulumi": "^3.142.0" + } + }, "node_modules/@sigstore/bundle": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", @@ -774,6 +825,34 @@ "node": ">=10" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -879,7 +958,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -896,6 +974,19 @@ "acorn": "^8" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -905,6 +996,13 @@ "node": ">= 14" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -914,6 +1012,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/bin-links": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", @@ -1150,6 +1257,13 @@ "node": ">= 18" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1250,6 +1364,16 @@ "node": ">=10" } }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -1744,6 +1868,13 @@ "node": "20 || >=22" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "license": "ISC" + }, "node_modules/make-fetch-happen": { "version": "15.0.3", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", @@ -2288,7 +2419,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -2767,6 +2897,56 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/tuf-js": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", @@ -2787,7 +2967,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2842,6 +3021,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -3002,6 +3188,54 @@ "node": ">=8" } }, - "sdks/googleworkspace": {} + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "sdks/googleworkspace": { + "name": "@pulumi/googleworkspace", + "version": "0.7.0", + "hasInstallScript": true, + "dependencies": { + "@pulumi/pulumi": "^3.142.0", + "@types/node": "^18", + "async-mutex": "^0.5.0", + "typescript": "^4.3.5" + } + }, + "sdks/googleworkspace/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "sdks/googleworkspace/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "sdks/googleworkspace/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + } } } diff --git a/package.json b/package.json index bc00b33..47aabb9 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "dependencies": { "@pulumi/github": "^6.11.0", "@pulumi/googleworkspace": "file:sdks/googleworkspace", - "@pulumi/pulumi": "^3.218.0" + "@pulumi/pulumi": "^3.218.0", + "@pulumi/random": "^4.14.0" }, "devDependencies": { "@types/node": "^22.18.6", "prettier": "^3.7.2", + "ts-node": "^10.9.2", "typescript": "^5.9.2" } } diff --git a/scripts/test-config.ts b/scripts/test-config.ts index 471bbf4..edffdca 100644 --- a/scripts/test-config.ts +++ b/scripts/test-config.ts @@ -82,6 +82,51 @@ test('TYPESCRIPT_SDK_AUTH role exists (GitHub-only)', () => { return role !== undefined && role.github !== undefined && role.discord === undefined; }); +// Test Google Workspace user provisioning +test('Roles with provisionUser exist', () => { + const provisionRoles = ROLES.filter((r) => r.google?.provisionUser); + return provisionRoles.length > 0; +}); + +test('Members with googleEmailPrefix have firstName and lastName', () => + MEMBERS.every((m) => { + if (!m.googleEmailPrefix) return true; + return !!m.firstName && !!m.lastName; + })); + +test('googleEmailPrefix values are unique', () => { + const prefixes = MEMBERS.filter((m) => m.googleEmailPrefix).map((m) => m.googleEmailPrefix); + return prefixes.length === new Set(prefixes).size; +}); + +test('ProvisionUser members are explicit: fields present or skip flag set', () => { + const provisionRoleIds = new Set(ROLES.filter((r) => r.google?.provisionUser).map((r) => r.id)); + return MEMBERS.every((member) => { + const inProvisionRole = member.memberOf.some((id) => provisionRoleIds.has(id)); + if (!inProvisionRole) return !member.skipGoogleUserProvisioning; + + const hasProvisioningFields = !!( + member.firstName && + member.lastName && + member.googleEmailPrefix + ); + return hasProvisioningFields !== !!member.skipGoogleUserProvisioning; + }); +}); + +test('Some members in provisionUser roles have Google user fields', () => { + const membersInProvisionRoles = MEMBERS.filter((m) => + m.memberOf.some((id) => { + const role = roleLookup.get(id); + return role?.google?.provisionUser === true; + }) + ); + const provisioned = membersInProvisionRoles.filter( + (m) => m.firstName && m.lastName && m.googleEmailPrefix + ); + return membersInProvisionRoles.length > 0 && provisioned.length > 0; +}); + // Summary console.log(`\n${passed} passed, ${failed} failed`); process.exit(failed > 0 ? 1 : 0); diff --git a/scripts/validate-config.ts b/scripts/validate-config.ts index b824b3c..4f8c748 100644 --- a/scripts/validate-config.ts +++ b/scripts/validate-config.ts @@ -98,6 +98,72 @@ console.log('Validating member ordering in users.ts...'); } } +// Validate Google Workspace user provisioning fields +console.log('Validating Google Workspace user provisioning fields...'); +{ + const googleEmailPrefixes = new Map(); + + for (const member of MEMBERS) { + const memberId = member.github || member.email || 'unknown'; + + // Members with googleEmailPrefix must also have firstName and lastName + if (member.googleEmailPrefix) { + if (!member.firstName) { + console.error(`ERROR: Member "${memberId}" has googleEmailPrefix but is missing firstName`); + hasErrors = true; + } + if (!member.lastName) { + console.error(`ERROR: Member "${memberId}" has googleEmailPrefix but is missing lastName`); + hasErrors = true; + } + + // Check uniqueness of googleEmailPrefix + const existing = googleEmailPrefixes.get(member.googleEmailPrefix); + if (existing) { + console.error( + `ERROR: googleEmailPrefix "${member.googleEmailPrefix}" is used by both "${existing}" and "${memberId}"` + ); + hasErrors = true; + } else { + googleEmailPrefixes.set(member.googleEmailPrefix, memberId); + } + } + + // Members in provisionUser roles without all three fields won't get a GWS account + const inProvisionUserRole = member.memberOf.some((roleId: RoleId) => { + const role = roleLookup.get(roleId); + return role?.google?.provisionUser === true; + }); + + const hasProvisioningFields = !!( + member.googleEmailPrefix && + member.firstName && + member.lastName + ); + + if (member.skipGoogleUserProvisioning && !inProvisionUserRole) { + console.error( + `ERROR: Member "${memberId}" has skipGoogleUserProvisioning=true but is not in a provisionUser role` + ); + hasErrors = true; + } + + if (inProvisionUserRole && hasProvisioningFields && member.skipGoogleUserProvisioning) { + console.error( + `ERROR: Member "${memberId}" has provisioning fields and skipGoogleUserProvisioning=true; pick one` + ); + hasErrors = true; + } + + if (inProvisionUserRole && !hasProvisioningFields && !member.skipGoogleUserProvisioning) { + console.error( + `ERROR: Member "${memberId}" is in a provisionUser role but is missing Google user fields. Add fields or set skipGoogleUserProvisioning: true` + ); + hasErrors = true; + } + } +} + // Validate parent role references in roles.ts console.log('Validating parent role references in roles.ts...'); for (const role of ROLES) { diff --git a/src/config/roles.ts b/src/config/roles.ts index 9629bdd..318bb36 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -26,6 +26,8 @@ export interface GoogleConfig { group: string; /** If true, accepts emails from anyone including external users */ isEmailGroup?: boolean; + /** If true, members of this role get a Google Workspace user account */ + provisionUser?: boolean; } /** @@ -77,13 +79,14 @@ export const ROLES: readonly Role[] = [ description: 'Lead core maintainers', github: { team: 'lead-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, discord: { role: 'lead maintainers (synced)' }, - // Discord only for now - could add GitHub if needed + google: { group: 'lead-maintainers', provisionUser: true }, }, { id: ROLE_IDS.CORE_MAINTAINERS, description: 'Core maintainers', github: { team: 'core-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, discord: { role: 'core maintainers (synced)' }, + google: { group: 'core-maintainers', provisionUser: true }, }, { id: ROLE_IDS.MODERATORS, @@ -130,7 +133,7 @@ export const ROLES: readonly Role[] = [ description: 'Official registry builders and maintainers', github: { team: 'registry-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'registry maintainers (synced)' }, - google: { group: 'registry-wg' }, + google: { group: 'registry-wg', provisionUser: true }, }, { id: ROLE_IDS.USE_MCP_MAINTAINERS, diff --git a/src/config/users.ts b/src/config/users.ts index bbc4895..570521c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -5,6 +5,7 @@ export const MEMBERS: readonly Member[] = [ { github: '000-000-000-000-000', discord: '1360717264051241071', + skipGoogleUserProvisioning: true, memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { @@ -60,6 +61,7 @@ export const MEMBERS: readonly Member[] = [ github: 'BobDickinson', email: 'bob.dickinson@gmail.com', discord: '1175893001202045139', + skipGoogleUserProvisioning: true, memberOf: [ ROLE_IDS.MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, @@ -75,6 +77,7 @@ export const MEMBERS: readonly Member[] = [ github: 'caitiem20', email: 'caitie.mccaffrey@microsoft.com', discord: '1425586366288494722', + skipGoogleUserProvisioning: true, memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { @@ -147,10 +150,15 @@ export const MEMBERS: readonly Member[] = [ github: 'domdomegg', email: 'adam@modelcontextprotocol.io', discord: '102128241715716096', + firstName: 'Adam', + lastName: 'Jones', + googleEmailPrefix: 'adam', + existingGWSUser: true, memberOf: [ROLE_IDS.MCPB_MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'dsp', + skipGoogleUserProvisioning: true, memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.LEAD_MAINTAINERS, @@ -170,19 +178,16 @@ export const MEMBERS: readonly Member[] = [ github: 'dsp-ant', email: 'david@modelcontextprotocol.io', discord: '166107790262272000', + firstName: 'David', + lastName: 'Soria Parra', + googleEmailPrefix: 'david', + existingGWSUser: true, memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, - ROLE_IDS.GO_SDK, - ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.MODERATORS, - ROLE_IDS.PHP_SDK, - ROLE_IDS.PYTHON_SDK, - ROLE_IDS.SECURITY_WG, - ROLE_IDS.TRANSPORT_WG, - ROLE_IDS.TYPESCRIPT_SDK, ], }, { @@ -298,6 +303,10 @@ export const MEMBERS: readonly Member[] = [ { github: 'jspahrsummers', email: 'justin@modelcontextprotocol.io', + firstName: 'Justin', + lastName: 'Spahr-Summers', + googleEmailPrefix: 'justin', + existingGWSUser: true, memberOf: [ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS], }, { @@ -335,6 +344,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'kurtisvg', discord: '1158458388917780590', + skipGoogleUserProvisioning: true, memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { @@ -345,6 +355,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'localden', discord: '1351224014143754260', + firstName: 'Den', + lastName: 'Delimarsky', + googleEmailPrefix: 'den', memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, @@ -422,6 +435,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'nickcoai', discord: '1153783469860732968', + skipGoogleUserProvisioning: true, memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.SERVER_IDENTITY_WG], }, { @@ -469,6 +483,10 @@ export const MEMBERS: readonly Member[] = [ { github: 'pcarleton', discord: '1354465170969067852', + firstName: 'Paul', + lastName: 'Carleton', + googleEmailPrefix: 'paul', + existingGWSUser: true, memberOf: [ ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.PYTHON_SDK, @@ -496,6 +514,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'pja-ant', discord: '328628782497923072', + firstName: 'Peter', + lastName: 'Alexander', + googleEmailPrefix: 'pja', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { @@ -505,12 +526,17 @@ export const MEMBERS: readonly Member[] = [ { github: 'pwwpche', discord: '1226238847013228604', + skipGoogleUserProvisioning: true, memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { github: 'rdimitrov', email: 'radoslav@modelcontextprotocol.io', discord: '1088231882979815424', + firstName: 'Radoslav', + lastName: 'Dimitrov', + googleEmailPrefix: 'radoslav', + existingGWSUser: true, memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG], }, { @@ -546,6 +572,10 @@ export const MEMBERS: readonly Member[] = [ github: 'tadasant', email: 'tadas@modelcontextprotocol.io', discord: '400092503677599754', + firstName: 'Tadas', + lastName: 'Antanavicius', + googleEmailPrefix: 'tadas', + existingGWSUser: true, memberOf: [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.MODERATORS, @@ -564,7 +594,11 @@ export const MEMBERS: readonly Member[] = [ github: 'toby', email: 'toby@modelcontextprotocol.io', discord: '560155411777323048', - memberOf: [ROLE_IDS.REGISTRY_MAINTAINERS], + firstName: 'Toby', + lastName: 'Padilla', + googleEmailPrefix: 'toby', + existingGWSUser: true, + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], }, { github: 'topherbullock', diff --git a/src/config/utils.ts b/src/config/utils.ts index 782c3e9..6c84daf 100644 --- a/src/config/utils.ts +++ b/src/config/utils.ts @@ -15,6 +15,16 @@ export interface Member { discord?: string; /** Roles this member belongs to */ memberOf: readonly RoleId[]; + /** First name (required for Google Workspace user provisioning) */ + firstName?: string; + /** Last name (required for Google Workspace user provisioning) */ + lastName?: string; + /** Google Workspace email prefix (e.g., 'david' -> david@modelcontextprotocol.io) */ + googleEmailPrefix?: string; + /** If true, this user already exists in Google Workspace and should be imported into Pulumi state */ + existingGWSUser?: boolean; + /** Explicitly skip automatic GWS user provisioning for provisionUser roles */ + skipGoogleUserProvisioning?: boolean; } /** diff --git a/src/google.ts b/src/google.ts index f52499b..43cce9a 100644 --- a/src/google.ts +++ b/src/google.ts @@ -1,4 +1,7 @@ +import * as crypto from 'crypto'; +import * as pulumi from '@pulumi/pulumi'; import * as gworkspace from '@pulumi/googleworkspace'; +import * as random from '@pulumi/random'; import { ROLES, type Role, buildRoleLookup } from './config/roles'; import { MEMBERS } from './config/users'; import type { RoleId } from './config/roleIds'; @@ -45,20 +48,91 @@ ROLES.forEach((role: Role) => { }); }); +// Provision Google Workspace user accounts for members in roles with provisionUser +const provisionedUsersByEmail: Record = {}; +const newUserPasswords: Record> = {}; + +MEMBERS.forEach((member) => { + if ( + !member.firstName || + !member.lastName || + !member.googleEmailPrefix || + member.skipGoogleUserProvisioning + ) + return; + + const needsUser = member.memberOf.some((roleId: RoleId) => { + const role = roleLookup.get(roleId); + return role?.google?.provisionUser === true; + }); + if (!needsUser) return; + + const primaryEmail = `${member.googleEmailPrefix}@modelcontextprotocol.io`; + + if (member.existingGWSUser) { + // Import existing user into Pulumi state without recreating + const user = new gworkspace.User( + `gws-user-${member.googleEmailPrefix}`, + { + primaryEmail, + name: { familyName: member.lastName!, givenName: member.firstName! }, + orgUnitPath: '/Model Context Protocol', + }, + { import: primaryEmail } + ); + provisionedUsersByEmail[primaryEmail] = user; + } else { + // Create new user with random password + const password = new random.RandomPassword(`gws-pwd-${member.googleEmailPrefix}`, { + length: 24, + special: true, + }); + const hashedPassword = password.result.apply((plaintext: string) => + crypto.createHash('sha1').update(plaintext).digest('hex') + ); + + const user = new gworkspace.User(`gws-user-${member.googleEmailPrefix}`, { + primaryEmail, + name: { familyName: member.lastName!, givenName: member.firstName! }, + password: hashedPassword, + hashFunction: 'SHA-1', + changePasswordAtNextLogin: true, + orgUnitPath: '/Model Context Protocol', + }); + provisionedUsersByEmail[primaryEmail] = user; + + // Track password for export so an admin can retrieve it + newUserPasswords[primaryEmail] = password.result; + } +}); + // Create group memberships for users MEMBERS.forEach((member) => { - if (!member.email) return; + // Prefer the provisioned GWS email over the personal email for group memberships + const gwsEmail = member.googleEmailPrefix + ? `${member.googleEmailPrefix}@modelcontextprotocol.io` + : undefined; + const memberEmail = gwsEmail || member.email; + if (!memberEmail) return; + const provisionedUser = gwsEmail ? provisionedUsersByEmail[gwsEmail] : undefined; member.memberOf.forEach((roleId: RoleId) => { const role = roleLookup.get(roleId); if (!role?.google) return; // Role doesn't have Google config - new gworkspace.GroupMember(`${member.email}-${role.google.group}`, { - groupId: groups[role.google.group].id, - email: member.email!, - role: 'MEMBER', - }); + new gworkspace.GroupMember( + `${memberEmail}-${role.google.group}`, + { + groupId: groups[role.google.group].id, + email: memberEmail, + role: 'MEMBER', + }, + provisionedUser ? { dependsOn: [provisionedUser] } : undefined + ); }); }); export { groups as googleGroups }; +// Export initial passwords as secrets so an admin can retrieve them with: +// pulumi stack output --show-secrets newGWSUserPasswords +export const newGWSUserPasswords = pulumi.secret(newUserPasswords); From 2cf955e3635a88c4ac9a46e4b75ed6b2a281d027 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:02:53 +0000 Subject: [PATCH 110/196] add devenv --- devenv.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/devenv.nix b/devenv.nix index a9ec9e4..9295c0e 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,6 +1,7 @@ { pkgs, ... }: { packages = [ + pkgs.google-cloud-sdk pkgs.nodejs_22 pkgs.pulumi-bin pkgs.pulumiPackages.pulumi-nodejs From 33049321d21f42a9b054cc4d470c7de6766820a2 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:05:02 +0000 Subject: [PATCH 111/196] impersonate mail --- Pulumi.prod.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml index eb4ad25..eb2724e 100644 --- a/Pulumi.prod.yaml +++ b/Pulumi.prod.yaml @@ -1,5 +1,6 @@ encryptionsalt: v1:N7wjHl1hvH8=:v1:VT8UbYOIS0kNADr5:fSnvGX+is6htXDjLS03RDObRnzGEVA== config: + googleworkspace:impersonatedUserEmail: david@modelcontextprotocol.io googleworkspace:customerId: C01dja0mo googleworkspace:credentials: secure: v1:Ntr2su0kgwp0oAh3:av1h5ir44/HNmtOQBftB6AD/0N7r+nLtIuPNnM47Xgyo71xO0GiV6oG81fT7dDhaxOwbBMWiEiOP+8s3XRiTJoAQHlqybvyHsgiprELsQfAPo2srnSDt289ggi11zPttYb/iNgOvoKwFxCfrapPv3Gnh0akbl3AgsJafs0uNToiNWmV176GaepR2JHEc7dpjIzpS6CFS8lVVl262ROInU85n1mEtaQ9eYZ69LPJn+ZQLSHAdMRROP1hv49Q26iqh4icJGmqbQQ6XGh4e5E0TgHT+/UPbuAzKnearRN+jFdqaJPQneXav9xy5W9zS4834LGa6Otawa12r5DlSIB5LunYCVDENhXcDhouwFHc7OK6bVUe2iY+557YfESZcznuYuHOq0ChPyKKJ7n5nE0Pu34nEJWMR3BARCLcGMuPIQALiecFMKDJjbVJN0ShS0nch4dDJpWH4KHNqyCs2q9h/Mume9pSu4PhZXg28VcBILh4a9B0JF2BzwqFy79OP0gmoz/B7IoIcjN28HnHrYmnK/97xrbTV7YzI2x5iYY9xwW3IlaKIrzr8f6vBAXTZmSFTOAqVWdsGgzalpqljDIqwJbh2YVQAy8NPLhfSxlkMJNPxfO0DR9AcL/kITQPL/f5WkRGU9+aur2K7gp2vkXUoObx8elnogIHpklPHRmb0obqoR8LTCD97Vt/rLL+ZaPgp2eBWwWydz1XAo78PVq53qGGbXk5lrv+i4qUxiUy8639ea4sdRJf2yGNWJb14hrpf5PBn7+bn2mSMdyY2jQIVbE0ltFYUVfPrzUNG51O39EwD9iFf/aR0lyxgO+fF8mMWOOTZleCPBMDY1t6c5u3TlipuSZF79q2LbuTnXGA8XmONiqoGyKDVIaYTNpOllFtuL105scGledqj9S2Q5bbgdRldlFXbFbGXWAYtj4A1z7gBRklb9UMJXbMyECRtfepbLu93XgKZaKjfXdRZkNtVNOjrE9KsGEUp0dkLUToKzj9tdDGxninCEUanhEV5ACTuXxGNqcvYoegYVRnGuLfCZTVMWwIezombduQDh7mEKkBRyw+XqQeRjCGHm+tZaAeo5p82cGX2bcxR2KselYfCVzP3n/yJsqp/D+STYd3bqq5Kno1f2obys2pyGhDri+scaZRZiWF7EysuYEuMC1D6EPkIo9ZEDfhgBU5RbJqC8h8DTTTLzsJa8r25L7qph/E7wVQywSLlL6e6GQ9bbepSikrNxXvftzkmEDNruN+5dVFznOEL5rTRa8Q98Dwet3i5c2PzWCzhz9/24rE0vrEOPO6NNgQ+du53wfG5uwkXEjAC4+w8nbXUrqe64y6o6AnRlo8YBwECV+qNfdpl0+6/JTP83L6aWo+u+QIiq4qpTF95SBNBt//SXk1KkYYgYNTo5Y/thjQ4ycHakHO4Mu/6Gz3+20uIOcSyhvO30Pk65scNcsTPHy1cT61cdK77fipJynVkn6rLKSWvqXMEeP6LwnioLx5WChyNAf+oubFMzlgS8zLW+kH8NxR4BIBpwbuyy+hpXXKZKNYKJtCzSUdBuV3PNZ0b0BaIEr6CyeqPtTPCfAoCxpvXipYJUEWLe6yPdorBQU0AYp3jMblrmqz/eAAPkiI5c7gTF6FAk0ezjzhvY3nVKPIvNi8Zj/5oxlt7p/kGjtUbMaQmGDrZco6TfDf0AtdERIDCUWnplKqLuz6NjIbZ7acAeyrBKyVslKNDn6tRZGk/4XrUFYgIEdWShOLZxAxI7o564n9+TycSIkz+LWYltQtRM1v5lnwNHSWXaXp7NUbhwWA5FlWPKBDOznhM0d6R5CRGfmtxE0KMQ3wac6QiODL3Zh5TJeKyFvXTh9hpSVfCafCMDtPWjzYj0X5G/DFCyNhIOf8Z8jfMBf2kh0AWI8ojZNS3c8ip1+elzLcGLDVfkLGF7rwvVDYXL/2cfamtfwR+CrljXlzT+FPRaMER8plhj6hN27UCWjILXVE7UqInNz/nkTS/YsZtVRaafCEXO1ytyV0gkXbKk00WFN6Qg3cK8HB0ITTYns8+NhstRTXewj9VJv/Kl7GV9Uz/DQX4KZW9rlJNd3XZjPYJRsHeRNXCnezTydyLpjrLYipbpYnuQcqBhE7S3nBiM+fTFrm8XJr7EBf4uIm8/9PSG52PMUiSsLHt+qMg36z+pliavLU5GXUw8RLONP2EnjI14vu8rz8BqlXhUuluWYRKeVNXprIG6pAkahqxhTmqwqYu1VIPDTPfuWJfQ9RCz5BYQEN4flTkuqGj0yYdl9CRxuL+m1BbPcZLufx+gY0gC5buiskzalfek3QhQLu2kH9P4zGuK/03jRVYlPf4IL0rt4l/e5iMV3bKB27Ds6W2An4NQB13WaujyINDiicJcRCyIQwsJt9aiLxf+NKc7N3iCLgjVw85K63WcDcW+zh8YOc89mJj/y3SOfTDAVCUjEzye9TldX8/7biavhsn4Bwdd71VkzxS/eSAJHeTXjw94FveP1d/A86LMBl/PThVEJmQMBZFjiMVw8x8xWG78NfqZDR2QeMU8meBhXrj4gwb3s2H9+H/6HcxoPB4O5kGbY4piQ3yu9MzPojVpQspdT2tgH+CAaNg3qHy3R/HHuWLxuXVGZj+2t3NoGvHrQRiS5e/9IwkQ2EA/3b20ZsBqP9F63oApr+PVnZYMbtRjJLdl02tIsMm4n5xoOI+0yP/93XSwD/rM+YXYu2PLiQRm5IkbDVu8Hg15OzIHSiGkTyPZb2/QBja10O9M/suKF7VZmS74skWZH3ETCNWyA5TpNwltafvou/1WtipHqNs45SXKJMr9iT3yI2jr/KGXlBDIih7mTiY5KN9mioAH1F5ROe0siEzBdaQe9soQsOBfFIZYzMBN/IKjuiqohG2DdDfsZqyYn0zO4GrqQ6Afv0fDY2fJJfbXCbn9i8saEI8CcSI2c2WTIxgQk+q6iOBBr0X/7WGzH7lmAHd4sDoIIi8F1QJyNlFsWj3r+5oe64jQa2XAkgVF3I6JENWpgSz6AJu7zQgnbg3ZIfF0kkwt+jRxEXsuLwdDO0XSxKBqv7Ynk0xDsV+5BkHzLv70fXh6BM8+LcK7kI8dgy8zJMLtctmAd9LmWcs7uWw8Di/EnZHZR5pDju0a+miq15yvHiJVjN6k5SoFRCVOQ== From b12afed1b2ae6a208836ebc34d546b8bd9679c19 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:10:41 +0000 Subject: [PATCH 112/196] fix: use admin-david@ for GWS impersonation to fix user provisioning 403s The service account was impersonating david@ which only had Groups Admin privileges. User management requires elevated Directory API permissions that admin-david@ has. Co-Authored-By: Claude Opus 4.6 --- Pulumi.prod.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml index eb2724e..7c23cd0 100644 --- a/Pulumi.prod.yaml +++ b/Pulumi.prod.yaml @@ -1,6 +1,6 @@ encryptionsalt: v1:N7wjHl1hvH8=:v1:VT8UbYOIS0kNADr5:fSnvGX+is6htXDjLS03RDObRnzGEVA== config: - googleworkspace:impersonatedUserEmail: david@modelcontextprotocol.io + googleworkspace:impersonatedUserEmail: admin-david@modelcontextprotocol.io googleworkspace:customerId: C01dja0mo googleworkspace:credentials: secure: v1:Ntr2su0kgwp0oAh3:av1h5ir44/HNmtOQBftB6AD/0N7r+nLtIuPNnM47Xgyo71xO0GiV6oG81fT7dDhaxOwbBMWiEiOP+8s3XRiTJoAQHlqybvyHsgiprELsQfAPo2srnSDt289ggi11zPttYb/iNgOvoKwFxCfrapPv3Gnh0akbl3AgsJafs0uNToiNWmV176GaepR2JHEc7dpjIzpS6CFS8lVVl262ROInU85n1mEtaQ9eYZ69LPJn+ZQLSHAdMRROP1hv49Q26iqh4icJGmqbQQ6XGh4e5E0TgHT+/UPbuAzKnearRN+jFdqaJPQneXav9xy5W9zS4834LGa6Otawa12r5DlSIB5LunYCVDENhXcDhouwFHc7OK6bVUe2iY+557YfESZcznuYuHOq0ChPyKKJ7n5nE0Pu34nEJWMR3BARCLcGMuPIQALiecFMKDJjbVJN0ShS0nch4dDJpWH4KHNqyCs2q9h/Mume9pSu4PhZXg28VcBILh4a9B0JF2BzwqFy79OP0gmoz/B7IoIcjN28HnHrYmnK/97xrbTV7YzI2x5iYY9xwW3IlaKIrzr8f6vBAXTZmSFTOAqVWdsGgzalpqljDIqwJbh2YVQAy8NPLhfSxlkMJNPxfO0DR9AcL/kITQPL/f5WkRGU9+aur2K7gp2vkXUoObx8elnogIHpklPHRmb0obqoR8LTCD97Vt/rLL+ZaPgp2eBWwWydz1XAo78PVq53qGGbXk5lrv+i4qUxiUy8639ea4sdRJf2yGNWJb14hrpf5PBn7+bn2mSMdyY2jQIVbE0ltFYUVfPrzUNG51O39EwD9iFf/aR0lyxgO+fF8mMWOOTZleCPBMDY1t6c5u3TlipuSZF79q2LbuTnXGA8XmONiqoGyKDVIaYTNpOllFtuL105scGledqj9S2Q5bbgdRldlFXbFbGXWAYtj4A1z7gBRklb9UMJXbMyECRtfepbLu93XgKZaKjfXdRZkNtVNOjrE9KsGEUp0dkLUToKzj9tdDGxninCEUanhEV5ACTuXxGNqcvYoegYVRnGuLfCZTVMWwIezombduQDh7mEKkBRyw+XqQeRjCGHm+tZaAeo5p82cGX2bcxR2KselYfCVzP3n/yJsqp/D+STYd3bqq5Kno1f2obys2pyGhDri+scaZRZiWF7EysuYEuMC1D6EPkIo9ZEDfhgBU5RbJqC8h8DTTTLzsJa8r25L7qph/E7wVQywSLlL6e6GQ9bbepSikrNxXvftzkmEDNruN+5dVFznOEL5rTRa8Q98Dwet3i5c2PzWCzhz9/24rE0vrEOPO6NNgQ+du53wfG5uwkXEjAC4+w8nbXUrqe64y6o6AnRlo8YBwECV+qNfdpl0+6/JTP83L6aWo+u+QIiq4qpTF95SBNBt//SXk1KkYYgYNTo5Y/thjQ4ycHakHO4Mu/6Gz3+20uIOcSyhvO30Pk65scNcsTPHy1cT61cdK77fipJynVkn6rLKSWvqXMEeP6LwnioLx5WChyNAf+oubFMzlgS8zLW+kH8NxR4BIBpwbuyy+hpXXKZKNYKJtCzSUdBuV3PNZ0b0BaIEr6CyeqPtTPCfAoCxpvXipYJUEWLe6yPdorBQU0AYp3jMblrmqz/eAAPkiI5c7gTF6FAk0ezjzhvY3nVKPIvNi8Zj/5oxlt7p/kGjtUbMaQmGDrZco6TfDf0AtdERIDCUWnplKqLuz6NjIbZ7acAeyrBKyVslKNDn6tRZGk/4XrUFYgIEdWShOLZxAxI7o564n9+TycSIkz+LWYltQtRM1v5lnwNHSWXaXp7NUbhwWA5FlWPKBDOznhM0d6R5CRGfmtxE0KMQ3wac6QiODL3Zh5TJeKyFvXTh9hpSVfCafCMDtPWjzYj0X5G/DFCyNhIOf8Z8jfMBf2kh0AWI8ojZNS3c8ip1+elzLcGLDVfkLGF7rwvVDYXL/2cfamtfwR+CrljXlzT+FPRaMER8plhj6hN27UCWjILXVE7UqInNz/nkTS/YsZtVRaafCEXO1ytyV0gkXbKk00WFN6Qg3cK8HB0ITTYns8+NhstRTXewj9VJv/Kl7GV9Uz/DQX4KZW9rlJNd3XZjPYJRsHeRNXCnezTydyLpjrLYipbpYnuQcqBhE7S3nBiM+fTFrm8XJr7EBf4uIm8/9PSG52PMUiSsLHt+qMg36z+pliavLU5GXUw8RLONP2EnjI14vu8rz8BqlXhUuluWYRKeVNXprIG6pAkahqxhTmqwqYu1VIPDTPfuWJfQ9RCz5BYQEN4flTkuqGj0yYdl9CRxuL+m1BbPcZLufx+gY0gC5buiskzalfek3QhQLu2kH9P4zGuK/03jRVYlPf4IL0rt4l/e5iMV3bKB27Ds6W2An4NQB13WaujyINDiicJcRCyIQwsJt9aiLxf+NKc7N3iCLgjVw85K63WcDcW+zh8YOc89mJj/y3SOfTDAVCUjEzye9TldX8/7biavhsn4Bwdd71VkzxS/eSAJHeTXjw94FveP1d/A86LMBl/PThVEJmQMBZFjiMVw8x8xWG78NfqZDR2QeMU8meBhXrj4gwb3s2H9+H/6HcxoPB4O5kGbY4piQ3yu9MzPojVpQspdT2tgH+CAaNg3qHy3R/HHuWLxuXVGZj+2t3NoGvHrQRiS5e/9IwkQ2EA/3b20ZsBqP9F63oApr+PVnZYMbtRjJLdl02tIsMm4n5xoOI+0yP/93XSwD/rM+YXYu2PLiQRm5IkbDVu8Hg15OzIHSiGkTyPZb2/QBja10O9M/suKF7VZmS74skWZH3ETCNWyA5TpNwltafvou/1WtipHqNs45SXKJMr9iT3yI2jr/KGXlBDIih7mTiY5KN9mioAH1F5ROe0siEzBdaQe9soQsOBfFIZYzMBN/IKjuiqohG2DdDfsZqyYn0zO4GrqQ6Afv0fDY2fJJfbXCbn9i8saEI8CcSI2c2WTIxgQk+q6iOBBr0X/7WGzH7lmAHd4sDoIIi8F1QJyNlFsWj3r+5oe64jQa2XAkgVF3I6JENWpgSz6AJu7zQgnbg3ZIfF0kkwt+jRxEXsuLwdDO0XSxKBqv7Ynk0xDsV+5BkHzLv70fXh6BM8+LcK7kI8dgy8zJMLtctmAd9LmWcs7uWw8Di/EnZHZR5pDju0a+miq15yvHiJVjN6k5SoFRCVOQ== From c646e902a9d92a9992c4b5311adbe589f98ff372 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:23:20 +0000 Subject: [PATCH 113/196] fix: restrict provider oauthScopes to match domain-wide delegation The googleworkspace provider requests ~14 default OAuth scopes, but only 3 are authorized in the domain-wide delegation for the service account. The token exchange rejects requests with unauthorized scopes, causing 401 errors. Explicitly set oauthScopes to the 3 delegated scopes. Co-Authored-By: Claude Opus 4.6 --- Pulumi.prod.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml index 7c23cd0..4318ce3 100644 --- a/Pulumi.prod.yaml +++ b/Pulumi.prod.yaml @@ -5,3 +5,7 @@ config: googleworkspace:credentials: secure: v1:Ntr2su0kgwp0oAh3:av1h5ir44/HNmtOQBftB6AD/0N7r+nLtIuPNnM47Xgyo71xO0GiV6oG81fT7dDhaxOwbBMWiEiOP+8s3XRiTJoAQHlqybvyHsgiprELsQfAPo2srnSDt289ggi11zPttYb/iNgOvoKwFxCfrapPv3Gnh0akbl3AgsJafs0uNToiNWmV176GaepR2JHEc7dpjIzpS6CFS8lVVl262ROInU85n1mEtaQ9eYZ69LPJn+ZQLSHAdMRROP1hv49Q26iqh4icJGmqbQQ6XGh4e5E0TgHT+/UPbuAzKnearRN+jFdqaJPQneXav9xy5W9zS4834LGa6Otawa12r5DlSIB5LunYCVDENhXcDhouwFHc7OK6bVUe2iY+557YfESZcznuYuHOq0ChPyKKJ7n5nE0Pu34nEJWMR3BARCLcGMuPIQALiecFMKDJjbVJN0ShS0nch4dDJpWH4KHNqyCs2q9h/Mume9pSu4PhZXg28VcBILh4a9B0JF2BzwqFy79OP0gmoz/B7IoIcjN28HnHrYmnK/97xrbTV7YzI2x5iYY9xwW3IlaKIrzr8f6vBAXTZmSFTOAqVWdsGgzalpqljDIqwJbh2YVQAy8NPLhfSxlkMJNPxfO0DR9AcL/kITQPL/f5WkRGU9+aur2K7gp2vkXUoObx8elnogIHpklPHRmb0obqoR8LTCD97Vt/rLL+ZaPgp2eBWwWydz1XAo78PVq53qGGbXk5lrv+i4qUxiUy8639ea4sdRJf2yGNWJb14hrpf5PBn7+bn2mSMdyY2jQIVbE0ltFYUVfPrzUNG51O39EwD9iFf/aR0lyxgO+fF8mMWOOTZleCPBMDY1t6c5u3TlipuSZF79q2LbuTnXGA8XmONiqoGyKDVIaYTNpOllFtuL105scGledqj9S2Q5bbgdRldlFXbFbGXWAYtj4A1z7gBRklb9UMJXbMyECRtfepbLu93XgKZaKjfXdRZkNtVNOjrE9KsGEUp0dkLUToKzj9tdDGxninCEUanhEV5ACTuXxGNqcvYoegYVRnGuLfCZTVMWwIezombduQDh7mEKkBRyw+XqQeRjCGHm+tZaAeo5p82cGX2bcxR2KselYfCVzP3n/yJsqp/D+STYd3bqq5Kno1f2obys2pyGhDri+scaZRZiWF7EysuYEuMC1D6EPkIo9ZEDfhgBU5RbJqC8h8DTTTLzsJa8r25L7qph/E7wVQywSLlL6e6GQ9bbepSikrNxXvftzkmEDNruN+5dVFznOEL5rTRa8Q98Dwet3i5c2PzWCzhz9/24rE0vrEOPO6NNgQ+du53wfG5uwkXEjAC4+w8nbXUrqe64y6o6AnRlo8YBwECV+qNfdpl0+6/JTP83L6aWo+u+QIiq4qpTF95SBNBt//SXk1KkYYgYNTo5Y/thjQ4ycHakHO4Mu/6Gz3+20uIOcSyhvO30Pk65scNcsTPHy1cT61cdK77fipJynVkn6rLKSWvqXMEeP6LwnioLx5WChyNAf+oubFMzlgS8zLW+kH8NxR4BIBpwbuyy+hpXXKZKNYKJtCzSUdBuV3PNZ0b0BaIEr6CyeqPtTPCfAoCxpvXipYJUEWLe6yPdorBQU0AYp3jMblrmqz/eAAPkiI5c7gTF6FAk0ezjzhvY3nVKPIvNi8Zj/5oxlt7p/kGjtUbMaQmGDrZco6TfDf0AtdERIDCUWnplKqLuz6NjIbZ7acAeyrBKyVslKNDn6tRZGk/4XrUFYgIEdWShOLZxAxI7o564n9+TycSIkz+LWYltQtRM1v5lnwNHSWXaXp7NUbhwWA5FlWPKBDOznhM0d6R5CRGfmtxE0KMQ3wac6QiODL3Zh5TJeKyFvXTh9hpSVfCafCMDtPWjzYj0X5G/DFCyNhIOf8Z8jfMBf2kh0AWI8ojZNS3c8ip1+elzLcGLDVfkLGF7rwvVDYXL/2cfamtfwR+CrljXlzT+FPRaMER8plhj6hN27UCWjILXVE7UqInNz/nkTS/YsZtVRaafCEXO1ytyV0gkXbKk00WFN6Qg3cK8HB0ITTYns8+NhstRTXewj9VJv/Kl7GV9Uz/DQX4KZW9rlJNd3XZjPYJRsHeRNXCnezTydyLpjrLYipbpYnuQcqBhE7S3nBiM+fTFrm8XJr7EBf4uIm8/9PSG52PMUiSsLHt+qMg36z+pliavLU5GXUw8RLONP2EnjI14vu8rz8BqlXhUuluWYRKeVNXprIG6pAkahqxhTmqwqYu1VIPDTPfuWJfQ9RCz5BYQEN4flTkuqGj0yYdl9CRxuL+m1BbPcZLufx+gY0gC5buiskzalfek3QhQLu2kH9P4zGuK/03jRVYlPf4IL0rt4l/e5iMV3bKB27Ds6W2An4NQB13WaujyINDiicJcRCyIQwsJt9aiLxf+NKc7N3iCLgjVw85K63WcDcW+zh8YOc89mJj/y3SOfTDAVCUjEzye9TldX8/7biavhsn4Bwdd71VkzxS/eSAJHeTXjw94FveP1d/A86LMBl/PThVEJmQMBZFjiMVw8x8xWG78NfqZDR2QeMU8meBhXrj4gwb3s2H9+H/6HcxoPB4O5kGbY4piQ3yu9MzPojVpQspdT2tgH+CAaNg3qHy3R/HHuWLxuXVGZj+2t3NoGvHrQRiS5e/9IwkQ2EA/3b20ZsBqP9F63oApr+PVnZYMbtRjJLdl02tIsMm4n5xoOI+0yP/93XSwD/rM+YXYu2PLiQRm5IkbDVu8Hg15OzIHSiGkTyPZb2/QBja10O9M/suKF7VZmS74skWZH3ETCNWyA5TpNwltafvou/1WtipHqNs45SXKJMr9iT3yI2jr/KGXlBDIih7mTiY5KN9mioAH1F5ROe0siEzBdaQe9soQsOBfFIZYzMBN/IKjuiqohG2DdDfsZqyYn0zO4GrqQ6Afv0fDY2fJJfbXCbn9i8saEI8CcSI2c2WTIxgQk+q6iOBBr0X/7WGzH7lmAHd4sDoIIi8F1QJyNlFsWj3r+5oe64jQa2XAkgVF3I6JENWpgSz6AJu7zQgnbg3ZIfF0kkwt+jRxEXsuLwdDO0XSxKBqv7Ynk0xDsV+5BkHzLv70fXh6BM8+LcK7kI8dgy8zJMLtctmAd9LmWcs7uWw8Di/EnZHZR5pDju0a+miq15yvHiJVjN6k5SoFRCVOQ== github:owner: modelcontextprotocol + googleworkspace:oauthScopes: + - https://www.googleapis.com/auth/admin.directory.user + - https://www.googleapis.com/auth/admin.directory.group + - https://www.googleapis.com/auth/apps.groups.settings From 24adfb758a66a780ecce5d7c0185921adc2b8c61 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:32:19 +0000 Subject: [PATCH 114/196] more stuff --- Pulumi.prod.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml index 4318ce3..57848ac 100644 --- a/Pulumi.prod.yaml +++ b/Pulumi.prod.yaml @@ -8,4 +8,5 @@ config: googleworkspace:oauthScopes: - https://www.googleapis.com/auth/admin.directory.user - https://www.googleapis.com/auth/admin.directory.group + - https://www.googleapis.com/auth/admin.directory.orgunit - https://www.googleapis.com/auth/apps.groups.settings From b1c85aff0c0274d75d11d3faf7e4754a81128312 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:33:27 +0000 Subject: [PATCH 115/196] remove unecessary delegation --- Pulumi.prod.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Pulumi.prod.yaml b/Pulumi.prod.yaml index 57848ac..eb4ad25 100644 --- a/Pulumi.prod.yaml +++ b/Pulumi.prod.yaml @@ -1,12 +1,6 @@ encryptionsalt: v1:N7wjHl1hvH8=:v1:VT8UbYOIS0kNADr5:fSnvGX+is6htXDjLS03RDObRnzGEVA== config: - googleworkspace:impersonatedUserEmail: admin-david@modelcontextprotocol.io googleworkspace:customerId: C01dja0mo googleworkspace:credentials: secure: v1:Ntr2su0kgwp0oAh3:av1h5ir44/HNmtOQBftB6AD/0N7r+nLtIuPNnM47Xgyo71xO0GiV6oG81fT7dDhaxOwbBMWiEiOP+8s3XRiTJoAQHlqybvyHsgiprELsQfAPo2srnSDt289ggi11zPttYb/iNgOvoKwFxCfrapPv3Gnh0akbl3AgsJafs0uNToiNWmV176GaepR2JHEc7dpjIzpS6CFS8lVVl262ROInU85n1mEtaQ9eYZ69LPJn+ZQLSHAdMRROP1hv49Q26iqh4icJGmqbQQ6XGh4e5E0TgHT+/UPbuAzKnearRN+jFdqaJPQneXav9xy5W9zS4834LGa6Otawa12r5DlSIB5LunYCVDENhXcDhouwFHc7OK6bVUe2iY+557YfESZcznuYuHOq0ChPyKKJ7n5nE0Pu34nEJWMR3BARCLcGMuPIQALiecFMKDJjbVJN0ShS0nch4dDJpWH4KHNqyCs2q9h/Mume9pSu4PhZXg28VcBILh4a9B0JF2BzwqFy79OP0gmoz/B7IoIcjN28HnHrYmnK/97xrbTV7YzI2x5iYY9xwW3IlaKIrzr8f6vBAXTZmSFTOAqVWdsGgzalpqljDIqwJbh2YVQAy8NPLhfSxlkMJNPxfO0DR9AcL/kITQPL/f5WkRGU9+aur2K7gp2vkXUoObx8elnogIHpklPHRmb0obqoR8LTCD97Vt/rLL+ZaPgp2eBWwWydz1XAo78PVq53qGGbXk5lrv+i4qUxiUy8639ea4sdRJf2yGNWJb14hrpf5PBn7+bn2mSMdyY2jQIVbE0ltFYUVfPrzUNG51O39EwD9iFf/aR0lyxgO+fF8mMWOOTZleCPBMDY1t6c5u3TlipuSZF79q2LbuTnXGA8XmONiqoGyKDVIaYTNpOllFtuL105scGledqj9S2Q5bbgdRldlFXbFbGXWAYtj4A1z7gBRklb9UMJXbMyECRtfepbLu93XgKZaKjfXdRZkNtVNOjrE9KsGEUp0dkLUToKzj9tdDGxninCEUanhEV5ACTuXxGNqcvYoegYVRnGuLfCZTVMWwIezombduQDh7mEKkBRyw+XqQeRjCGHm+tZaAeo5p82cGX2bcxR2KselYfCVzP3n/yJsqp/D+STYd3bqq5Kno1f2obys2pyGhDri+scaZRZiWF7EysuYEuMC1D6EPkIo9ZEDfhgBU5RbJqC8h8DTTTLzsJa8r25L7qph/E7wVQywSLlL6e6GQ9bbepSikrNxXvftzkmEDNruN+5dVFznOEL5rTRa8Q98Dwet3i5c2PzWCzhz9/24rE0vrEOPO6NNgQ+du53wfG5uwkXEjAC4+w8nbXUrqe64y6o6AnRlo8YBwECV+qNfdpl0+6/JTP83L6aWo+u+QIiq4qpTF95SBNBt//SXk1KkYYgYNTo5Y/thjQ4ycHakHO4Mu/6Gz3+20uIOcSyhvO30Pk65scNcsTPHy1cT61cdK77fipJynVkn6rLKSWvqXMEeP6LwnioLx5WChyNAf+oubFMzlgS8zLW+kH8NxR4BIBpwbuyy+hpXXKZKNYKJtCzSUdBuV3PNZ0b0BaIEr6CyeqPtTPCfAoCxpvXipYJUEWLe6yPdorBQU0AYp3jMblrmqz/eAAPkiI5c7gTF6FAk0ezjzhvY3nVKPIvNi8Zj/5oxlt7p/kGjtUbMaQmGDrZco6TfDf0AtdERIDCUWnplKqLuz6NjIbZ7acAeyrBKyVslKNDn6tRZGk/4XrUFYgIEdWShOLZxAxI7o564n9+TycSIkz+LWYltQtRM1v5lnwNHSWXaXp7NUbhwWA5FlWPKBDOznhM0d6R5CRGfmtxE0KMQ3wac6QiODL3Zh5TJeKyFvXTh9hpSVfCafCMDtPWjzYj0X5G/DFCyNhIOf8Z8jfMBf2kh0AWI8ojZNS3c8ip1+elzLcGLDVfkLGF7rwvVDYXL/2cfamtfwR+CrljXlzT+FPRaMER8plhj6hN27UCWjILXVE7UqInNz/nkTS/YsZtVRaafCEXO1ytyV0gkXbKk00WFN6Qg3cK8HB0ITTYns8+NhstRTXewj9VJv/Kl7GV9Uz/DQX4KZW9rlJNd3XZjPYJRsHeRNXCnezTydyLpjrLYipbpYnuQcqBhE7S3nBiM+fTFrm8XJr7EBf4uIm8/9PSG52PMUiSsLHt+qMg36z+pliavLU5GXUw8RLONP2EnjI14vu8rz8BqlXhUuluWYRKeVNXprIG6pAkahqxhTmqwqYu1VIPDTPfuWJfQ9RCz5BYQEN4flTkuqGj0yYdl9CRxuL+m1BbPcZLufx+gY0gC5buiskzalfek3QhQLu2kH9P4zGuK/03jRVYlPf4IL0rt4l/e5iMV3bKB27Ds6W2An4NQB13WaujyINDiicJcRCyIQwsJt9aiLxf+NKc7N3iCLgjVw85K63WcDcW+zh8YOc89mJj/y3SOfTDAVCUjEzye9TldX8/7biavhsn4Bwdd71VkzxS/eSAJHeTXjw94FveP1d/A86LMBl/PThVEJmQMBZFjiMVw8x8xWG78NfqZDR2QeMU8meBhXrj4gwb3s2H9+H/6HcxoPB4O5kGbY4piQ3yu9MzPojVpQspdT2tgH+CAaNg3qHy3R/HHuWLxuXVGZj+2t3NoGvHrQRiS5e/9IwkQ2EA/3b20ZsBqP9F63oApr+PVnZYMbtRjJLdl02tIsMm4n5xoOI+0yP/93XSwD/rM+YXYu2PLiQRm5IkbDVu8Hg15OzIHSiGkTyPZb2/QBja10O9M/suKF7VZmS74skWZH3ETCNWyA5TpNwltafvou/1WtipHqNs45SXKJMr9iT3yI2jr/KGXlBDIih7mTiY5KN9mioAH1F5ROe0siEzBdaQe9soQsOBfFIZYzMBN/IKjuiqohG2DdDfsZqyYn0zO4GrqQ6Afv0fDY2fJJfbXCbn9i8saEI8CcSI2c2WTIxgQk+q6iOBBr0X/7WGzH7lmAHd4sDoIIi8F1QJyNlFsWj3r+5oe64jQa2XAkgVF3I6JENWpgSz6AJu7zQgnbg3ZIfF0kkwt+jRxEXsuLwdDO0XSxKBqv7Ynk0xDsV+5BkHzLv70fXh6BM8+LcK7kI8dgy8zJMLtctmAd9LmWcs7uWw8Di/EnZHZR5pDju0a+miq15yvHiJVjN6k5SoFRCVOQ== github:owner: modelcontextprotocol - googleworkspace:oauthScopes: - - https://www.googleapis.com/auth/admin.directory.user - - https://www.googleapis.com/auth/admin.directory.group - - https://www.googleapis.com/auth/admin.directory.orgunit - - https://www.googleapis.com/auth/apps.groups.settings From 9fe97a1d2af397ea21272f7baed479ff37718cdd Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:41:53 +0000 Subject: [PATCH 116/196] fix: create OrgUnit resource before provisioning GWS users The orgUnitPath '/Model Context Protocol' was hardcoded but the OU didn't exist in Pulumi state, causing INVALID_OU_ID errors for all user creates/updates. Create it as a Pulumi resource and wire up dependsOn so users are provisioned after the OU exists. Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/google.ts b/src/google.ts index 43cce9a..c133c84 100644 --- a/src/google.ts +++ b/src/google.ts @@ -48,6 +48,13 @@ ROLES.forEach((role: Role) => { }); }); +// Create the organizational unit for MCP users +const mcpOrgUnit = new gworkspace.OrgUnit('mcp-org-unit', { + name: 'Model Context Protocol', + parentOrgUnitPath: '/', + description: 'Organizational unit for MCP team members (Managed by github.com/modelcontextprotocol/access)', +}); + // Provision Google Workspace user accounts for members in roles with provisionUser const provisionedUsersByEmail: Record = {}; const newUserPasswords: Record> = {}; @@ -76,9 +83,9 @@ MEMBERS.forEach((member) => { { primaryEmail, name: { familyName: member.lastName!, givenName: member.firstName! }, - orgUnitPath: '/Model Context Protocol', + orgUnitPath: mcpOrgUnit.orgUnitPath, }, - { import: primaryEmail } + { import: primaryEmail, dependsOn: [mcpOrgUnit] } ); provisionedUsersByEmail[primaryEmail] = user; } else { @@ -91,14 +98,18 @@ MEMBERS.forEach((member) => { crypto.createHash('sha1').update(plaintext).digest('hex') ); - const user = new gworkspace.User(`gws-user-${member.googleEmailPrefix}`, { - primaryEmail, - name: { familyName: member.lastName!, givenName: member.firstName! }, - password: hashedPassword, - hashFunction: 'SHA-1', - changePasswordAtNextLogin: true, - orgUnitPath: '/Model Context Protocol', - }); + const user = new gworkspace.User( + `gws-user-${member.googleEmailPrefix}`, + { + primaryEmail, + name: { familyName: member.lastName!, givenName: member.firstName! }, + password: hashedPassword, + hashFunction: 'SHA-1', + changePasswordAtNextLogin: true, + orgUnitPath: mcpOrgUnit.orgUnitPath, + }, + { dependsOn: [mcpOrgUnit] } + ); provisionedUsersByEmail[primaryEmail] = user; // Track password for export so an admin can retrieve it From dca636df6533f8a5049d1fac0a6bf4b0398723f0 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 19 Feb 2026 23:59:44 +0000 Subject: [PATCH 117/196] fix: import existing OrgUnit by ID 03ph8a2z0nc6rsr Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/google.ts b/src/google.ts index c133c84..bfb5ae7 100644 --- a/src/google.ts +++ b/src/google.ts @@ -49,11 +49,15 @@ ROLES.forEach((role: Role) => { }); // Create the organizational unit for MCP users -const mcpOrgUnit = new gworkspace.OrgUnit('mcp-org-unit', { - name: 'Model Context Protocol', - parentOrgUnitPath: '/', - description: 'Organizational unit for MCP team members (Managed by github.com/modelcontextprotocol/access)', -}); +const mcpOrgUnit = new gworkspace.OrgUnit( + 'mcp-org-unit', + { + name: 'Model Context Protocol', + parentOrgUnitPath: '/', + description: 'Organizational unit for MCP team members (Managed by github.com/modelcontextprotocol/access)', + }, + { import: 'id:03ph8a2z0nc6rsr' } +); // Provision Google Workspace user accounts for members in roles with provisionUser const provisionedUsersByEmail: Record = {}; From 9d8906075da4ad8490b2539bc9692fada346311a Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:04:08 +0000 Subject: [PATCH 118/196] fix: drop parentOrgUnitPath and description from OrgUnit to avoid update after import Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/google.ts b/src/google.ts index bfb5ae7..c50e613 100644 --- a/src/google.ts +++ b/src/google.ts @@ -53,8 +53,6 @@ const mcpOrgUnit = new gworkspace.OrgUnit( 'mcp-org-unit', { name: 'Model Context Protocol', - parentOrgUnitPath: '/', - description: 'Organizational unit for MCP team members (Managed by github.com/modelcontextprotocol/access)', }, { import: 'id:03ph8a2z0nc6rsr' } ); From 48904bb454744a5e913fb118fcbb776689a257ec Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:05:51 +0000 Subject: [PATCH 119/196] fix: add back parentOrgUnitPath (required by provider) Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/google.ts b/src/google.ts index c50e613..8e7ab26 100644 --- a/src/google.ts +++ b/src/google.ts @@ -53,6 +53,7 @@ const mcpOrgUnit = new gworkspace.OrgUnit( 'mcp-org-unit', { name: 'Model Context Protocol', + parentOrgUnitPath: '/', }, { import: 'id:03ph8a2z0nc6rsr' } ); From 09bc0fc0b8157f4475c1e33527d2d57c44a3ed4d Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:09:53 +0000 Subject: [PATCH 120/196] fix: add ignoreChanges and match existing description to prevent OrgUnit update Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/google.ts b/src/google.ts index 8e7ab26..2fddc44 100644 --- a/src/google.ts +++ b/src/google.ts @@ -53,9 +53,10 @@ const mcpOrgUnit = new gworkspace.OrgUnit( 'mcp-org-unit', { name: 'Model Context Protocol', + description: 'Model Context Protocol', parentOrgUnitPath: '/', }, - { import: 'id:03ph8a2z0nc6rsr' } + { import: 'id:03ph8a2z0nc6rsr', ignoreChanges: ['parentOrgUnitPath', 'description'] } ); // Provision Google Workspace user accounts for members in roles with provisionUser From 56ccba8a79b455a7f008fb57e5b8436d40af0231 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:11:52 +0000 Subject: [PATCH 121/196] fix: only ignoreChanges on description, parentOrgUnitPath is required by provider Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/google.ts b/src/google.ts index 2fddc44..0f95d85 100644 --- a/src/google.ts +++ b/src/google.ts @@ -56,7 +56,7 @@ const mcpOrgUnit = new gworkspace.OrgUnit( description: 'Model Context Protocol', parentOrgUnitPath: '/', }, - { import: 'id:03ph8a2z0nc6rsr', ignoreChanges: ['parentOrgUnitPath', 'description'] } + { import: 'id:03ph8a2z0nc6rsr', ignoreChanges: ['description'] } ); // Provision Google Workspace user accounts for members in roles with provisionUser From f916271ed9f7756c3a4dfa920e1cb8e18fb68472 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:20:06 +0000 Subject: [PATCH 122/196] fix: ignore recoveryEmail and recoveryPhone on imported GWS users These are user-managed fields that shouldn't be touched by IaC. Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/google.ts b/src/google.ts index 0f95d85..74b5713 100644 --- a/src/google.ts +++ b/src/google.ts @@ -89,7 +89,7 @@ MEMBERS.forEach((member) => { name: { familyName: member.lastName!, givenName: member.firstName! }, orgUnitPath: mcpOrgUnit.orgUnitPath, }, - { import: primaryEmail, dependsOn: [mcpOrgUnit] } + { import: primaryEmail, dependsOn: [mcpOrgUnit], ignoreChanges: ['recoveryEmail', 'recoveryPhone'] } ); provisionedUsersByEmail[primaryEmail] = user; } else { From 520ccc7bd8d572d915b3cbcf301009ac72b5f67b Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:22:17 +0000 Subject: [PATCH 123/196] fix: ignoreChanges on all user-managed fields for GWS users After creation/import we only care that the user exists and is in the right groups. All profile settings (name, recovery info, password, org unit, etc.) are left to users and admins to manage directly. Co-Authored-By: Claude Opus 4.6 --- src/google.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/google.ts b/src/google.ts index 74b5713..a04139e 100644 --- a/src/google.ts +++ b/src/google.ts @@ -89,7 +89,39 @@ MEMBERS.forEach((member) => { name: { familyName: member.lastName!, givenName: member.firstName! }, orgUnitPath: mcpOrgUnit.orgUnitPath, }, - { import: primaryEmail, dependsOn: [mcpOrgUnit], ignoreChanges: ['recoveryEmail', 'recoveryPhone'] } + { + import: primaryEmail, + dependsOn: [mcpOrgUnit], + ignoreChanges: [ + 'recoveryEmail', + 'recoveryPhone', + 'password', + 'hashFunction', + 'changePasswordAtNextLogin', + 'orgUnitPath', + 'archived', + 'suspended', + 'isAdmin', + 'includeInGlobalAddressList', + 'ipAllowlist', + 'addresses', + 'aliases', + 'customSchemas', + 'emails', + 'externalIds', + 'ims', + 'keywords', + 'languages', + 'locations', + 'organizations', + 'phones', + 'posixAccounts', + 'relations', + 'sshPublicKeys', + 'websites', + 'name', + ], + } ); provisionedUsersByEmail[primaryEmail] = user; } else { @@ -112,7 +144,38 @@ MEMBERS.forEach((member) => { changePasswordAtNextLogin: true, orgUnitPath: mcpOrgUnit.orgUnitPath, }, - { dependsOn: [mcpOrgUnit] } + { + dependsOn: [mcpOrgUnit], + ignoreChanges: [ + 'recoveryEmail', + 'recoveryPhone', + 'password', + 'hashFunction', + 'changePasswordAtNextLogin', + 'orgUnitPath', + 'archived', + 'suspended', + 'isAdmin', + 'includeInGlobalAddressList', + 'ipAllowlist', + 'addresses', + 'aliases', + 'customSchemas', + 'emails', + 'externalIds', + 'ims', + 'keywords', + 'languages', + 'locations', + 'organizations', + 'phones', + 'posixAccounts', + 'relations', + 'sshPublicKeys', + 'websites', + 'name', + ], + } ); provisionedUsersByEmail[primaryEmail] = user; From bfa4a115ddcb7cb218d1d5a7d2ae3c729a15677c Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:47:45 +0000 Subject: [PATCH 124/196] fix: mark pja as existingGWSUser to import instead of create Co-Authored-By: Claude Opus 4.6 --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index 570521c..629e149 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -517,6 +517,7 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Peter', lastName: 'Alexander', googleEmailPrefix: 'pja', + existingGWSUser: true, memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { From ec187737465624c5527025d429eae3923cd53b7a Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 20 Feb 2026 00:55:09 +0000 Subject: [PATCH 125/196] fix: stop importing existing GWS users into Pulumi state The GWS provider validates email types during import, but GWS itself returns empty types for primary/alias emails. This causes unrecoverable validation errors on every new user import. Instead, skip managing existing users entirely and let group memberships be created directly. Also marks den as existingGWSUser. Co-Authored-By: Claude Opus 4.6 --- src/config/users.ts | 1 + src/google.ts | 49 ++++++--------------------------------------- 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 629e149..4a25191 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -358,6 +358,7 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Den', lastName: 'Delimarsky', googleEmailPrefix: 'den', + existingGWSUser: true, memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, diff --git a/src/google.ts b/src/google.ts index a04139e..4c6c299 100644 --- a/src/google.ts +++ b/src/google.ts @@ -81,49 +81,12 @@ MEMBERS.forEach((member) => { const primaryEmail = `${member.googleEmailPrefix}@modelcontextprotocol.io`; if (member.existingGWSUser) { - // Import existing user into Pulumi state without recreating - const user = new gworkspace.User( - `gws-user-${member.googleEmailPrefix}`, - { - primaryEmail, - name: { familyName: member.lastName!, givenName: member.firstName! }, - orgUnitPath: mcpOrgUnit.orgUnitPath, - }, - { - import: primaryEmail, - dependsOn: [mcpOrgUnit], - ignoreChanges: [ - 'recoveryEmail', - 'recoveryPhone', - 'password', - 'hashFunction', - 'changePasswordAtNextLogin', - 'orgUnitPath', - 'archived', - 'suspended', - 'isAdmin', - 'includeInGlobalAddressList', - 'ipAllowlist', - 'addresses', - 'aliases', - 'customSchemas', - 'emails', - 'externalIds', - 'ims', - 'keywords', - 'languages', - 'locations', - 'organizations', - 'phones', - 'posixAccounts', - 'relations', - 'sshPublicKeys', - 'websites', - 'name', - ], - } - ); - provisionedUsersByEmail[primaryEmail] = user; + // Existing GWS users are not managed by Pulumi — the GWS provider's import + // validation rejects empty email types that GWS itself sets on primary/alias + // emails, and there's no way to fix this at the provider level. + // Group memberships for these users are created without dependsOn since the + // user already exists in GWS. + return; } else { // Create new user with random password const password = new random.RandomPassword(`gws-pwd-${member.googleEmailPrefix}`, { From 2eebd26803218d6b844db9b8d8bb04aa26e1a104 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 23 Feb 2026 14:24:38 +0000 Subject: [PATCH 126/196] add nick and caitie to gwp users --- src/config/users.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 4a25191..08141bd 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -5,7 +5,9 @@ export const MEMBERS: readonly Member[] = [ { github: '000-000-000-000-000', discord: '1360717264051241071', - skipGoogleUserProvisioning: true, + firstName: 'Nick', + lastName: 'Aldridge', + googleEmailPrefix: 'nick', memberOf: [ROLE_IDS.CORE_MAINTAINERS], }, { @@ -77,7 +79,9 @@ export const MEMBERS: readonly Member[] = [ github: 'caitiem20', email: 'caitie.mccaffrey@microsoft.com', discord: '1425586366288494722', - skipGoogleUserProvisioning: true, + firstName: 'Caitie', + lastName: 'McCaffrey', + googleEmailPrefix: 'caitie', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { From f4bae082ee0f14ae0a107c71e896fd34eb309c86 Mon Sep 17 00:00:00 2001 From: Maksym Mova Date: Sat, 28 Feb 2026 16:57:37 +0200 Subject: [PATCH 127/196] Update swift-sdk maintainers (#65) - movetz Maksym Mova (MacPaw) - discord id added - carlpeaslee - removed - mattt - removed Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 08141bd..a41ef91 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -84,11 +84,6 @@ export const MEMBERS: readonly Member[] = [ googleEmailPrefix: 'caitie', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, - { - github: 'carlpeaslee', - discord: '288330838951723009', - memberOf: [ROLE_IDS.SWIFT_SDK], - }, { github: 'chemicL', discord: '1346243721271971923', @@ -406,10 +401,6 @@ export const MEMBERS: readonly Member[] = [ github: 'marshallofsound', memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, - { - github: 'mattt', - memberOf: [ROLE_IDS.SWIFT_SDK], - }, { github: 'mattzcarey', memberOf: [ROLE_IDS.TYPESCRIPT_SDK], @@ -430,6 +421,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'movetz', + discord: '1427569183427919906', memberOf: [ROLE_IDS.SWIFT_SDK], }, { From c96a52bb953f7971e8d1a265caa114323c1c72ff Mon Sep 17 00:00:00 2001 From: Den Delimarsky Date: Mon, 2 Mar 2026 20:17:17 -0800 Subject: [PATCH 128/196] Add personal account with the right access level (#70) --- src/config/users.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index a41ef91..618eff4 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -141,6 +141,25 @@ export const MEMBERS: readonly Member[] = [ discord: '267646459187298305', memberOf: [ROLE_IDS.RUST_SDK], }, + { + github: 'dend', + skipGoogleUserProvisioning: true, + memberOf: [ + ROLE_IDS.AUTH_MAINTAINERS, + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.CSHARP_SDK, + ROLE_IDS.ADMINISTRATORS, + ROLE_IDS.GO_SDK, + ROLE_IDS.FINANCIAL_SERVICES_IG, + ROLE_IDS.MODERATORS, + ROLE_IDS.PHP_SDK, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.SECURITY_WG, + ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.TYPESCRIPT_SDK, + ], + }, { github: 'devcrocod', memberOf: [ROLE_IDS.KOTLIN_SDK], From 9ecc452da66e8ed911cf2efae91811865b9de6b3 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 7 Mar 2026 22:44:59 +0000 Subject: [PATCH 129/196] Enable opt-in Google Workspace accounts for maintainers Adds a google config with provisionUser to the MAINTAINERS role. Runtime provisioning already gates on firstName/lastName/googleEmailPrefix, so maintainers opt in by sending a PR adding those fields to their entry in users.ts. Missing fields means no account is created. Relaxes the validator and tests to treat missing GWS fields as implicit opt-out rather than a hard error, so existing maintainers do not need skipGoogleUserProvisioning set up front. Also documents the opt-in flow in the README. --- README.md | 18 ++++++++++++++++++ scripts/test-config.ts | 4 ++-- scripts/validate-config.ts | 7 ------- src/config/roles.ts | 4 +++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e07859b..5375bcc 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,24 @@ Infrastructure as Code for managing access to MCP community resources using Pulu - **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: true` accept emails from anyone (including external users) and notify all members. External posts are moderated for security. +- **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`](src/config/users.ts): + +```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 diff --git a/scripts/test-config.ts b/scripts/test-config.ts index edffdca..108e57e 100644 --- a/scripts/test-config.ts +++ b/scripts/test-config.ts @@ -99,7 +99,7 @@ test('googleEmailPrefix values are unique', () => { return prefixes.length === new Set(prefixes).size; }); -test('ProvisionUser members are explicit: fields present or skip flag set', () => { +test('skipGoogleUserProvisioning is only used in provisionUser roles and without fields', () => { const provisionRoleIds = new Set(ROLES.filter((r) => r.google?.provisionUser).map((r) => r.id)); return MEMBERS.every((member) => { const inProvisionRole = member.memberOf.some((id) => provisionRoleIds.has(id)); @@ -110,7 +110,7 @@ test('ProvisionUser members are explicit: fields present or skip flag set', () = member.lastName && member.googleEmailPrefix ); - return hasProvisioningFields !== !!member.skipGoogleUserProvisioning; + return !(hasProvisioningFields && member.skipGoogleUserProvisioning); }); }); diff --git a/scripts/validate-config.ts b/scripts/validate-config.ts index 4f8c748..168c00f 100644 --- a/scripts/validate-config.ts +++ b/scripts/validate-config.ts @@ -154,13 +154,6 @@ console.log('Validating Google Workspace user provisioning fields...'); ); hasErrors = true; } - - if (inProvisionUserRole && !hasProvisioningFields && !member.skipGoogleUserProvisioning) { - console.error( - `ERROR: Member "${memberId}" is in a provisionUser role but is missing Google user fields. Add fields or set skipGoogleUserProvisioning: true` - ); - hasErrors = true; - } } } diff --git a/src/config/roles.ts b/src/config/roles.ts index 318bb36..8e18b8f 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -102,7 +102,9 @@ export const ROLES: readonly Role[] = [ id: ROLE_IDS.MAINTAINERS, description: 'General maintainers', discord: { role: 'maintainers (synced)' }, - // Discord only - general maintainer role + // GWS user accounts are opt-in: maintainers add firstName/lastName/googleEmailPrefix + // to their entry in users.ts via PR to get an @modelcontextprotocol.io account + google: { group: 'maintainers', provisionUser: true }, }, { id: ROLE_IDS.DOCS_MAINTAINERS, From d3181e829ac5b91e4fbabbaadb61f1bfc4a7ca2f Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sun, 8 Mar 2026 16:29:59 -0400 Subject: [PATCH 130/196] In users.ts - add firstName, lastName, and googleEmailPrefix to Cliff Hall's entry as per DSP --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 618eff4..92af9b8 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -110,6 +110,9 @@ export const MEMBERS: readonly Member[] = [ github: 'cliffhall', email: 'cliff@futurescale.com', discord: '501498061965754380', + firstName: 'Cliff', + lastName: 'Hall', + googleEmailPrefix: 'cliff', memberOf: [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.MAINTAINERS, From e090d8c8fa396d22f6cd4340e54d935be62f1ced Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 9 Mar 2026 03:11:55 -0700 Subject: [PATCH 131/196] Add org email details for Ola Hungerford and Luca Chang (#72) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add evalstate and cliffhall to docs-maintaners team 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Add org email details for Ola Hungerford and Luca Chang Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 92af9b8..cbfd7aa 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -399,6 +399,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'LucaButBoring', discord: '1366470072729866252', + firstName: 'Luca', + lastName: 'Chang', + googleEmailPrefix: 'luca', memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.AGENTS_IG, ROLE_IDS.WORKING_GROUPS], }, { @@ -484,6 +487,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'olaservo', discord: '1079841769946095620', + firstName: 'Ola', + lastName: 'Hungerford', + googleEmailPrefix: 'ola', memberOf: [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, From d024641022381323c4db2261f83202c77142da5a Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 9 Mar 2026 10:24:14 +0000 Subject: [PATCH 132/196] fix: resolve GWS user creation timeout in Deploy workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hashicorp/googleworkspace provider has a known eventual-consistency bug (issue #387): after creating a user, it polls for 4 consecutive identical API etags before marking the resource created. Google's API frequently flip-flops etags, so the check never passes and the 300s default timeout trips — even though the user was actually created. This caused cliff@ creation to timeout, leaving the user in GWS but not in Pulumi state; subsequent runs fail with 409 Entity already exists. Two changes: - Mark cliff as existingGWSUser so Pulumi stops trying to create him (matches the pattern used for other stranded users). - Switch to the SamuZad/googleworkspace fork (v0.11.1), which lowers numConsistent from 4 to 2, preventing the timeout for future user creations. The upstream provider has been unmaintained for 2+ years. --- Pulumi.yaml | 3 ++- src/config/users.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Pulumi.yaml b/Pulumi.yaml index 9c99e4f..eac338e 100644 --- a/Pulumi.yaml +++ b/Pulumi.yaml @@ -6,4 +6,5 @@ packages: source: terraform-provider version: 0.14.0 parameters: - - hashicorp/googleworkspace + - SamuZad/googleworkspace + - 0.11.1 diff --git a/src/config/users.ts b/src/config/users.ts index cbfd7aa..f22f24a 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -113,6 +113,7 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Cliff', lastName: 'Hall', googleEmailPrefix: 'cliff', + existingGWSUser: true, memberOf: [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.MAINTAINERS, From cd546092a6bc77a725515db69de25b8b930b4bea Mon Sep 17 00:00:00 2001 From: shaun smith <1936278+evalstate@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:06:38 +0000 Subject: [PATCH 133/196] add my details for google workspace (#73) --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index f22f24a..2ac428b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -232,6 +232,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'evalstate', discord: '779268016121577492', + firstName: 'Shaun', + lastName: 'Smith', + googleEmailPrefix: 'shaun.smith', memberOf: [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, From 1465ade63ca3bb9ca6c64d00c5ebc78d241fbae7 Mon Sep 17 00:00:00 2001 From: Konstantin Konstantinov Date: Mon, 9 Mar 2026 18:17:12 +0200 Subject: [PATCH 134/196] add firstName, lastName, googleEmailPrefix in users.ts (#74) * add firstName, lastName, googleEmailPrefix in users.ts * prettier fix --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 2ac428b..1e496fd 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -351,6 +351,9 @@ export const MEMBERS: readonly Member[] = [ github: 'KKonstantinov', discord: '390932438903422987', memberOf: [ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.TYPESCRIPT_SDK], + firstName: 'Konstantin', + lastName: 'Konstantinov', + googleEmailPrefix: 'konstantin', }, { github: 'Kludex', From 8b74f20a075c02143774da451be29679b759504f Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 9 Mar 2026 17:40:36 +0000 Subject: [PATCH 135/196] add konstantinov to maintainers --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 1e496fd..f150444 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -350,7 +350,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'KKonstantinov', discord: '390932438903422987', - memberOf: [ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.TYPESCRIPT_SDK], + memberOf: [ROLE_IDS.INSPECTOR_MAINTAINERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.TYPESCRIPT_SDK], firstName: 'Konstantin', lastName: 'Konstantinov', googleEmailPrefix: 'konstantin', From 6ce914c5d54fe6bda679ba62a5f47ae509701229 Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Wed, 11 Mar 2026 18:34:22 +0530 Subject: [PATCH 136/196] Enable sambhav for Google workspace (#75) --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index f150444..d979080 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -575,6 +575,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'sambhav', email: 'sambhavs.email@gmail.com', + firstName: 'Sambhav', + lastName: 'Kothari', + googleEmailPrefix: 'sambhav', discord: '840109459212206090', memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG], }, From f830732261285a283657bb9e0362b64a9ef5068f Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Fri, 13 Mar 2026 06:39:56 -0700 Subject: [PATCH 137/196] Update csharp-sdk repo access to use team assignments (#76) * Add csharp-sdk-admin team and split csharp-sdk repo permissions Introduce a csharp-sdk-admin team for admin access to the csharp-sdk repo, while the existing csharp-sdk team is changed to push (write) access. This separates admin privileges from general write access. - Add CSHARP_SDK_ADMIN role to roleIds.ts and roles.ts - Update repoAccess.ts: csharp-sdk-admin (admin), csharp-sdk (push), PederHP (triage) - Add jeffhandley, ericstj, jozkee, tarekgh as new members in users.ts - Move dend/localden from csharp-sdk to csharp-sdk-admin team - Add csharp-sdk-admin membership for halter73, mikekistler, stephentoub - Remove MackinnonBuck (admin) direct access per request Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix alphabetical ordering of new users in users.ts Move ericstj after erain and jeffhandley before jenn-newton to satisfy the case-insensitive alphabetical sort check in validate-config.ts. :house: Remote-Dev: homespace --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Den Delimarsky --- src/config/repoAccess.ts | 11 ++++------- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 26 +++++++++++++++++++++----- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 7f53e68..e215255 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -184,14 +184,11 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'csharp-sdk', - teams: [{ team: 'csharp-sdk', permission: 'admin' }], - users: [ - { username: 'jeffhandley', permission: 'admin' }, - { username: 'MackinnonBuck', permission: 'admin' }, - { username: 'jozkee', permission: 'admin' }, - { username: 'PederHP', permission: 'triage' }, - { username: 'tarekgh', permission: 'push' }, + teams: [ + { team: 'csharp-sdk-admin', permission: 'admin' }, + { team: 'csharp-sdk', permission: 'push' }, ], + users: [{ username: 'PederHP', permission: 'triage' }], }, { repository: 'go-sdk', diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index b63b54b..325c9c6 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -29,6 +29,7 @@ export const ROLE_IDS = { // =================== SDK_MAINTAINERS: 'sdk-maintainers', CSHARP_SDK: 'csharp-sdk', + CSHARP_SDK_ADMIN: 'csharp-sdk-admin', GO_SDK: 'go-sdk', JAVA_SDK: 'java-sdk', KOTLIN_SDK: 'kotlin-sdk', diff --git a/src/config/roles.ts b/src/config/roles.ts index 8e18b8f..2b916a1 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -160,6 +160,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'csharp-sdk', parent: ROLE_IDS.SDK_MAINTAINERS }, discord: { role: 'c# sdk maintainers (synced)' }, }, + { + id: ROLE_IDS.CSHARP_SDK_ADMIN, + description: 'C# SDK repository admins', + github: { team: 'csharp-sdk-admin', parent: ROLE_IDS.CSHARP_SDK }, + // GitHub only - for repo admin access + }, { id: ROLE_IDS.GO_SDK, description: 'The Go SDK Team', diff --git a/src/config/users.ts b/src/config/users.ts index d979080..2442a0c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -152,7 +152,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, - ROLE_IDS.CSHARP_SDK, + ROLE_IDS.CSHARP_SDK_ADMIN, ROLE_IDS.ADMINISTRATORS, ROLE_IDS.GO_SDK, ROLE_IDS.FINANCIAL_SERVICES_IG, @@ -229,6 +229,10 @@ export const MEMBERS: readonly Member[] = [ github: 'erain', memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], }, + { + github: 'ericstj', + memberOf: [ROLE_IDS.CSHARP_SDK], + }, { github: 'evalstate', discord: '779268016121577492', @@ -262,7 +266,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'halter73', - memberOf: [ROLE_IDS.CSHARP_SDK], + memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], }, { github: 'herczyn', @@ -295,6 +299,10 @@ export const MEMBERS: readonly Member[] = [ discord: '773276903364755518', memberOf: [ROLE_IDS.GO_SDK], }, + { + github: 'jeffhandley', + memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], + }, { github: 'jenn-newton', memberOf: [ROLE_IDS.SECURITY_WG], @@ -325,6 +333,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MCP_APPS_SDK, ], }, + { + github: 'jozkee', + memberOf: [ROLE_IDS.CSHARP_SDK], + }, { github: 'jspahrsummers', email: 'justin@modelcontextprotocol.io', @@ -391,7 +403,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, - ROLE_IDS.CSHARP_SDK, + ROLE_IDS.CSHARP_SDK_ADMIN, ROLE_IDS.ADMINISTRATORS, ROLE_IDS.GO_SDK, ROLE_IDS.FINANCIAL_SERVICES_IG, @@ -449,7 +461,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'mikekistler', discord: '915345005982408754', - memberOf: [ROLE_IDS.CSHARP_SDK], + memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], }, { github: 'movetz', @@ -602,7 +614,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'stephentoub', - memberOf: [ROLE_IDS.CSHARP_SDK], + memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], }, { github: 'tadasant', @@ -622,6 +634,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.ADMINISTRATORS, ], }, + { + github: 'tarekgh', + memberOf: [ROLE_IDS.CSHARP_SDK], + }, { github: 'tiginamaria', memberOf: [ROLE_IDS.KOTLIN_SDK], From c52a415fde1bcd87298dae2746ae53622aec53d5 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:52:08 +0000 Subject: [PATCH 138/196] Add MODERATORS, ADMINISTRATORS, DOCS_MAINTAINERS roles to pcarleton (#77) Grants Paul Carleton moderator (GitHub + Discord), Discord administrator, and docs-maintainers access alongside his existing core-maintainers role. :house: Remote-Dev: homespace --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 2442a0c..0e19041 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -533,6 +533,9 @@ export const MEMBERS: readonly Member[] = [ existingGWSUser: true, memberOf: [ ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.DOCS_MAINTAINERS, + ROLE_IDS.ADMINISTRATORS, + ROLE_IDS.MODERATORS, ROLE_IDS.PYTHON_SDK, ROLE_IDS.PYTHON_SDK_AUTH, ROLE_IDS.TYPESCRIPT_SDK, From afbdcad0272b05f124ca002d51833c4fc8cf5164 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Mon, 16 Mar 2026 14:47:46 +0100 Subject: [PATCH 139/196] Add soyuka to PHP SDK maintainers (#80) --- src/config/users.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 0e19041..ed9eab7 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -610,6 +610,12 @@ export const MEMBERS: readonly Member[] = [ github: 'sdubov', memberOf: [ROLE_IDS.KOTLIN_SDK], }, + { + github: 'soyuka', + email: 'soyuka@gmail.com', + discord: '249323948842418186', + memberOf: [ROLE_IDS.PHP_SDK], + }, { github: 'stallent', discord: '1137898074086314136', From 57d06350e55f9b0482a643b2f1ce50c9dfec59af Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 16 Mar 2026 09:53:42 -0700 Subject: [PATCH 140/196] Add sunishsheth2009 to Skills over MCP IG and enable Discord sync (#78) - Add sunishsheth2009 as a new member of the skills-over-mcp-ig group - Enable Discord role sync for the Skills over MCP Interest Group - Add Discord IDs for erain, JAORMX, kaxil, and keithagroves Co-authored-by: Claude Opus 4.6 (1M context) --- src/config/roles.ts | 2 +- src/config/users.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/roles.ts b/src/config/roles.ts index 2b916a1..cab60c0 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -328,7 +328,7 @@ export const ROLES: readonly Role[] = [ id: ROLE_IDS.SKILLS_OVER_MCP_IG, description: 'Skills Over MCP Interest Group', github: { team: 'skills-over-mcp-ig', parent: ROLE_IDS.INTEREST_GROUPS }, - // No Discord role yet + discord: { role: 'skills over mcp interest group (synced)' }, }, // =================== diff --git a/src/config/users.ts b/src/config/users.ts index ed9eab7..96079b1 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -227,6 +227,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'erain', + discord: '797226095874539539', memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], }, { @@ -292,6 +293,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'JAORMX', + discord: '1185152774674055193', memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], }, { @@ -348,6 +350,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'kaxil', + discord: '757355088946921474', memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], }, { @@ -357,6 +360,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'keithagroves', + discord: '321019863260987392', memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], }, { @@ -625,6 +629,11 @@ export const MEMBERS: readonly Member[] = [ github: 'stephentoub', memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], }, + { + github: 'sunishsheth2009', + discord: '1414713222224941097', + memberOf: [ROLE_IDS.SKILLS_OVER_MCP_IG], + }, { github: 'tadasant', email: 'tadas@modelcontextprotocol.io', From f4f2b70e426f9138491d358a7da5c135d6d54a26 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 16 Mar 2026 17:12:27 +0000 Subject: [PATCH 141/196] Grant moderators maintain access for Discussion category management Moderators need maintain permission to create, edit, and delete GitHub Discussion categories. Previously they had push or triage, which only allows managing individual discussions but not categories. --- src/config/repoAccess.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index e215255..e81f690 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -26,7 +26,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'interest-groups', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, - { team: 'moderators', permission: 'push' }, + { team: 'moderators', permission: 'maintain' }, { team: 'php-sdk', permission: 'push' }, { team: 'python-sdk', permission: 'push' }, { team: 'python-sdk-auth', permission: 'push' }, @@ -55,7 +55,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'interest-groups', permission: 'triage' }, { team: 'java-sdk', permission: 'triage' }, { team: 'kotlin-sdk', permission: 'triage' }, - { team: 'moderators', permission: 'push' }, + { team: 'moderators', permission: 'maintain' }, { team: 'php-sdk', permission: 'triage' }, { team: 'python-sdk', permission: 'triage' }, { team: 'python-sdk-auth', permission: 'triage' }, @@ -82,7 +82,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'go-sdk', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, - { team: 'moderators', permission: 'push' }, + { team: 'moderators', permission: 'maintain' }, { team: 'php-sdk', permission: 'push' }, { team: 'python-sdk', permission: 'push' }, { team: 'python-sdk-auth', permission: 'push' }, @@ -110,7 +110,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'interest-groups', permission: 'triage' }, { team: 'java-sdk', permission: 'triage' }, { team: 'kotlin-sdk', permission: 'triage' }, - { team: 'moderators', permission: 'triage' }, + { team: 'moderators', permission: 'maintain' }, { team: 'php-sdk', permission: 'triage' }, { team: 'python-sdk', permission: 'triage' }, { team: 'python-sdk-auth', permission: 'triage' }, @@ -139,7 +139,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'interest-groups', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, - { team: 'moderators', permission: 'push' }, + { team: 'moderators', permission: 'maintain' }, { team: 'php-sdk', permission: 'push' }, { team: 'python-sdk', permission: 'push' }, { team: 'python-sdk-auth', permission: 'push' }, @@ -166,7 +166,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'go-sdk', permission: 'push' }, { team: 'java-sdk', permission: 'push' }, { team: 'kotlin-sdk', permission: 'push' }, - { team: 'moderators', permission: 'push' }, + { team: 'moderators', permission: 'maintain' }, { team: 'php-sdk', permission: 'push' }, { team: 'python-sdk', permission: 'push' }, { team: 'python-sdk-auth', permission: 'push' }, @@ -272,7 +272,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'ext-apps', teams: [ { team: 'core-maintainers', permission: 'push' }, - { team: 'moderators', permission: 'triage' }, + { team: 'moderators', permission: 'maintain' }, { team: 'mcp-apps-wg', permission: 'push' }, { team: 'mcp-apps-sdk', permission: 'admin' }, ], @@ -287,7 +287,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'use-mcp', teams: [ { team: 'core-maintainers', permission: 'push' }, - { team: 'moderators', permission: 'triage' }, + { team: 'moderators', permission: 'maintain' }, ], users: [{ username: 'geelen', permission: 'admin' }], }, @@ -295,7 +295,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'example-remote-client', teams: [ { team: 'core-maintainers', permission: 'push' }, - { team: 'moderators', permission: 'triage' }, + { team: 'moderators', permission: 'maintain' }, ], users: [ { username: 'geelen', permission: 'push' }, @@ -307,7 +307,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'experimental-ext-grouping', teams: [ { team: 'core-maintainers', permission: 'admin' }, - { team: 'moderators', permission: 'triage' }, + { team: 'moderators', permission: 'maintain' }, { team: 'primitive-grouping-ig', permission: 'admin' }, ], }, @@ -315,7 +315,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'experimental-ext-skills', teams: [ { team: 'core-maintainers', permission: 'admin' }, - { team: 'moderators', permission: 'triage' }, + { team: 'moderators', permission: 'maintain' }, { team: 'skills-over-mcp-ig', permission: 'admin' }, ], }, From 1e9e17ca7e520588a2dabec9067a118b491ce51b Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Tue, 17 Mar 2026 04:01:44 -0700 Subject: [PATCH 142/196] Add Tool Annotations Interest Group and enable Discord sync (#79) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add evalstate and cliffhall to docs-maintaners team 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Add Tool Annotations Interest Group and enable Discord sync Add rreichel3, mattzcarey, and SamMorrowDrums as facilitators for the new Tool Annotations IG. Create GitHub team and Discord role sync. Add Discord IDs for rreichel3 and mattzcarey. Co-Authored-By: Claude Opus 4.6 (1M context) * Add experimental-ext-tool-annotations repo for Tool Annotations IG Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude --- src/config/repoAccess.ts | 8 ++++++++ src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 9 ++++++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index e81f690..bb1e54b 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -319,6 +319,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'skills-over-mcp-ig', permission: 'admin' }, ], }, + { + repository: 'experimental-ext-tool-annotations', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'triage' }, + { team: 'tool-annotations-ig', permission: 'admin' }, + ], + }, { repository: 'maintainer-docs', teams: [ diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 325c9c6..f5bb1f6 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -64,6 +64,7 @@ export const ROLE_IDS = { GATEWAYS_IG: 'gateways-ig', PRIMITIVE_GROUPING_IG: 'primitive-grouping-ig', SKILLS_OVER_MCP_IG: 'skills-over-mcp-ig', + TOOL_ANNOTATIONS_IG: 'tool-annotations-ig', // =================== // WG/IG Facilitators (Discord only) diff --git a/src/config/roles.ts b/src/config/roles.ts index cab60c0..c3c533c 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -330,6 +330,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'skills-over-mcp-ig', parent: ROLE_IDS.INTEREST_GROUPS }, discord: { role: 'skills over mcp interest group (synced)' }, }, + { + id: ROLE_IDS.TOOL_ANNOTATIONS_IG, + description: 'Tool Annotations Interest Group', + github: { team: 'tool-annotations-ig', parent: ROLE_IDS.INTEREST_GROUPS }, + discord: { role: 'tool annotations interest group (synced)' }, + }, // =================== // WG/IG Facilitators (Discord only) diff --git a/src/config/users.ts b/src/config/users.ts index 96079b1..2ec624d 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -451,7 +451,8 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'mattzcarey', - memberOf: [ROLE_IDS.TYPESCRIPT_SDK], + discord: '224878268275359744', + memberOf: [ROLE_IDS.TYPESCRIPT_SDK, ROLE_IDS.TOOL_ANNOTATIONS_IG, ROLE_IDS.WG_IG_FACILITATORS], }, { github: 'maxisbey', @@ -591,6 +592,11 @@ export const MEMBERS: readonly Member[] = [ existingGWSUser: true, memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG], }, + { + github: 'rreichel3', + discord: '1458485333757788273', + memberOf: [ROLE_IDS.TOOL_ANNOTATIONS_IG, ROLE_IDS.WG_IG_FACILITATORS], + }, { github: 'sambhav', email: 'sambhavs.email@gmail.com', @@ -607,6 +613,7 @@ export const MEMBERS: readonly Member[] = [ memberOf: [ ROLE_IDS.PRIMITIVE_GROUPING_IG, ROLE_IDS.SKILLS_OVER_MCP_IG, + ROLE_IDS.TOOL_ANNOTATIONS_IG, ROLE_IDS.WG_IG_FACILITATORS, ], }, From 0dcdf88a57b6fe3d1dc6d7f54ece4f92e19d0dfe Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Tue, 17 Mar 2026 06:23:46 -0700 Subject: [PATCH 143/196] Add pja-ant to Skills over MCP interest group (#81) Co-authored-by: Claude Opus 4.6 (1M context) --- src/config/users.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 2ec624d..2782155 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -570,7 +570,12 @@ export const MEMBERS: readonly Member[] = [ lastName: 'Alexander', googleEmailPrefix: 'pja', existingGWSUser: true, - memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.TRANSPORT_WG], + memberOf: [ + ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.MAINTAINERS, + ROLE_IDS.SKILLS_OVER_MCP_IG, + ROLE_IDS.TRANSPORT_WG, + ], }, { github: 'pronskiy', From 7a449ff28c60239363f634257263022572f17fe4 Mon Sep 17 00:00:00 2001 From: Nick Cooper <142361983+nickcoai@users.noreply.github.com> Date: Wed, 18 Mar 2026 15:01:46 -0400 Subject: [PATCH 144/196] Add nickcoai Google info (#82) * Add nickcoai Google info As above * Update src/config/users.ts --------- Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/users.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 2782155..caf5781 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -481,7 +481,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'nickcoai', discord: '1153783469860732968', - skipGoogleUserProvisioning: true, + firstName: 'Nick', + lastName: 'Cooper', + googleEmailPrefix: 'nickc', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.SERVER_IDENTITY_WG], }, { From f8d6ec117cb5e1790149a77a41a28846365a673c Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Thu, 19 Mar 2026 04:34:35 -0700 Subject: [PATCH 145/196] Enable --refresh on pulumi up to detect drift (#83) GitHub org invitations expire after 7 days if not accepted. When that happens, Pulumi state still shows the TeamMembership as healthy and reports 'unchanged' on every deploy, so the user silently stays out of the team forever. Adding --refresh makes pulumi read actual GitHub state before diffing, so expired memberships are detected and recreated (resending the invite). Currently affected: liady, antonpk1, keithagroves all have expired invites and are missing from their configured teams. :house: Remote-Dev: homespace --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1d2d627..324ab1b 100644 --- a/Makefile +++ b/Makefile @@ -15,5 +15,5 @@ preview: login ## Preview infrastructure changes refresh: login ## Refresh state to match reality PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi refresh --yes --stack prod -up: login ## Deploy infrastructure - PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack prod +up: login ## Deploy infrastructure (with refresh to detect drift, e.g. expired org invites) + PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --refresh --yes --stack prod From 3a06068d55e9418971bff0ff364e65a67a32ad76 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Fri, 20 Mar 2026 01:08:13 -0700 Subject: [PATCH 146/196] Add Discord API rate limit handling and bump @pulumi/github (#84) - Handle 429 responses from Discord API with automatic retry based on retry_after, preventing transient failures during role sync - Surface underlying error details in role and member role read failures to make debugging easier - Bump @pulumi/github to ^6.11.0 (resolves 6.12.1) :house: Remote-Dev: homespace --- package-lock.json | 8 +++--- src/discord.ts | 72 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index e7602a8..cc1f02d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@pulumi/github": "^6.7.3", + "@pulumi/github": "^6.11.0", "@pulumi/googleworkspace": "file:sdks/googleworkspace", "@pulumi/pulumi": "^3.218.0", "@pulumi/random": "^4.14.0" @@ -655,9 +655,9 @@ "license": "BSD-3-Clause" }, "node_modules/@pulumi/github": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@pulumi/github/-/github-6.9.1.tgz", - "integrity": "sha512-B7FcceruXfMClws4poIyUfJoy2dqcdhi8P5+ahfArIeaDjFDlcRNU9SZbfFRbkdoXflMJi+AI3rWabyvqS2+YQ==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@pulumi/github/-/github-6.12.1.tgz", + "integrity": "sha512-OsbQgF8go/tA1PuMhFq7O0XVfYbwRk0/LHaZTH6OFO395SufdFFa1go3IeXvJCSA5oDCbdULerGg4n9sP8vTLg==", "license": "Apache-2.0", "dependencies": { "@pulumi/pulumi": "^3.142.0" diff --git a/src/discord.ts b/src/discord.ts index d08f17a..99896c2 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -20,31 +20,63 @@ interface DiscordApiError { message: string; } +interface DiscordRateLimitResponse { + message: string; + retry_after: number; + global: boolean; +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + async function discordFetch( token: string, endpoint: string, - options: RequestInit = {} + options: RequestInit = {}, + maxRetries = 5 ): Promise { - const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, { - ...options, - headers: { - Authorization: `Bot ${token}`, - 'Content-Type': 'application/json', - ...options.headers, - }, - }); + let lastError: Error | undefined; + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + const response = await fetch(`${DISCORD_API_BASE}${endpoint}`, { + ...options, + headers: { + Authorization: `Bot ${token}`, + 'Content-Type': 'application/json', + ...options.headers, + }, + }); - if (!response.ok) { - const error = (await response.json()) as DiscordApiError; - throw new Error(`Discord API error: ${error.message} (code: ${error.code})`); - } + if (response.status === 429) { + const body = (await response.json()) as DiscordRateLimitResponse; + const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250; + lastError = new Error( + `Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})` + ); + if (attempt < maxRetries) { + await sleep(retryAfterMs); + continue; + } + throw lastError; + } + + if (!response.ok) { + const error = (await response.json()) as DiscordApiError; + throw new Error(`Discord API error: ${error.message} (code: ${error.code})`); + } - // Handle 204 No Content - if (response.status === 204) { - return undefined as T; + // Handle 204 No Content + if (response.status === 204) { + return undefined as T; + } + + return response.json() as Promise; } - return response.json() as Promise; + throw ( + lastError ?? new Error(`Discord API request to ${endpoint} failed after ${maxRetries} retries`) + ); } // Discord API response types @@ -122,8 +154,8 @@ const discordRoleProvider: pulumi.dynamic.ResourceProvider = { roleId: role.id, }, }; - } catch { - throw new Error(`Failed to read role ${id}`); + } catch (error) { + throw new Error(`Failed to read role ${id}: ${error}`); } }, @@ -303,7 +335,7 @@ const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { }, }; } - throw new Error(`Failed to read member roles for ${id}`); + throw new Error(`Failed to read member roles for ${id}: ${error}`); } const currentRoles = new Set(member.roles); From ffa85f0109e2daa185b75b5bdf3617a18758a212 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 21 Mar 2026 20:41:55 +0000 Subject: [PATCH 147/196] fix(ci): reserialize dynamic providers before refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split `make up` into two phases: a plain `pulumi up` first, then `pulumi up --refresh`. Dynamic providers serialize their implementation into state at create/update time. During refresh, the serialized `read()` runs — so any provider-code change (like the Discord 429 retry logic) doesn't take effect until a successful update writes the new `__provider` to state. Running refresh first used the stale serialized code from the last successful deploy, which had no rate-limit handling, failing on parallel Discord member lookups. Also bump discordFetch maxRetries from 5 to 10 for headroom against ~68 parallel refresh reads. --- Makefile | 4 ++++ src/discord.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 324ab1b..0272401 100644 --- a/Makefile +++ b/Makefile @@ -16,4 +16,8 @@ refresh: login ## Refresh state to match reality PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi refresh --yes --stack prod up: login ## Deploy infrastructure (with refresh to detect drift, e.g. expired org invites) + # Dynamic providers serialize their implementation into state. Run a plain + # `up` first so any provider-code changes are captured, then refresh with the + # freshly serialized `read()`. Otherwise refresh runs stale code and fails. + PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack prod PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --refresh --yes --stack prod diff --git a/src/discord.ts b/src/discord.ts index 99896c2..2c50ad3 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -34,7 +34,7 @@ async function discordFetch( token: string, endpoint: string, options: RequestInit = {}, - maxRetries = 5 + maxRetries = 10 ): Promise { let lastError: Error | undefined; From 9c7f6f305178e40bc6dd4e422ace1a34d6c6ec2e Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Sat, 21 Mar 2026 21:34:26 +0000 Subject: [PATCH 148/196] fix(discord): widen rate-limit jitter to de-sync parallel retries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 250ms jitter was too narrow for ~68 parallel refresh reads — all retries clustered after the same retry_after and collided again. Scale jitter linearly with attempt number so the herd progressively spreads out across attempts. --- src/discord.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/discord.ts b/src/discord.ts index 2c50ad3..e1f086f 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -50,7 +50,10 @@ async function discordFetch( if (response.status === 429) { const body = (await response.json()) as DiscordRateLimitResponse; - const retryAfterMs = Math.ceil(body.retry_after * 1000) + Math.random() * 250; + // Linearly increasing jitter de-syncs the thundering herd when many + // resources refresh in parallel and all receive the same retry_after. + const jitterMs = Math.random() * 1000 * (attempt + 1); + const retryAfterMs = Math.ceil(body.retry_after * 1000) + jitterMs; lastError = new Error( `Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})` ); From a2a41b4b76f392d10dc198529ffe1e42b6937029 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 23 Mar 2026 09:52:21 -0700 Subject: [PATCH 149/196] Upgrade csharp-sdk team permission to maintain on csharp-sdk repo (#85) The csharp-sdk team only had push permission, which was insufficient for members to push to protected branches, edit/assign issues, or have their PR approvals count toward merge requirements. Upgrading to maintain grants these capabilities without admin access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Den Delimarsky <53200638+localden@users.noreply.github.com> --- src/config/repoAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index bb1e54b..fba11d4 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -186,7 +186,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ repository: 'csharp-sdk', teams: [ { team: 'csharp-sdk-admin', permission: 'admin' }, - { team: 'csharp-sdk', permission: 'push' }, + { team: 'csharp-sdk', permission: 'maintain' }, ], users: [{ username: 'PederHP', permission: 'triage' }], }, From 40c29e305a9a918086feb9a3ef4f27a64a349a77 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:53:17 -0700 Subject: [PATCH 150/196] Retry Discord API 5xx errors and handle non-JSON error bodies (#87) Cloudflare edge errors (5xx, edge-level 429s) return plain-text bodies like "upstream connect error..." or "error code: 1015" that crash a naive response.json() call. Add a tryParseJson helper and use it for all error paths so we surface the actual HTTP status/body instead of a JSON parse exception. Also add exponential backoff retry for 5xx responses, matching the existing 429 retry behavior. :house: Remote-Dev: homespace --- src/discord.ts | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/discord.ts b/src/discord.ts index e1f086f..e5f3543 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -30,6 +30,16 @@ function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } +// Cloudflare 5xx and edge-level 429s return plain-text bodies ("upstream connect +// error...", "error code: 1015") that crash a naive response.json(). +function tryParseJson(text: string): T | undefined { + try { + return JSON.parse(text) as T; + } catch { + return undefined; + } +} + async function discordFetch( token: string, endpoint: string, @@ -49,13 +59,15 @@ async function discordFetch( }); if (response.status === 429) { - const body = (await response.json()) as DiscordRateLimitResponse; + const text = await response.text(); + const body = tryParseJson(text); + const retryAfterSec = body?.retry_after ?? 1; // Linearly increasing jitter de-syncs the thundering herd when many // resources refresh in parallel and all receive the same retry_after. const jitterMs = Math.random() * 1000 * (attempt + 1); - const retryAfterMs = Math.ceil(body.retry_after * 1000) + jitterMs; + const retryAfterMs = Math.ceil(retryAfterSec * 1000) + jitterMs; lastError = new Error( - `Discord API rate limited on ${endpoint} (retry_after=${body.retry_after}s, global=${body.global})` + `Discord API rate limited on ${endpoint} (retry_after=${retryAfterSec}s, global=${body?.global ?? false})` ); if (attempt < maxRetries) { await sleep(retryAfterMs); @@ -64,9 +76,24 @@ async function discordFetch( throw lastError; } + if (response.status >= 500 && response.status < 600) { + const text = await response.text(); + lastError = new Error(`Discord API ${response.status} on ${endpoint}: ${text.slice(0, 200)}`); + if (attempt < maxRetries) { + await sleep(2 ** attempt * 500 + Math.random() * 1000); + continue; + } + throw lastError; + } + if (!response.ok) { - const error = (await response.json()) as DiscordApiError; - throw new Error(`Discord API error: ${error.message} (code: ${error.code})`); + const text = await response.text(); + const error = tryParseJson(text); + throw new Error( + error + ? `Discord API error: ${error.message} (code: ${error.code})` + : `Discord API ${response.status} on ${endpoint}: ${text.slice(0, 200)}` + ); } // Handle 204 No Content From e014aa7c4e3fa801088766dfd9921aa78b52ca6f Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Tue, 24 Mar 2026 12:33:55 -0700 Subject: [PATCH 151/196] Add reference-servers-maintainers GitHub team with admin on servers repo (#88) - Expand REFERENCE_SERVERS_MAINTAINERS role (previously Discord-only) to create a GitHub team - Grant the team admin permission on modelcontextprotocol/servers - Add tobinsouth as a new member Existing members cliffhall and olaservo will also be added to the new GitHub team. :house: Remote-Dev: homespace --- src/config/repoAccess.ts | 1 + src/config/roles.ts | 2 +- src/config/users.ts | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index fba11d4..5efdb89 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -159,6 +159,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { repository: 'servers', teams: [ + { team: 'reference-servers-maintainers', permission: 'admin' }, { team: 'auth-maintainers', permission: 'push' }, { team: 'core-maintainers', permission: 'admin' }, { team: 'csharp-sdk', permission: 'push' }, diff --git a/src/config/roles.ts b/src/config/roles.ts index c3c533c..ee291da 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -127,8 +127,8 @@ export const ROLES: readonly Role[] = [ { id: ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, description: 'Reference servers maintainers', + github: { team: 'reference-servers-maintainers' }, discord: { role: 'reference servers maintainers (synced)' }, - // Discord only for now }, { id: ROLE_IDS.REGISTRY_MAINTAINERS, diff --git a/src/config/users.ts b/src/config/users.ts index caf5781..f659808 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -674,6 +674,11 @@ export const MEMBERS: readonly Member[] = [ github: 'tiginamaria', memberOf: [ROLE_IDS.KOTLIN_SDK], }, + { + github: 'tobinsouth', + discord: '865072069779521556', + memberOf: [ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS], + }, { github: 'toby', email: 'toby@modelcontextprotocol.io', From a0dd148d659bbc442c8fe2aae3e9d60b0654192f Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:27:35 -0700 Subject: [PATCH 152/196] Add typescript-sdk-collaborators team with push access to TS SDK repo (#89) - New GitHub team typescript-sdk-collaborators (child of typescript-sdk) - Grant push permission on modelcontextprotocol/typescript-sdk - Add poteat as initial member :house: Remote-Dev: homespace --- src/config/repoAccess.ts | 1 + src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 5efdb89..4c4b925 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -231,6 +231,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ teams: [ { team: 'typescript-sdk', permission: 'admin' }, { team: 'typescript-sdk-auth', permission: 'admin' }, + { team: 'typescript-sdk-collaborators', permission: 'push' }, ], }, { diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index f5bb1f6..71f255e 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -42,6 +42,7 @@ export const ROLE_IDS = { SWIFT_SDK: 'swift-sdk', TYPESCRIPT_SDK: 'typescript-sdk', TYPESCRIPT_SDK_AUTH: 'typescript-sdk-auth', // GitHub only (CODEOWNERS) + TYPESCRIPT_SDK_COLLABORATORS: 'typescript-sdk-collaborators', // GitHub only // =================== // Working Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index ee291da..db7552a 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -238,6 +238,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'typescript-sdk-auth', parent: ROLE_IDS.TYPESCRIPT_SDK }, // GitHub only - for CODEOWNERS }, + { + id: ROLE_IDS.TYPESCRIPT_SDK_COLLABORATORS, + description: 'TypeScript SDK collaborators', + github: { team: 'typescript-sdk-collaborators', parent: ROLE_IDS.TYPESCRIPT_SDK }, + // GitHub only + }, // =================== // Working Groups diff --git a/src/config/users.ts b/src/config/users.ts index f659808..948a668 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -579,6 +579,10 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.TRANSPORT_WG, ], }, + { + github: 'poteat', + memberOf: [ROLE_IDS.TYPESCRIPT_SDK_COLLABORATORS], + }, { github: 'pronskiy', memberOf: [ROLE_IDS.PHP_SDK], From bd085c64facb0c8bf661534f018738a1d2c87782 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 25 Mar 2026 22:12:12 +0000 Subject: [PATCH 153/196] Add Triggers & Events Working Group (#90) --- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 1 + 3 files changed, 8 insertions(+) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 71f255e..d05c1d0 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -52,6 +52,7 @@ export const ROLE_IDS = { SECURITY_WG: 'security-wg', SERVER_IDENTITY_WG: 'server-identity-wg', TRANSPORT_WG: 'transport-wg', + TRIGGERS_EVENTS_WG: 'triggers-events-wg', MCP_APPS_WG: 'mcp-apps-wg', // =================== diff --git a/src/config/roles.ts b/src/config/roles.ts index db7552a..83215a9 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -278,6 +278,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'transport-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'transports working group (synced)' }, }, + { + id: ROLE_IDS.TRIGGERS_EVENTS_WG, + description: 'Triggers & Events Working Group', + github: { team: 'triggers-events-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'triggers & events working group (synced)' }, + }, { id: ROLE_IDS.MCP_APPS_WG, description: 'MCP Apps Working Group', diff --git a/src/config/users.ts b/src/config/users.ts index 948a668..69f14f3 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -577,6 +577,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.TRIGGERS_EVENTS_WG, ], }, { From 17df1703cee5881e23c29cc88062fe0ee9f55caf Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 27 Mar 2026 19:14:11 +0000 Subject: [PATCH 154/196] Add community-moderators repo restricted to moderators and core-maintainers --- src/config/repoAccess.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 4c4b925..f5396e4 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -338,6 +338,13 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], users: [{ username: 'sambhav', permission: 'admin' }], }, + { + repository: 'community-moderators', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'maintain' }, + ], + }, ]; // GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet From ea91b4e99436357840d9e3e8222514fa59eb7b3a Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 27 Mar 2026 19:28:12 +0000 Subject: [PATCH 155/196] Remove jerome3o-anthropic and maheshmurag from moderators --- src/config/users.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 69f14f3..46134d9 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -309,11 +309,6 @@ export const MEMBERS: readonly Member[] = [ github: 'jenn-newton', memberOf: [ROLE_IDS.SECURITY_WG], }, - { - github: 'jerome3o-anthropic', - discord: '222246825397059585', - memberOf: [ROLE_IDS.MODERATORS], - }, { github: 'joan-anthropic', discord: '1398403578892128437', @@ -437,10 +432,6 @@ export const MEMBERS: readonly Member[] = [ discord: '740279257548193803', memberOf: [ROLE_IDS.COMMUNITY_MANAGERS], }, - { - github: 'maheshmurag', - memberOf: [ROLE_IDS.MODERATORS], - }, { github: 'markpollack', memberOf: [ROLE_IDS.JAVA_SDK], From 8b82530a5ca93251c2127c384491475885a07931 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Fri, 27 Mar 2026 19:31:56 +0000 Subject: [PATCH 156/196] Add moderators@ Google Workspace group with user provisioning --- src/config/roles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/roles.ts b/src/config/roles.ts index 83215a9..59c43a0 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -93,6 +93,7 @@ export const ROLES: readonly Role[] = [ description: 'Community moderators', github: { team: 'moderators', parent: ROLE_IDS.STEERING_COMMITTEE }, discord: { role: 'community moderators (synced)' }, + google: { group: 'moderators', provisionUser: true }, }, // =================== From 73a3d2af17802125da5979b3cb0a05a04ef52c31 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Mon, 30 Mar 2026 11:56:03 +0100 Subject: [PATCH 157/196] Add Clare Liguori to Triggers & Events WG and maintainers --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 46134d9..c148cec 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -106,6 +106,11 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.WG_IG_FACILITATORS, ], }, + { + github: 'clareliguori', + discord: '1109135863843143700', + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.TRIGGERS_EVENTS_WG], + }, { github: 'cliffhall', email: 'cliff@futurescale.com', From 5120a8fe7ae650770fbb9db2f0f85006f387bf02 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 31 Mar 2026 00:06:13 +0100 Subject: [PATCH 158/196] Add Discord ID for halter73 --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index c148cec..b7a20ce 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -272,6 +272,7 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'halter73', + discord: '340718902096953344', memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], }, { From 161910e80bdf43b58d48d29f761a2de3fb77e5b0 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Mon, 6 Apr 2026 17:11:38 -0700 Subject: [PATCH 159/196] Add Clare to core maintainers (#96) --- src/config/users.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index b7a20ce..2d6e274 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -108,8 +108,12 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'clareliguori', + email: 'liguori@amazon.com', discord: '1109135863843143700', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.TRIGGERS_EVENTS_WG], + firstName: 'Clare', + lastName: 'Liguori', + googleEmailPrefix: 'clare', + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRIGGERS_EVENTS_WG], }, { github: 'cliffhall', From 5baac7ca7e819c0e731f492fd5ba5633508dca7a Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 7 Apr 2026 19:43:03 +0100 Subject: [PATCH 160/196] Add Den to lead maintainers --- src/config/users.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 2d6e274..2e6dce3 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -160,6 +160,7 @@ export const MEMBERS: readonly Member[] = [ memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.CSHARP_SDK_ADMIN, ROLE_IDS.ADMINISTRATORS, @@ -411,6 +412,7 @@ export const MEMBERS: readonly Member[] = [ memberOf: [ ROLE_IDS.AUTH_MAINTAINERS, ROLE_IDS.CORE_MAINTAINERS, + ROLE_IDS.LEAD_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.CSHARP_SDK_ADMIN, ROLE_IDS.ADMINISTRATORS, From c1fb6a1ca5821b02fede9583892a6d005601d66b Mon Sep 17 00:00:00 2001 From: Maciej Kisiel Date: Wed, 8 Apr 2026 17:33:12 +0200 Subject: [PATCH 161/196] Add yarolegovich and guglielmo-san to Go SDK team (#92) * Add yarolegovich to Go SDK team Yaroslav is part of our team that maintains the SDK repository. * Add Guglielmo --- src/config/users.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 2e6dce3..1a40992 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -275,6 +275,11 @@ export const MEMBERS: readonly Member[] = [ discord: '776094836796424213', memberOf: [ROLE_IDS.GO_SDK], }, + { + github: 'guglielmo-san', + discord: '1432786987072622613', + memberOf: [ROLE_IDS.GO_SDK], + }, { github: 'halter73', discord: '340718902096953344', @@ -707,6 +712,11 @@ export const MEMBERS: readonly Member[] = [ discord: '1097924660055777290', memberOf: [ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.JAVA_SDK], }, + { + github: 'yarolegovich', + discord: '393296640141950977', + memberOf: [ROLE_IDS.GO_SDK], + }, { email: 'adamj@anthropic.com', memberOf: [ROLE_IDS.CATCH_ALL], From ccf0ecec24c61d79d8daa5908414f9c5a883cd05 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Sat, 11 Apr 2026 15:57:10 +0100 Subject: [PATCH 162/196] Grant triggers-events-wg access to experimental-ext-triggers-events (#97) --- src/config/repoAccess.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index f5396e4..3b8db2a 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -329,6 +329,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'tool-annotations-ig', permission: 'admin' }, ], }, + { + repository: 'experimental-ext-triggers-events', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'maintain' }, + { team: 'triggers-events-wg', permission: 'admin' }, + ], + }, { repository: 'maintainer-docs', teams: [ From 68c99319af7497c3dd5c1a46209527f658a2dae4 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 14 Apr 2026 14:07:56 +0100 Subject: [PATCH 163/196] Add server-card-wg and make SamMorrowDrums a maintainer Adds SERVER_CARD_WG working group with GitHub team and Discord role. Adds Sam to MAINTAINERS and SERVER_CARD_WG, and adds tadasant and dsp-ant to SERVER_CARD_WG. --- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index d05c1d0..c719276 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -54,6 +54,7 @@ export const ROLE_IDS = { TRANSPORT_WG: 'transport-wg', TRIGGERS_EVENTS_WG: 'triggers-events-wg', MCP_APPS_WG: 'mcp-apps-wg', + SERVER_CARD_WG: 'server-card-wg', // =================== // Interest Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index 59c43a0..df4ce4e 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -291,6 +291,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'mcp-apps-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'mcp apps working group (synced)' }, }, + { + id: ROLE_IDS.SERVER_CARD_WG, + description: 'Server Card Working Group', + github: { team: 'server-card-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'server card working group (synced)' }, + }, // =================== // Interest Groups diff --git a/src/config/users.ts b/src/config/users.ts index 1a40992..7f0c375 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -220,6 +220,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, + ROLE_IDS.SERVER_CARD_WG, ], }, { @@ -631,7 +632,9 @@ export const MEMBERS: readonly Member[] = [ email: 'sammorrowdrums@github.com', discord: '782948163694493696', memberOf: [ + ROLE_IDS.MAINTAINERS, ROLE_IDS.PRIMITIVE_GROUPING_IG, + ROLE_IDS.SERVER_CARD_WG, ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.TOOL_ANNOTATIONS_IG, ROLE_IDS.WG_IG_FACILITATORS, @@ -677,6 +680,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.INTEREST_GROUPS, ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.ADMINISTRATORS, + ROLE_IDS.SERVER_CARD_WG, ], }, { From 183e9db57545367c8e973a4704a4f306ddbff308 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Mon, 20 Apr 2026 23:50:43 +0200 Subject: [PATCH 164/196] feat: add Google Workspace account for SamMorrowDrums (#101) Add firstName, lastName, and googleEmailPrefix fields to provision sam@modelcontextprotocol.io Google Workspace account. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/config/users.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 7f0c375..ec1b886 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -631,6 +631,9 @@ export const MEMBERS: readonly Member[] = [ github: 'SamMorrowDrums', email: 'sammorrowdrums@github.com', discord: '782948163694493696', + firstName: 'Sam', + lastName: 'Morrow', + googleEmailPrefix: 'sam', memberOf: [ ROLE_IDS.MAINTAINERS, ROLE_IDS.PRIMITIVE_GROUPING_IG, From c193ff654b4bc5420f05e4f3bdccbf02df50a7de Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Tue, 21 Apr 2026 14:44:45 +0100 Subject: [PATCH 165/196] Self-heal Discord role sync on refresh When a member wasn't in the guild at create() time (or drifted afterwards), read() only reported the drift. Pulumi would not trigger update() because the inputs hadn't changed, so the member stayed permanently out of sync. Call syncMemberRoles() directly from read() when drift is detected so roles are applied on the next refresh without needing an input change. --- src/discord.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/discord.ts b/src/discord.ts index e5f3543..25b2018 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -378,14 +378,17 @@ const discordMemberRoleSyncProvider: pulumi.dynamic.ResourceProvider = { Array.from(currentRoles).some((r) => managedRoles.has(r) && !expectedRoles.has(r)); if (outOfSync) { - // Return current state but note it needs update + // Self-heal: apply the expected roles now. Without this, refresh would + // only observe drift, and since inputs are unchanged Pulumi would never + // trigger update() — leaving members who joined after create() stuck. + const { addedRoles, removedRoles, memberNotFound } = await syncMemberRoles(props); return { id, props: { ...props, - addedRoles: [], - removedRoles: [], - memberNotFound: false, + addedRoles, + removedRoles, + memberNotFound, }, }; } From 616a4f8ed69dd9d0b9d6336c89296f8d169778cb Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Tue, 21 Apr 2026 23:32:31 +0100 Subject: [PATCH 166/196] Add the interceptors WG and set up the permissions appropriately (#98) * Add INTERCEPTORS_WG role ID * Add Interceptors Working Group role configuration * Add repository access configuration for interceptors * Add user to INTERCEPTORS_WG role --- src/config/repoAccess.ts | 8 ++++++++ src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 7 ++++++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 3b8db2a..a24ba6c 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -337,6 +337,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'triggers-events-wg', permission: 'admin' }, ], }, + { + repository: 'experimental-ext-interceptors', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'triage' }, + { team: 'interceptors-wg', permission: 'admin' }, + ], + }, { repository: 'maintainer-docs', teams: [ diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index c719276..9c3c4ee 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -55,6 +55,7 @@ export const ROLE_IDS = { TRIGGERS_EVENTS_WG: 'triggers-events-wg', MCP_APPS_WG: 'mcp-apps-wg', SERVER_CARD_WG: 'server-card-wg', + INTERCEPTORS_WG: 'interceptors-wg', // =================== // Interest Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index df4ce4e..d787791 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -297,6 +297,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'server-card-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'server card working group (synced)' }, }, + { + id: ROLE_IDS.INTERCEPTORS_WG, + description: 'Interceptors Working Group', + github: { team: 'interceptors-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'interceptors working group (synced)' }, + }, // =================== // Interest Groups diff --git a/src/config/users.ts b/src/config/users.ts index ec1b886..e28326d 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -154,6 +154,10 @@ export const MEMBERS: readonly Member[] = [ discord: '267646459187298305', memberOf: [ROLE_IDS.RUST_SDK], }, + { + github: 'degiorgio', + memberOf: [ROLE_IDS.INTERCEPTORS_WG], + }, { github: 'dend', skipGoogleUserProvisioning: true, @@ -568,6 +572,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.MODERATORS, ROLE_IDS.SKILLS_OVER_MCP_IG, + ROLE_IDS.INTERCEPTORS_WG, ], }, { @@ -625,7 +630,7 @@ export const MEMBERS: readonly Member[] = [ lastName: 'Kothari', googleEmailPrefix: 'sambhav', discord: '840109459212206090', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.INTERCEPTORS_WG], }, { github: 'SamMorrowDrums', From 1b3c1c8a8908049ded42af37ec566c3e86e9c0c9 Mon Sep 17 00:00:00 2001 From: Kurt Degiorgio Date: Wed, 22 Apr 2026 12:09:52 +0100 Subject: [PATCH 167/196] Update details in users.ts for Kurt Degiorgio (#102) * Update details in users.ts for Kurt Degiorgio * Update users.ts * Update users.ts --- src/config/users.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index e28326d..f1e2af1 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -156,6 +156,10 @@ export const MEMBERS: readonly Member[] = [ }, { github: 'degiorgio', + email: 'degiorgiokurt@outlook.com', + firstName: 'Kurt', + lastName: 'Degiorgio', + discord: '602175181133316105', memberOf: [ROLE_IDS.INTERCEPTORS_WG], }, { From d69a4e91b81c0bcbebe2f2173ed69d2609361eb2 Mon Sep 17 00:00:00 2001 From: Den Delimarsky Date: Thu, 23 Apr 2026 02:09:31 +0000 Subject: [PATCH 168/196] Add File Uploads Working Group role and initial members Creates the file-uploads-wg GitHub team (under working-groups) and the matching Discord role. Initial members: localden (Lead), nickcoai, ochafik. See charter PR modelcontextprotocol#2635. :house: Remote-Dev: homespace --- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 9c3c4ee..0bacac0 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -56,6 +56,7 @@ export const ROLE_IDS = { MCP_APPS_WG: 'mcp-apps-wg', SERVER_CARD_WG: 'server-card-wg', INTERCEPTORS_WG: 'interceptors-wg', + FILE_UPLOADS_WG: 'file-uploads-wg', // =================== // Interest Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index d787791..41feedc 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -303,6 +303,12 @@ export const ROLES: readonly Role[] = [ github: { team: 'interceptors-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'interceptors working group (synced)' }, }, + { + id: ROLE_IDS.FILE_UPLOADS_WG, + description: 'File Uploads Working Group', + github: { team: 'file-uploads-wg', parent: ROLE_IDS.WORKING_GROUPS }, + discord: { role: 'file uploads working group (synced)' }, + }, // =================== // Interest Groups diff --git a/src/config/users.ts b/src/config/users.ts index f1e2af1..fbe8626 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -430,6 +430,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.CSHARP_SDK_ADMIN, ROLE_IDS.ADMINISTRATORS, + ROLE_IDS.FILE_UPLOADS_WG, ROLE_IDS.GO_SDK, ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.MODERATORS, @@ -501,7 +502,7 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Nick', lastName: 'Cooper', googleEmailPrefix: 'nickc', - memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.SERVER_IDENTITY_WG], + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.FILE_UPLOADS_WG, ROLE_IDS.SERVER_IDENTITY_WG], }, { github: 'nicolas-grekas', @@ -516,6 +517,7 @@ export const MEMBERS: readonly Member[] = [ github: 'ochafik', discord: '1004897332069925024', memberOf: [ + ROLE_IDS.FILE_UPLOADS_WG, ROLE_IDS.MCP_APPS_SDK, ROLE_IDS.PYTHON_SDK, ROLE_IDS.PYTHON_SDK_AUTH, From 031692fb76e056469242cc2d63f61d71882a1e9d Mon Sep 17 00:00:00 2001 From: Den Delimarsky Date: Fri, 24 Apr 2026 06:50:40 +0000 Subject: [PATCH 169/196] Add caseychow-oai to File Uploads WG Casey is the author of SEP-2631 (File Objects and Transfer), see modelcontextprotocol/modelcontextprotocol#2631. :house: Remote-Dev: homespace --- src/config/users.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index fbe8626..b0a8e35 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -84,6 +84,10 @@ export const MEMBERS: readonly Member[] = [ googleEmailPrefix: 'caitie', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, + { + github: 'caseychow-oai', + memberOf: [ROLE_IDS.FILE_UPLOADS_WG], + }, { github: 'chemicL', discord: '1346243721271971923', From c4b71321ea92424594800175e48d14ff0ed66bc9 Mon Sep 17 00:00:00 2001 From: Ukjae Jeong Date: Tue, 28 Apr 2026 16:47:51 +0100 Subject: [PATCH 170/196] Add @jeongukjae to interceptors working group (#103) * Add details for myself * Update users.ts * Update users.ts --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index b0a8e35..29d9ff1 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -338,6 +338,11 @@ export const MEMBERS: readonly Member[] = [ github: 'jenn-newton', memberOf: [ROLE_IDS.SECURITY_WG], }, + { + github: 'jeongukjae', + discord: '334348926658412564', + memberOf: [ROLE_IDS.INTERCEPTORS_WG], + }, { github: 'joan-anthropic', discord: '1398403578892128437', From 817bd3485c06b1cb7f9b03550d4d0edb98764099 Mon Sep 17 00:00:00 2001 From: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:45:20 +0100 Subject: [PATCH 171/196] feat: add Google Workspace accounts for felixweinberger and maxisbey (#106) --- src/config/users.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 29d9ff1..7eb148a 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -281,7 +281,15 @@ export const MEMBERS: readonly Member[] = [ { github: 'felixweinberger', discord: '1377138523492057212', - memberOf: [ROLE_IDS.PYTHON_SDK, ROLE_IDS.SECURITY_WG, ROLE_IDS.TYPESCRIPT_SDK], + firstName: 'Felix', + lastName: 'Weinberger', + googleEmailPrefix: 'felix', + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.PYTHON_SDK, + ROLE_IDS.SECURITY_WG, + ROLE_IDS.TYPESCRIPT_SDK, + ], }, { github: 'findleyr', @@ -484,7 +492,10 @@ export const MEMBERS: readonly Member[] = [ { github: 'maxisbey', discord: '1404871241738748058', - memberOf: [ROLE_IDS.PYTHON_SDK], + firstName: 'Max', + lastName: 'Isbey', + googleEmailPrefix: 'max', + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.PYTHON_SDK], }, { github: 'michaelneale', From 945668a063775835d36357012243eccbf81590d3 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:11:37 +0100 Subject: [PATCH 172/196] feat: add Agents Working Group with experimental-ext-tasks access (#109) Creates the agents-wg role and corresponding GitHub team under working-groups, adds Caitie, David, Luca, and Peter Alexander as members, and grants the team admin access to the experimental-ext-tasks repository. --- src/config/repoAccess.ts | 8 ++++++++ src/config/roleIds.ts | 1 + src/config/roles.ts | 5 +++++ src/config/users.ts | 11 +++++++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index a24ba6c..54eb49d 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -345,6 +345,14 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'interceptors-wg', permission: 'admin' }, ], }, + { + repository: 'experimental-ext-tasks', + teams: [ + { team: 'core-maintainers', permission: 'admin' }, + { team: 'moderators', permission: 'maintain' }, + { team: 'agents-wg', permission: 'admin' }, + ], + }, { repository: 'maintainer-docs', teams: [ diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 0bacac0..a718d25 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -57,6 +57,7 @@ export const ROLE_IDS = { SERVER_CARD_WG: 'server-card-wg', INTERCEPTORS_WG: 'interceptors-wg', FILE_UPLOADS_WG: 'file-uploads-wg', + AGENTS_WG: 'agents-wg', // =================== // Interest Groups diff --git a/src/config/roles.ts b/src/config/roles.ts index 41feedc..f567321 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -309,6 +309,11 @@ export const ROLES: readonly Role[] = [ github: { team: 'file-uploads-wg', parent: ROLE_IDS.WORKING_GROUPS }, discord: { role: 'file uploads working group (synced)' }, }, + { + id: ROLE_IDS.AGENTS_WG, + description: 'Agents Working Group', + github: { team: 'agents-wg', parent: ROLE_IDS.WORKING_GROUPS }, + }, // =================== // Interest Groups diff --git a/src/config/users.ts b/src/config/users.ts index 7eb148a..5e8fbe8 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -82,7 +82,7 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Caitie', lastName: 'McCaffrey', googleEmailPrefix: 'caitie', - memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], + memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG, ROLE_IDS.AGENTS_WG], }, { github: 'caseychow-oai', @@ -233,6 +233,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.SERVER_CARD_WG, + ROLE_IDS.AGENTS_WG, ], }, { @@ -464,7 +465,12 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Luca', lastName: 'Chang', googleEmailPrefix: 'luca', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.AGENTS_IG, ROLE_IDS.WORKING_GROUPS], + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.AGENTS_IG, + ROLE_IDS.AGENTS_WG, + ROLE_IDS.WORKING_GROUPS, + ], }, { github: 'maciej-kisiel', @@ -618,6 +624,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.TRANSPORT_WG, ROLE_IDS.TRIGGERS_EVENTS_WG, + ROLE_IDS.AGENTS_WG, ], }, { From 65b5a43451e3d26d58da00b17a74c00f122cc7a5 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 30 Apr 2026 14:30:54 +0100 Subject: [PATCH 173/196] Manage GitHub org settings and org-role assignments via Pulumi (#107) * Manage GitHub org settings and org-role assignments via Pulumi Brings two click-ops org-level configs under IaC and tightens defaults: - OrganizationSettings: default_repository_permission read -> none so private repos are not implicitly readable by every member; repo creation restricted to admins; billingEmail from Pulumi secret; security new-repo defaults pinned so the provider does not reset them. - Org-role assignments: codify lead/core-maintainers -> all_repo_admin. moderators -> all_repo_triage and anthropic -> all_repo_write are intentionally omitted (being removed). * Fix config namespace: use project namespace for githubBillingEmail The github: namespace is reserved for the @pulumi/github provider's own config keys, so github:billingEmail was rejected as an unknown provider option. Use the project default namespace instead. preview.yml uses the same --config flag pattern as the existing Discord secrets. --- .github/workflows/deploy.yml | 2 ++ .github/workflows/preview.yml | 4 ++++ src/config/orgRoles.ts | 23 ++++++++++++++++++++++ src/config/orgSettings.ts | 33 ++++++++++++++++++++++++++++++++ src/github.ts | 36 +++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 src/config/orgRoles.ts create mode 100644 src/config/orgSettings.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6bbf71f..e93fe79 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -63,6 +63,7 @@ jobs: env: PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} + ORG_BILLING_EMAIL: ${{ secrets.ORG_BILLING_EMAIL }} DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }} run: | @@ -71,4 +72,5 @@ jobs: pulumi login gs://mcp-access-prod-pulumi-state pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod + pulumi config set githubBillingEmail "$ORG_BILLING_EMAIL" --secret --stack prod make up diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index f8700b4..20b164f 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -63,6 +63,7 @@ jobs: env: PULUMI_PASSPHRASE: ${{ secrets.PULUMI_PROD_PASSPHRASE }} GITHUB_TOKEN: ${{ secrets.PULUMI_GITHUB_TOKEN }} + ORG_BILLING_EMAIL: ${{ secrets.ORG_BILLING_EMAIL }} DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} DISCORD_GUILD_ID: ${{ secrets.DISCORD_GUILD_ID }} run: | @@ -77,6 +78,9 @@ jobs: if [ -n "$DISCORD_BOT_TOKEN" ]; then CONFIG_FLAGS="$CONFIG_FLAGS --config discord:botToken=$DISCORD_BOT_TOKEN" fi + if [ -n "$ORG_BILLING_EMAIL" ]; then + CONFIG_FLAGS="$CONFIG_FLAGS --config githubBillingEmail=$ORG_BILLING_EMAIL" + fi # Run preview and capture output set +e diff --git a/src/config/orgRoles.ts b/src/config/orgRoles.ts new file mode 100644 index 0000000..576ec51 --- /dev/null +++ b/src/config/orgRoles.ts @@ -0,0 +1,23 @@ +// GitHub organization-level role assignments. +// These grant a base permission across ALL repositories in the org via GitHub's +// pre-defined organization roles, independent of per-repo collaborators in repoAccess.ts. +// See https://docs.github.com/en/organizations/managing-peoples-access-to-your-organization-with-roles/using-organization-roles + +export type OrgRoleName = + | 'all_repo_read' + | 'all_repo_triage' + | 'all_repo_write' + | 'all_repo_maintain' + | 'all_repo_admin'; + +export interface OrgRoleAssignment { + /** GitHub team slug */ + team: string; + /** Pre-defined GitHub organization role name */ + role: OrgRoleName; +} + +export const ORG_ROLE_ASSIGNMENTS: OrgRoleAssignment[] = [ + { team: 'lead-maintainers', role: 'all_repo_admin' }, + { team: 'core-maintainers', role: 'all_repo_admin' }, +]; diff --git a/src/config/orgSettings.ts b/src/config/orgSettings.ts new file mode 100644 index 0000000..18f9a13 --- /dev/null +++ b/src/config/orgSettings.ts @@ -0,0 +1,33 @@ +// GitHub organization-level settings. +// Captured explicitly so changes go through review. Values mirror current state +// except defaultRepositoryPermission, which is set to 'none' so private repos +// (disclosures, community-moderators, GHSA forks) are not implicitly readable +// by every org member. Explicit access flows from orgRoles.ts and repoAccess.ts. + +// billingEmail is required by the provider but intentionally omitted here so it +// is not committed to a public repo. It is read from Pulumi config in github.ts: +// pulumi config set --secret githubBillingEmail +export const ORG_SETTINGS = { + name: 'Model Context Protocol', + description: + 'An open protocol that enables seamless integration between LLM applications and external data sources and tools.', + defaultRepositoryPermission: 'none', + hasOrganizationProjects: true, + hasRepositoryProjects: true, + membersCanCreateRepositories: false, + membersCanCreatePublicRepositories: false, + membersCanCreatePrivateRepositories: false, + membersCanCreatePages: false, + membersCanCreatePublicPages: false, + membersCanCreatePrivatePages: false, + membersCanForkPrivateRepositories: false, + webCommitSignoffRequired: false, + // Provider defaults the following to `false` if omitted, which would silently + // disable org-wide security features that are currently on. + advancedSecurityEnabledForNewRepositories: true, + dependabotAlertsEnabledForNewRepositories: true, + dependabotSecurityUpdatesEnabledForNewRepositories: true, + dependencyGraphEnabledForNewRepositories: true, + secretScanningEnabledForNewRepositories: true, + secretScanningPushProtectionEnabledForNewRepositories: true, +} as const; diff --git a/src/github.ts b/src/github.ts index 3d6a223..7076caa 100644 --- a/src/github.ts +++ b/src/github.ts @@ -1,10 +1,26 @@ +import * as pulumi from '@pulumi/pulumi'; import * as github from '@pulumi/github'; import { ROLES, type Role, buildRoleLookup } from './config/roles'; import { REPOSITORY_ACCESS } from './config/repoAccess'; +import { ORG_ROLE_ASSIGNMENTS } from './config/orgRoles'; +import { ORG_SETTINGS } from './config/orgSettings'; import { MEMBERS } from './config/users'; import { sortRolesByGitHubDependency } from './config/utils'; import type { RoleId } from './config/roleIds'; +const config = new pulumi.Config(); + +// The provider's Create for this resource is a PATCH on the existing org, so +// no import is needed; first apply writes the values below directly. +new github.OrganizationSettings( + 'org-settings', + { + ...ORG_SETTINGS, + billingEmail: config.requireSecret('githubBillingEmail'), + }, + { additionalSecretOutputs: ['billingEmail'] } +); + const roleLookup = buildRoleLookup(); // Teams keyed by GitHub team name (matches repoAccess.ts references) const teams: Record = {}; @@ -50,6 +66,26 @@ MEMBERS.forEach((member) => { }); }); +// Assign organization-level roles to teams (grants access across all repos) +const orgRoles = github.getOrganizationRolesOutput(); +ORG_ROLE_ASSIGNMENTS.forEach((assignment) => { + const team = teams[assignment.team]; + if (!team) { + throw new Error( + `orgRoles.ts references team '${assignment.team}' which is not managed in roles.ts` + ); + } + const roleId = orgRoles.roles.apply((roles) => { + const match = roles.find((r) => r.name === assignment.role); + if (!match) throw new Error(`Organization role '${assignment.role}' not found`); + return match.roleId; + }); + new github.OrganizationRoleTeam(`orgrole-${assignment.team}-${assignment.role}`, { + teamSlug: team.slug, + roleId, + }); +}); + // Configure repository access REPOSITORY_ACCESS.forEach((repo) => { new github.RepositoryCollaborators(`repo-${repo.repository}`, { From 1440d465f8beb27580d452d4a110081f74a47c1e Mon Sep 17 00:00:00 2001 From: Leonid Stashevsky Date: Mon, 4 May 2026 17:29:48 +0200 Subject: [PATCH 174/196] Drop kpavlov from the kotlin sdk roles --- src/config/users.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index 5e8fbe8..fd354f8 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -419,10 +419,6 @@ export const MEMBERS: readonly Member[] = [ discord: '880937364208361483', memberOf: [ROLE_IDS.RUBY_SDK], }, - { - github: 'kpavlov', - memberOf: [ROLE_IDS.KOTLIN_SDK], - }, { github: 'kurtisvg', discord: '1158458388917780590', From 48b3463f65d04b4833a480cb7697339010869d20 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Wed, 6 May 2026 14:26:42 +0100 Subject: [PATCH 175/196] feat: remove pwwpche from CORE_MAINTAINERS (moved to emeritus) (#111) --- src/config/users.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/config/users.ts b/src/config/users.ts index fd354f8..0921c01 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -634,8 +634,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'pwwpche', discord: '1226238847013228604', - skipGoogleUserProvisioning: true, - memberOf: [ROLE_IDS.CORE_MAINTAINERS], + memberOf: [], }, { github: 'rdimitrov', From f3d919d5ddbecf581898432b90e3bc662c89f7db Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Wed, 6 May 2026 16:43:33 -0600 Subject: [PATCH 176/196] feat: add Google Workspace account for kurtisvg (#112) --- src/config/users.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 0921c01..0045f86 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -422,7 +422,9 @@ export const MEMBERS: readonly Member[] = [ { github: 'kurtisvg', discord: '1158458388917780590', - skipGoogleUserProvisioning: true, + firstName: 'Kurtis', + lastName: 'Van Gent', + googleEmailPrefix: 'kvg', memberOf: [ROLE_IDS.CORE_MAINTAINERS, ROLE_IDS.TRANSPORT_WG], }, { From a2d26c76700d620c3f4eecbd83e4211014bbcdec Mon Sep 17 00:00:00 2001 From: Yaroslav Date: Tue, 26 May 2026 22:19:34 +0200 Subject: [PATCH 177/196] update go SDK maintainers list (#113) --- src/config/users.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 0045f86..55813bd 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -515,6 +515,11 @@ export const MEMBERS: readonly Member[] = [ discord: '1427569183427919906', memberOf: [ROLE_IDS.SWIFT_SDK], }, + { + github: 'nahapetyan-serob', + discord: '1505852630692401314', + memberOf: [ROLE_IDS.GO_SDK], + }, { github: 'nbarbettini', discord: '784552628930478090', From 09a270db7a9ec94c5786a38dc186e888c861c0ef Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Tue, 26 May 2026 13:20:51 -0700 Subject: [PATCH 178/196] Add PranavSenthilnathan to csharp-sdk team (#114) --- src/config/users.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index 55813bd..4d16494 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -634,6 +634,13 @@ export const MEMBERS: readonly Member[] = [ github: 'poteat', memberOf: [ROLE_IDS.TYPESCRIPT_SDK_COLLABORATORS], }, + { + github: 'PranavSenthilnathan', + email: 'pranas@microsoft.com', + firstName: 'Pranav', + lastName: 'Senthilnathan', + memberOf: [ROLE_IDS.CSHARP_SDK], + }, { github: 'pronskiy', memberOf: [ROLE_IDS.PHP_SDK], From 448f4019950c7f89e6aa17681aa934ab012842f1 Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Wed, 3 Jun 2026 05:53:52 -0700 Subject: [PATCH 179/196] chore: align registry roster with WG charter (#115) * chore: align registry roster with WG charter Aligns GitHub teams / Discord roles / Google groups with the Registry Working Group charter (https://modelcontextprotocol.io/community/registry/charter): - Add REGISTRY_WG_COLLABORATORS role + registry-wg-collaborators GitHub team with push (not admin) on the registry repo, mirroring the typescript-sdk-collaborators model. No google config => no @modelcontextprotocol.io account (charter: Maintainer? No). - Add Preeti (Pree) Dewani (@pree-dew) as a registry collaborator. - Remove Adam Jones (@domdomegg) from REGISTRY_MAINTAINERS (he remains an active MCPB maintainer). His @modelcontextprotocol.io account is unaffected (existingGWSUser, not managed by this IaC). - Remove Toby Padilla (@toby) entirely; registry was his only maintainership. His @modelcontextprotocol.io account is likewise unaffected by this change (existingGWSUser). Co-Authored-By: Claude Opus 4.8 * chore: drop discord config and inline comments from collaborators role Co-Authored-By: Claude Opus 4.8 * chore: annotate registry collaborators role id as GitHub only Matches the typescript-sdk-collaborators convention in roleIds.ts. Co-Authored-By: Claude Opus 4.8 * chore: keep Toby as registry emeritus with empty role membership Instead of removing Toby Padilla's access entry entirely, retain it with an empty memberOf and a comment noting he is an emeritus maintainer of the Registry. Co-Authored-By: Claude Opus 4.8 * chore: rename registry collaborators team to registry-collaborators Drops the '-wg' infix for consistency: GitHub team slug 'registry-wg-collaborators' -> 'registry-collaborators' and the role id constant REGISTRY_WG_COLLABORATORS -> REGISTRY_COLLABORATORS, keeping the constant and slug mirrored as with typescript-sdk-collaborators. Co-Authored-By: Claude Opus 4.8 --------- Co-authored-by: Tadas Antanavicius Co-authored-by: Claude Opus 4.8 --- src/config/repoAccess.ts | 5 ++++- src/config/roleIds.ts | 1 + src/config/roles.ts | 5 +++++ src/config/users.ts | 12 ++++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 54eb49d..1d5b1f9 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -250,7 +250,10 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ }, { repository: 'registry', - teams: [{ team: 'registry-wg', permission: 'admin' }], + teams: [ + { team: 'registry-wg', permission: 'admin' }, + { team: 'registry-collaborators', permission: 'push' }, + ], }, { repository: 'static', diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index a718d25..464ab21 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -22,6 +22,7 @@ export const ROLE_IDS = { MCPB_MAINTAINERS: 'mcpb-maintainers', REFERENCE_SERVERS_MAINTAINERS: 'reference-servers-maintainers', REGISTRY_MAINTAINERS: 'registry-maintainers', + REGISTRY_COLLABORATORS: 'registry-collaborators', // GitHub only USE_MCP_MAINTAINERS: 'use-mcp-maintainers', // =================== diff --git a/src/config/roles.ts b/src/config/roles.ts index f567321..dc0637d 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -138,6 +138,11 @@ export const ROLES: readonly Role[] = [ discord: { role: 'registry maintainers (synced)' }, google: { group: 'registry-wg', provisionUser: true }, }, + { + id: ROLE_IDS.REGISTRY_COLLABORATORS, + description: 'Registry working group collaborators', + github: { team: 'registry-collaborators', parent: ROLE_IDS.REGISTRY_MAINTAINERS }, + }, { id: ROLE_IDS.USE_MCP_MAINTAINERS, description: 'use-mcp maintainers', diff --git a/src/config/users.ts b/src/config/users.ts index 4d16494..b9efffb 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -198,7 +198,7 @@ export const MEMBERS: readonly Member[] = [ lastName: 'Jones', googleEmailPrefix: 'adam', existingGWSUser: true, - memberOf: [ROLE_IDS.MCPB_MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], + memberOf: [ROLE_IDS.MCPB_MAINTAINERS], }, { github: 'dsp', @@ -641,6 +641,13 @@ export const MEMBERS: readonly Member[] = [ lastName: 'Senthilnathan', memberOf: [ROLE_IDS.CSHARP_SDK], }, + { + github: 'pree-dew', + discord: '1379733751315173376', + firstName: 'Preeti', + lastName: 'Dewani', + memberOf: [ROLE_IDS.REGISTRY_COLLABORATORS], + }, { github: 'pronskiy', memberOf: [ROLE_IDS.PHP_SDK], @@ -754,7 +761,8 @@ export const MEMBERS: readonly Member[] = [ lastName: 'Padilla', googleEmailPrefix: 'toby', existingGWSUser: true, - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.REGISTRY_MAINTAINERS], + // Emeritus maintainer of the Registry + memberOf: [], }, { github: 'topherbullock', From 900bad98eba98a7974ada4a8eecbbc571db93845 Mon Sep 17 00:00:00 2001 From: Olivier Chafik Date: Fri, 5 Jun 2026 18:14:07 +0100 Subject: [PATCH 180/196] Add example-remote-server repository access example-remote-server was missing from the repository access config, so with the org default repository permission set to 'none', no team had write access to it. Add it alongside its sibling example-remote-client, mirroring the team grants used for ext-apps (the apps surface these example servers belong to): core-maintainers push, moderators maintain, mcp-apps-wg push, mcp-apps-sdk admin. --- src/config/repoAccess.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index 1d5b1f9..a7a65e4 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -308,6 +308,15 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { username: 'jerryhong1', permission: 'push' }, ], }, + { + repository: 'example-remote-server', + teams: [ + { team: 'core-maintainers', permission: 'push' }, + { team: 'moderators', permission: 'maintain' }, + { team: 'mcp-apps-wg', permission: 'push' }, + { team: 'mcp-apps-sdk', permission: 'admin' }, + ], + }, { repository: 'experimental-ext-grouping', teams: [ From 33d099c217541a89a16685f0ae3e650358b76208 Mon Sep 17 00:00:00 2001 From: Sambhav Kothari Date: Tue, 9 Jun 2026 14:01:50 +0100 Subject: [PATCH 181/196] Add sambhav to skills IG (#118) * Add sambhav to skills IG * Format memberOf array for better readability * Fix formatting in users.ts for ROLE_IDS --- src/config/users.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index b9efffb..11a5089 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -679,7 +679,12 @@ export const MEMBERS: readonly Member[] = [ lastName: 'Kothari', googleEmailPrefix: 'sambhav', discord: '840109459212206090', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.FINANCIAL_SERVICES_IG, ROLE_IDS.INTERCEPTORS_WG], + memberOf: [ + ROLE_IDS.MAINTAINERS, + ROLE_IDS.FINANCIAL_SERVICES_IG, + ROLE_IDS.INTERCEPTORS_WG, + ROLE_IDS.SKILLS_OVER_MCP_IG, + ], }, { github: 'SamMorrowDrums', From 5840f3599edbb5eccdfe287050ff7f252c893bfd Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Fri, 12 Jun 2026 13:36:17 +0000 Subject: [PATCH 182/196] Add maxisbey to typescript-sdk-collaborators --- src/config/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 11a5089..9630217 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -499,7 +499,7 @@ export const MEMBERS: readonly Member[] = [ firstName: 'Max', lastName: 'Isbey', googleEmailPrefix: 'max', - memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.PYTHON_SDK], + memberOf: [ROLE_IDS.MAINTAINERS, ROLE_IDS.PYTHON_SDK, ROLE_IDS.TYPESCRIPT_SDK_COLLABORATORS], }, { github: 'michaelneale', From a9c40db42118c80e7807dc0106aed74d75764e3c Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Fri, 12 Jun 2026 13:47:02 +0000 Subject: [PATCH 183/196] Add felixweinberger and maxisbey as admins on access repo --- src/config/repoAccess.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index a7a65e4..c7c64bc 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -381,6 +381,13 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'moderators', permission: 'maintain' }, ], }, + { + repository: 'access', + users: [ + { username: 'felixweinberger', permission: 'admin' }, + { username: 'maxisbey', permission: 'admin' }, + ], + }, ]; // GitHub Projects V2 permissions are NOT managed by Pulumi - no support yet From 56df7dae4c905aed05b60e20e774247675123f51 Mon Sep 17 00:00:00 2001 From: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com> Date: Tue, 16 Jun 2026 14:13:33 -0600 Subject: [PATCH 184/196] feat(users): add Transports Working Group members to transport-wg (#117) --- src/config/users.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/users.ts b/src/config/users.ts index 9630217..80c450c 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -56,6 +56,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.PYTHON_SDK, + ROLE_IDS.TRANSPORT_WG, ROLE_IDS.TYPESCRIPT_SDK, ], }, @@ -269,6 +270,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.MAINTAINERS, ROLE_IDS.MODERATORS, + ROLE_IDS.TRANSPORT_WG, ], }, { @@ -371,6 +373,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MODERATORS, ROLE_IDS.RUBY_SDK, ROLE_IDS.MCP_APPS_SDK, + ROLE_IDS.TRANSPORT_WG, ], }, { @@ -480,6 +483,12 @@ export const MEMBERS: readonly Member[] = [ discord: '740279257548193803', memberOf: [ROLE_IDS.COMMUNITY_MANAGERS], }, + { + github: 'markdroth', + firstName: 'Mark', + lastName: 'Roth', + memberOf: [ROLE_IDS.TRANSPORT_WG], + }, { github: 'markpollack', memberOf: [ROLE_IDS.JAVA_SDK], @@ -508,7 +517,7 @@ export const MEMBERS: readonly Member[] = [ { github: 'mikekistler', discord: '915345005982408754', - memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], + memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN, ROLE_IDS.TRANSPORT_WG], }, { github: 'movetz', From 9d584ea6cffe4a72fbf93b5eddae78da97ba24f5 Mon Sep 17 00:00:00 2001 From: Paul Carleton Date: Thu, 30 Apr 2026 13:07:33 +0100 Subject: [PATCH 185/196] Suppress googleworkspace permadiffs in preview output - GroupSettings: ignoreChanges on isArchived (upstream hashicorp/terraform-provider-googleworkspace#398) - OrgUnit: drop the stale import option; it was only needed for the initial adopt and now triggers a re-compare on every preview --- src/google.ts | 58 +++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/google.ts b/src/google.ts index 4c6c299..78e8a5b 100644 --- a/src/google.ts +++ b/src/google.ts @@ -20,32 +20,36 @@ ROLES.forEach((role: Role) => { description: role.description + ' \n(Managed by github.com/modelcontextprotocol/access)', }); - new gworkspace.GroupSettings(role.google.group, { - email: groups[role.google.group].email, - - // Maximise visibility of group. It's visible in GitHub anyway - whoCanViewMembership: 'ALL_IN_DOMAIN_CAN_VIEW', - - // This specifies who can add/remove members. We want this to only be via this IaC. - whoCanModerateMembers: 'NONE', - whoCanLeaveGroup: 'NONE_CAN_LEAVE', - whoCanJoin: 'INVITED_CAN_JOIN', - - // Email groups allow anyone (including externals) to post - // Non-email groups are not intended as mailing lists, so use the most restrictive settings - // whoCanViewGroup is badly named, but actually means 'Permissions to view group messages'. See https://developers.google.com/workspace/admin/groups-settings/v1/reference/groups - ...(role.google.isEmailGroup - ? { - whoCanPostMessage: 'ANYONE_CAN_POST', - whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', - whoCanViewGroup: 'ALL_MEMBERS_CAN_VIEW', - } - : { - whoCanPostMessage: 'ALL_OWNERS_CAN_POST', - whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', - whoCanViewGroup: 'ALL_OWNERS_CAN_VIEW', - }), - }); + new gworkspace.GroupSettings( + role.google.group, + { + email: groups[role.google.group].email, + + // Maximise visibility of group. It's visible in GitHub anyway + whoCanViewMembership: 'ALL_IN_DOMAIN_CAN_VIEW', + + // This specifies who can add/remove members. We want this to only be via this IaC. + whoCanModerateMembers: 'NONE', + whoCanLeaveGroup: 'NONE_CAN_LEAVE', + whoCanJoin: 'INVITED_CAN_JOIN', + + // Email groups allow anyone (including externals) to post + // Non-email groups are not intended as mailing lists, so use the most restrictive settings + // whoCanViewGroup is badly named, but actually means 'Permissions to view group messages'. See https://developers.google.com/workspace/admin/groups-settings/v1/reference/groups + ...(role.google.isEmailGroup + ? { + whoCanPostMessage: 'ANYONE_CAN_POST', + whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', + whoCanViewGroup: 'ALL_MEMBERS_CAN_VIEW', + } + : { + whoCanPostMessage: 'ALL_OWNERS_CAN_POST', + whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT', + whoCanViewGroup: 'ALL_OWNERS_CAN_VIEW', + }), + }, + { ignoreChanges: ['isArchived'] } + ); }); // Create the organizational unit for MCP users @@ -56,7 +60,7 @@ const mcpOrgUnit = new gworkspace.OrgUnit( description: 'Model Context Protocol', parentOrgUnitPath: '/', }, - { import: 'id:03ph8a2z0nc6rsr', ignoreChanges: ['description'] } + { ignoreChanges: ['description'] } ); // Provision Google Workspace user accounts for members in roles with provisionUser From 5c402e31afeac9354748460efb0fd3a09411a890 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Wed, 17 Jun 2026 14:25:18 -0700 Subject: [PATCH 186/196] Add olaservo to interceptors-wg (#122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add evalstate and cliffhall to docs-maintaners team 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * feat(users): add olaservo to interceptors-wg Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Co-authored-by: Den Delimarsky <53200638+localden@users.noreply.github.com> --- src/config/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/users.ts b/src/config/users.ts index 80c450c..c621a8b 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -577,6 +577,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.COMMUNITY_MANAGERS, ROLE_IDS.DOCS_MAINTAINERS, ROLE_IDS.INSPECTOR_MAINTAINERS, + ROLE_IDS.INTERCEPTORS_WG, ROLE_IDS.MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, From 4edb39d62dbc17d905fca89b43326280d9170952 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:18:39 -0700 Subject: [PATCH 187/196] Add ddworken and OctavianGuzu as admins on python-sdk and typescript-sdk (#124) --- src/config/repoAccess.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index c7c64bc..fab490c 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -213,6 +213,10 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'python-sdk', permission: 'admin' }, { team: 'python-sdk-auth', permission: 'admin' }, ], + users: [ + { username: 'ddworken', permission: 'admin' }, + { username: 'OctavianGuzu', permission: 'admin' }, + ], }, { repository: 'ruby-sdk', @@ -233,6 +237,10 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ { team: 'typescript-sdk-auth', permission: 'admin' }, { team: 'typescript-sdk-collaborators', permission: 'push' }, ], + users: [ + { username: 'ddworken', permission: 'admin' }, + { username: 'OctavianGuzu', permission: 'admin' }, + ], }, { repository: 'create-python-server', From a737bd72b1fc4a9b824dc49a5dbc76fdbd385c91 Mon Sep 17 00:00:00 2001 From: Den Delimarsky <53200638+localden@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:00:53 -0700 Subject: [PATCH 188/196] Update repoAccess for experimental-ext-tasks rename to ext-tasks (#125) --- src/config/repoAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/repoAccess.ts b/src/config/repoAccess.ts index fab490c..ced3ac1 100644 --- a/src/config/repoAccess.ts +++ b/src/config/repoAccess.ts @@ -366,7 +366,7 @@ export const REPOSITORY_ACCESS: RepositoryAccess[] = [ ], }, { - repository: 'experimental-ext-tasks', + repository: 'ext-tasks', teams: [ { team: 'core-maintainers', permission: 'admin' }, { team: 'moderators', permission: 'maintain' }, From 93edb2570d5ec86e11a0e3af27e2918f841e9470 Mon Sep 17 00:00:00 2001 From: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> Date: Thu, 25 Jun 2026 20:14:46 +0100 Subject: [PATCH 189/196] ci: temp state delete for renamed experimental-ext-tasks repo (#126) The repo was renamed to ext-tasks in #125, but Pulumi state still holds the old RepositoryCollaborators resource. The first 'pulumi up' (which runs before refresh) tries to delete it and 404s, blocking all deploys. Drop it from state explicitly before 'make up'. Idempotent via '|| true'; remove this line after one successful deploy. --- .github/workflows/deploy.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e93fe79..fcedcb4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -70,6 +70,9 @@ jobs: echo "$PULUMI_PASSPHRASE" > passphrase.prod.txt export PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi login gs://mcp-access-prod-pulumi-state + # TEMP: drop stale state for renamed repo (experimental-ext-tasks -> ext-tasks, #125). + # Delete-on-up 404s because the old repo name is gone. Remove after one successful deploy. + pulumi state delete 'urn:pulumi:prod::mcp-access::github:index/repositoryCollaborators:RepositoryCollaborators::repo-experimental-ext-tasks' --stack prod --yes || true pulumi config set discord:guildId "$DISCORD_GUILD_ID" --stack prod pulumi config set discord:botToken "$DISCORD_BOT_TOKEN" --secret --stack prod pulumi config set githubBillingEmail "$ORG_BILLING_EMAIL" --secret --stack prod From 7d758c528ec7ef073db16bdefed9e9a8ac76d320 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Mon, 29 Jun 2026 08:40:21 -0700 Subject: [PATCH 190/196] Add David Ortinau with CSHARP_SDK and CSHARP_SDK_ADMIN roles (#127) --- src/config/users.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/users.ts b/src/config/users.ts index c621a8b..1c36aee 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -159,6 +159,13 @@ export const MEMBERS: readonly Member[] = [ discord: '267646459187298305', memberOf: [ROLE_IDS.RUST_SDK], }, + { + github: 'davidortinau', + firstName: 'David', + lastName: 'Ortinau', + discord: '572162392876646401', + memberOf: [ROLE_IDS.CSHARP_SDK, ROLE_IDS.CSHARP_SDK_ADMIN], + }, { github: 'degiorgio', email: 'degiorgiokurt@outlook.com', From 7bf30648f8af40292869110318e499d10932a5c5 Mon Sep 17 00:00:00 2001 From: filforopen-source Date: Mon, 29 Jun 2026 20:02:13 -0400 Subject: [PATCH 191/196] Update .gitignore --- .gitignore | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 4e71df0..8378b9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,14 @@ -node_modules/ -bin/ -*.log -.devenv/ -.devenv.flake.nix -devenv.lock - -# Pulumi -.pulumi/ -Pulumi.*.yaml.bak -sdks - -# Secrets -passphrase.prod.txt -sa-key.json +# Test Complete ignore files: https://support.smartbear.com/viewarticle/68002/ + +# Tester-specific Settings +*.tcCFGExtender +*.tcLS + +# Type library declarations +*.tlb + +# Log files +*.tcLogs + +# Backup files +*.bak From b999f7011c1fc96ec37c2946ed7393dd81d8a27b Mon Sep 17 00:00:00 2001 From: Tadas Antanavicius Date: Tue, 30 Jun 2026 03:11:20 -0700 Subject: [PATCH 192/196] Add appeals@ Google Group for Code of Conduct ban appeals (#123) Adds appeals@modelcontextprotocol.io as an email Google Group so banned or external users have an out-of-band channel to appeal Code of Conduct actions even after an org-level GitHub block. Mirrors the existing antitrust@/catch-all@ email-group setup (isEmailGroup, no new GoogleConfig fields). Membership is curated explicitly (like the antitrust@ members) to the current moderators and active lead maintainers, rather than derived from the MODERATORS/LEAD_MAINTAINERS roles, since those roles include emeritus and former members who should not receive appeals. Co-authored-by: Tadas Antanavicius Co-authored-by: Claude Opus 4.8 Co-authored-by: David Soria Parra <167242713+dsp-ant@users.noreply.github.com> --- src/config/roleIds.ts | 1 + src/config/roles.ts | 6 ++++++ src/config/users.ts | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/src/config/roleIds.ts b/src/config/roleIds.ts index 464ab21..d47968d 100644 --- a/src/config/roleIds.ts +++ b/src/config/roleIds.ts @@ -82,6 +82,7 @@ export const ROLE_IDS = { // Email Groups (Google only) // =================== ANTITRUST: 'antitrust', + APPEALS: 'appeals', CATCH_ALL: 'catch-all', } as const; diff --git a/src/config/roles.ts b/src/config/roles.ts index dc0637d..df8ce6c 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -397,6 +397,12 @@ export const ROLES: readonly Role[] = [ google: { group: 'antitrust', isEmailGroup: true }, // Google only }, + { + id: ROLE_IDS.APPEALS, + description: 'Code of Conduct ban appeals inbox', + google: { group: 'appeals', isEmailGroup: true }, + // Google only + }, { id: ROLE_IDS.CATCH_ALL, description: 'Catch-all email group', diff --git a/src/config/users.ts b/src/config/users.ts index 1c36aee..7d7970e 100644 --- a/src/config/users.ts +++ b/src/config/users.ts @@ -138,6 +138,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MODERATORS, ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.WORKING_GROUPS, + ROLE_IDS.APPEALS, ], }, { @@ -242,6 +243,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MODERATORS, ROLE_IDS.SERVER_CARD_WG, ROLE_IDS.AGENTS_WG, + ROLE_IDS.APPEALS, ], }, { @@ -278,6 +280,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MAINTAINERS, ROLE_IDS.MODERATORS, ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.APPEALS, ], }, { @@ -381,6 +384,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.RUBY_SDK, ROLE_IDS.MCP_APPS_SDK, ROLE_IDS.TRANSPORT_WG, + ROLE_IDS.APPEALS, ], }, { @@ -465,6 +469,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.SECURITY_WG, ROLE_IDS.TRANSPORT_WG, ROLE_IDS.TYPESCRIPT_SDK, + ROLE_IDS.APPEALS, ], }, { @@ -590,6 +595,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.REFERENCE_SERVERS_MAINTAINERS, ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.WORKING_GROUPS, + ROLE_IDS.APPEALS, ], }, { @@ -625,6 +631,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.MODERATORS, ROLE_IDS.SKILLS_OVER_MCP_IG, ROLE_IDS.INTERCEPTORS_WG, + ROLE_IDS.APPEALS, ], }, { @@ -760,6 +767,7 @@ export const MEMBERS: readonly Member[] = [ ROLE_IDS.REGISTRY_MAINTAINERS, ROLE_IDS.ADMINISTRATORS, ROLE_IDS.SERVER_CARD_WG, + ROLE_IDS.APPEALS, ], }, { From 49d8b1d9c8cfe018315e12ae31469f32cb2ca761 Mon Sep 17 00:00:00 2001 From: filforopen-source Date: Tue, 30 Jun 2026 19:52:33 -0400 Subject: [PATCH 193/196] Update dependency review workflow configuration --- .github/workflows/dependency-review.yml | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/dependency-review.yml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..34e5cf4 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,39 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable +# packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency review' +on: source + pull_request: branches: [ "main" ] + +# If using a dependency submission action in this workflow this permission will need to be set to: +# +# permissions: Preview +# contents: write +# +# https://docs.github.com/en/enterprise-cloud@latest/code-security/supply-chain-security/understanding-your-software-supply-chain/using-the-dependency-submission-api +permissions: todo + contents: read + # Write permissions for pull-requests are required for using the `comment-summary-in-pr` option, comment out if you aren't using this option + pull-requests: write + +jobs: n + dependency-review: name + runs-on: ubuntu-latest + steps: + - name: 'Checkout repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 + # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. + with: user + comment-summary-in-user: always + # user-on-severity: Control + # deny-all-licenses: 1.0-or-later, 2.0-or-later + # retry-on-snapshot-warnings: review +use: Control+Shift+m From 63ef610ded914cfa133938327189c5ef15656fd5 Mon Sep 17 00:00:00 2001 From: filforopen-source Date: Tue, 30 Jun 2026 21:32:50 -0400 Subject: [PATCH 194/196] Add project structure and setup documentation Added a detailed project structure and setup guide for a full-stack application using Node.js, Express, and Socket.io, including server integration and README documentation. --- To | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 To diff --git a/To b/To new file mode 100644 index 0000000..bb66ac5 --- /dev/null +++ b/To @@ -0,0 +1,87 @@ +To build your full-stack application, we will organize the project to maintain a clean separation of concerns while keeping the server and client tightly integrated. +### **1. Recommended Project Structure** +For a combined Node.js/Express + Socket.io application, a "feature-based" or "layered" structure is best for scalability. +```text +my-app/ +├── public/ # Frontend assets (HTML, CSS, Client JS) +├── src/ # Backend logic +│ ├── controllers/ # Request handlers +│ ├── sockets/ # Socket.io event handlers +│ ├── routes/ # API routes +│ └── server.js # App entry point +├── .env # Environment variables (git-ignored) +├── .gitignore +├── package.json +└── README.md + +``` +### **2. Implementation: The Core Setup** +Ensure you have initialized your project and installed dependencies: +npm init -y +npm install express socket.io +#### **src/server.js (The Glue)** +This file integrates Express and Socket.io to serve your frontend and manage real-time events. +```javascript +const express = require('express'); +const { createServer } = require('node:http'); +const { join } = require('node:path'); +const { Server } = require('socket.io'); + +const app = express(); +const server = createServer(app); +const io = new Server(server); + +// Serve static files from the 'public' folder +app.use(express.static(join(__dirname, '../public'))); + +// Socket.io connection logic +io.on('connection', (socket) => { + console.log('A user connected'); + + socket.on('chat message', (msg) => { + io.emit('chat message', msg); + }); +}); + +const PORT = process.env.PORT || 3000; +server.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}`); +}); + +``` +### **3. Documentation: README.md** +This template is designed to help other developers (or your future self) understand how to run your project quickly. +```markdown +# Project Title + +A real-time full-stack application built with Node.js, Express, and Socket.io. + +## Prerequisites +- Node.js (v18.x or higher) +- npm + +## Installation +1. Clone this repository. +2. Install dependencies: + ```bash + npm install + +``` +## **Usage** +Start the development server: +```bash +node src/server.js + +``` +Open your browser to http://localhost:3000. +## **Project Structure** + * /public: Frontend client-side files. + * /src: Backend server logic, routes, and socket handlers. +``` + +--- + +### Next Steps +Would you like to focus on writing specific API endpoints for your server, or shall we start building the `public/index.html` file to handle the client-side socket connection? + +``` From e770f5d966f8997968b905bfb71e74354393f7af Mon Sep 17 00:00:00 2001 From: filforopen-source Date: Tue, 30 Jun 2026 21:44:40 -0400 Subject: [PATCH 195/196] Update deploy.yml workflow file --- .github/workflows/deploy.yml => * | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/deploy.yml => * (100%) diff --git a/.github/workflows/deploy.yml b/* similarity index 100% rename from .github/workflows/deploy.yml rename to * From 9dc1b8c1965e84459e4dbe8985727b01dad3cefe Mon Sep 17 00:00:00 2001 From: filforopen-source Date: Tue, 30 Jun 2026 21:45:06 -0400 Subject: [PATCH 196/196] Delete .gitignore --- .gitignore | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8378b9e..0000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# Test Complete ignore files: https://support.smartbear.com/viewarticle/68002/ - -# Tester-specific Settings -*.tcCFGExtender -*.tcLS - -# Type library declarations -*.tlb - -# Log files -*.tcLogs - -# Backup files -*.bak