Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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.

## Deployment

Expand Down
13 changes: 13 additions & 0 deletions src/config/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ 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.
export const GROUPS = defineGroups([
{
name: 'test-parent',
Expand All @@ -12,4 +15,14 @@ export const GROUPS = defineGroups([
description: 'Registry maintainers',
memberOf: ['test-parent'],
},
{
name: 'test-email-group',
description: 'Example email group that accepts external emails',
isEmailGroup: true,
},
{
name: 'antitrust',
description: 'Antitrust compliance contacts',
isEmailGroup: true,
},
] as const);
12 changes: 12 additions & 0 deletions src/config/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,16 @@ export const MEMBERS: readonly Member[] = [
email: 'adam@modelcontextprotocol.io',
memberOf: ['test-child'],
},
{
email: 'davidsp@anthropic.com',
memberOf: ['antitrust'],
},
{
email: 'mattsamuels@anthropic.com',
memberOf: ['antitrust'],
},
{
email: 'davideramian@anthropic.com',
memberOf: ['antitrust'],
},
] as const;
4 changes: 3 additions & 1 deletion src/config/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function defineGroups<
name: Lowercase<string>;
description: string;
memberOf?: readonly (T[number]['name'])[];
isEmailGroup?: boolean;
}[]
>(groups: T) {
for (const group of groups) {
Expand All @@ -28,10 +29,11 @@ export interface Group {
name: GroupKey;
description: string;
memberOf?: readonly GroupKey[];
isEmailGroup?: boolean;
}

export interface Member {
github?: string;
email?: `${string}@modelcontextprotocol.io`;
email?: string;
memberOf: readonly GroupKey[];
}
23 changes: 15 additions & 8 deletions src/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,25 @@ GROUPS.forEach((group: Group) => {

// 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',

// We don't intend these groups to be used as mailing lists, so we set posting and viewing messages to the most restrictive settings currently available.
whoCanPostMessage: 'ALL_OWNERS_CAN_POST',
whoCanContactOwner: 'ALL_OWNERS_CAN_CONTACT',
// This is badly named, but actually means 'Permissions to view group messages'. See https://developers.google.com/workspace/admin/groups-settings/v1/reference/groups
whoCanViewGroup: 'ALL_OWNERS_CAN_VIEW',


// 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.memberOf?.forEach((parentGroupKey) => {
Expand Down