// Google Meet MCP Tools
// 18 tools for managing meeting spaces, conference records, recordings, and transcripts
import { z } from 'zod';
import type { MeetService } from '../services/meet.js';
// ─────────────────────────────────────────────────────────────────────────────
// SCHEMAS
// ─────────────────────────────────────────────────────────────────────────────
const AccessTypeSchema = z.enum(['OPEN', 'TRUSTED', 'RESTRICTED']).optional();
const EntryPointAccessSchema = z.enum(['ALL', 'CREATOR_APP_ONLY']).optional();
const ModerationModeSchema = z.enum(['OFF', 'ON']).optional();
const RestrictionTypeSchema = z.enum(['HOSTS_ONLY', 'NO_RESTRICTION']).optional();
const DefaultJoinAsViewerSchema = z.enum(['ON', 'OFF']).optional();
const MemberRoleSchema = z.enum(['COHOST']).optional();
const ModerationRestrictionsSchema = z.object({
chatRestriction: RestrictionTypeSchema,
reactionRestriction: RestrictionTypeSchema,
presentRestriction: RestrictionTypeSchema,
defaultJoinAsViewerType: DefaultJoinAsViewerSchema,
}).optional();
const PaginationSchema = z.object({
pageSize: z.number().min(1).max(100).optional().describe('Maximum number of results per page (1-100)'),
pageToken: z.string().optional().describe('Token for pagination'),
});
// ─────────────────────────────────────────────────────────────────────────────
// TOOL DEFINITIONS
// ─────────────────────────────────────────────────────────────────────────────
export const meetTools = [
// ─────────────────────────────────────────────────────────────────────────
// SPACES
// ─────────────────────────────────────────────────────────────────────────
{
name: 'meet_createSpace',
description: 'Create a new Google Meet meeting space. Returns meeting URI and code that can be shared with participants. Optionally configure access controls, moderation settings, and auto-recording/transcription.',
inputSchema: {
type: 'object' as const,
properties: {
accessType: {
type: 'string',
enum: ['OPEN', 'TRUSTED', 'RESTRICTED'],
description: 'Who can join without knocking. OPEN: anyone with link, TRUSTED: same organization, RESTRICTED: only invited users'
},
entryPointAccess: {
type: 'string',
enum: ['ALL', 'CREATOR_APP_ONLY'],
description: 'Entry points allowed. ALL: any method, CREATOR_APP_ONLY: only via creating app'
},
moderation: {
type: 'string',
enum: ['OFF', 'ON'],
description: 'Enable moderation mode for host controls'
},
moderationRestrictions: {
type: 'object',
properties: {
chatRestriction: { type: 'string', enum: ['HOSTS_ONLY', 'NO_RESTRICTION'] },
reactionRestriction: { type: 'string', enum: ['HOSTS_ONLY', 'NO_RESTRICTION'] },
presentRestriction: { type: 'string', enum: ['HOSTS_ONLY', 'NO_RESTRICTION'] },
defaultJoinAsViewerType: { type: 'string', enum: ['ON', 'OFF'] }
},
description: 'Feature restrictions when moderation is ON'
},
autoRecording: {
type: 'boolean',
description: 'Automatically start recording when meeting begins'
},
autoTranscription: {
type: 'boolean',
description: 'Automatically enable transcription'
},
autoSmartNotes: {
type: 'boolean',
description: 'Automatically enable "take notes for me" feature'
},
generateAttendanceReport: {
type: 'boolean',
description: 'Generate attendance report after meeting'
}
},
required: []
}
},
{
name: 'meet_getSpace',
description: 'Get details of a Google Meet meeting space including its configuration and active conference status.',
inputSchema: {
type: 'object' as const,
properties: {
spaceId: {
type: 'string',
description: 'Space ID, meeting code (e.g., "abc-mnop-xyz"), or full resource name'
}
},
required: ['spaceId']
}
},
{
name: 'meet_updateSpace',
description: 'Update configuration of an existing Google Meet meeting space.',
inputSchema: {
type: 'object' as const,
properties: {
spaceId: {
type: 'string',
description: 'Space ID or meeting code to update'
},
accessType: {
type: 'string',
enum: ['OPEN', 'TRUSTED', 'RESTRICTED'],
description: 'Who can join without knocking'
},
entryPointAccess: {
type: 'string',
enum: ['ALL', 'CREATOR_APP_ONLY'],
description: 'Entry points allowed'
},
moderation: {
type: 'string',
enum: ['OFF', 'ON'],
description: 'Enable/disable moderation mode'
},
moderationRestrictions: {
type: 'object',
properties: {
chatRestriction: { type: 'string', enum: ['HOSTS_ONLY', 'NO_RESTRICTION'] },
reactionRestriction: { type: 'string', enum: ['HOSTS_ONLY', 'NO_RESTRICTION'] },
presentRestriction: { type: 'string', enum: ['HOSTS_ONLY', 'NO_RESTRICTION'] },
defaultJoinAsViewerType: { type: 'string', enum: ['ON', 'OFF'] }
},
description: 'Feature restrictions when moderation is ON'
},
autoRecording: { type: 'boolean' },
autoTranscription: { type: 'boolean' },
autoSmartNotes: { type: 'boolean' },
generateAttendanceReport: { type: 'boolean' }
},
required: ['spaceId']
}
},
{
name: 'meet_endActiveConference',
description: 'End the currently active conference in a meeting space. All participants will be disconnected.',
inputSchema: {
type: 'object' as const,
properties: {
spaceId: {
type: 'string',
description: 'Space ID or meeting code'
}
},
required: ['spaceId']
}
},
// ─────────────────────────────────────────────────────────────────────────
// MEMBERS
// ─────────────────────────────────────────────────────────────────────────
{
name: 'meet_addMember',
description: 'Add a member (e.g., co-host) to a meeting space. Members can join without knocking and may have special permissions.',
inputSchema: {
type: 'object' as const,
properties: {
spaceId: {
type: 'string',
description: 'Space ID or meeting code'
},
email: {
type: 'string',
description: 'Email address of the user to add'
},
role: {
type: 'string',
enum: ['COHOST'],
description: 'Role to assign. COHOST gives same abilities as meeting organizer'
}
},
required: ['spaceId', 'email']
}
},
{
name: 'meet_listMembers',
description: 'List all members configured for a meeting space.',
inputSchema: {
type: 'object' as const,
properties: {
spaceId: {
type: 'string',
description: 'Space ID or meeting code'
},
pageSize: {
type: 'number',
description: 'Maximum results per page (1-100)'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: ['spaceId']
}
},
{
name: 'meet_deleteMember',
description: 'Remove a member from a meeting space.',
inputSchema: {
type: 'object' as const,
properties: {
spaceId: {
type: 'string',
description: 'Space ID or meeting code'
},
memberId: {
type: 'string',
description: 'Member ID to remove'
}
},
required: ['spaceId', 'memberId']
}
},
// ─────────────────────────────────────────────────────────────────────────
// CONFERENCE RECORDS
// ─────────────────────────────────────────────────────────────────────────
{
name: 'meet_listConferenceRecords',
description: 'List all conference records (past meetings). Can filter by space or time range.',
inputSchema: {
type: 'object' as const,
properties: {
filter: {
type: 'string',
description: 'Filter expression. E.g., "space.name=spaces/abc" or "start_time>2024-01-01T00:00:00Z"'
},
pageSize: {
type: 'number',
description: 'Maximum results per page (1-100)'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: []
}
},
{
name: 'meet_getConferenceRecord',
description: 'Get details of a specific conference record including start/end times and space.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID or full resource name'
}
},
required: ['conferenceId']
}
},
// ─────────────────────────────────────────────────────────────────────────
// PARTICIPANTS
// ─────────────────────────────────────────────────────────────────────────
{
name: 'meet_listParticipants',
description: 'List all participants who attended a conference.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
filter: {
type: 'string',
description: 'Filter expression'
},
pageSize: {
type: 'number',
description: 'Maximum results per page (1-100)'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: ['conferenceId']
}
},
{
name: 'meet_getParticipant',
description: 'Get details of a specific participant including join/leave times and user info.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
participantId: {
type: 'string',
description: 'Participant ID'
}
},
required: ['conferenceId', 'participantId']
}
},
{
name: 'meet_listParticipantSessions',
description: 'List all sessions for a participant (each join/leave is a separate session).',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
participantId: {
type: 'string',
description: 'Participant ID'
},
pageSize: {
type: 'number',
description: 'Maximum results per page'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: ['conferenceId', 'participantId']
}
},
// ─────────────────────────────────────────────────────────────────────────
// RECORDINGS
// ─────────────────────────────────────────────────────────────────────────
{
name: 'meet_listRecordings',
description: 'List all recordings from a conference. Recordings are stored as MP4 files in Google Drive.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
pageSize: {
type: 'number',
description: 'Maximum results per page'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: ['conferenceId']
}
},
{
name: 'meet_getRecording',
description: 'Get details of a specific recording including Drive file ID and playback URL.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
recordingId: {
type: 'string',
description: 'Recording ID'
}
},
required: ['conferenceId', 'recordingId']
}
},
// ─────────────────────────────────────────────────────────────────────────
// TRANSCRIPTS
// ─────────────────────────────────────────────────────────────────────────
{
name: 'meet_listTranscripts',
description: 'List all transcripts from a conference. Transcripts are stored as Google Docs.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
pageSize: {
type: 'number',
description: 'Maximum results per page'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: ['conferenceId']
}
},
{
name: 'meet_getTranscript',
description: 'Get details of a specific transcript including Google Docs document ID and URL.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
transcriptId: {
type: 'string',
description: 'Transcript ID'
}
},
required: ['conferenceId', 'transcriptId']
}
},
{
name: 'meet_listTranscriptEntries',
description: 'List individual transcript entries (speech segments) with speaker info and timestamps.',
inputSchema: {
type: 'object' as const,
properties: {
conferenceId: {
type: 'string',
description: 'Conference record ID'
},
transcriptId: {
type: 'string',
description: 'Transcript ID'
},
pageSize: {
type: 'number',
description: 'Maximum results per page'
},
pageToken: {
type: 'string',
description: 'Pagination token'
}
},
required: ['conferenceId', 'transcriptId']
}
},
];
// ─────────────────────────────────────────────────────────────────────────────
// ZOD VALIDATION SCHEMAS
// ─────────────────────────────────────────────────────────────────────────────
const CreateSpaceSchema = z.object({
accessType: AccessTypeSchema,
entryPointAccess: EntryPointAccessSchema,
moderation: ModerationModeSchema,
moderationRestrictions: ModerationRestrictionsSchema,
autoRecording: z.boolean().optional(),
autoTranscription: z.boolean().optional(),
autoSmartNotes: z.boolean().optional(),
generateAttendanceReport: z.boolean().optional(),
});
const GetSpaceSchema = z.object({
spaceId: z.string().min(1),
});
const UpdateSpaceSchema = z.object({
spaceId: z.string().min(1),
accessType: AccessTypeSchema,
entryPointAccess: EntryPointAccessSchema,
moderation: ModerationModeSchema,
moderationRestrictions: ModerationRestrictionsSchema,
autoRecording: z.boolean().optional(),
autoTranscription: z.boolean().optional(),
autoSmartNotes: z.boolean().optional(),
generateAttendanceReport: z.boolean().optional(),
});
const EndActiveConferenceSchema = z.object({
spaceId: z.string().min(1),
});
const AddMemberSchema = z.object({
spaceId: z.string().min(1),
email: z.string().email(),
role: MemberRoleSchema,
});
const ListMembersSchema = z.object({
spaceId: z.string().min(1),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
const DeleteMemberSchema = z.object({
spaceId: z.string().min(1),
memberId: z.string().min(1),
});
const ListConferenceRecordsSchema = z.object({
filter: z.string().optional(),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
const GetConferenceRecordSchema = z.object({
conferenceId: z.string().min(1),
});
const ListParticipantsSchema = z.object({
conferenceId: z.string().min(1),
filter: z.string().optional(),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
const GetParticipantSchema = z.object({
conferenceId: z.string().min(1),
participantId: z.string().min(1),
});
const ListParticipantSessionsSchema = z.object({
conferenceId: z.string().min(1),
participantId: z.string().min(1),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
const ListRecordingsSchema = z.object({
conferenceId: z.string().min(1),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
const GetRecordingSchema = z.object({
conferenceId: z.string().min(1),
recordingId: z.string().min(1),
});
const ListTranscriptsSchema = z.object({
conferenceId: z.string().min(1),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
const GetTranscriptSchema = z.object({
conferenceId: z.string().min(1),
transcriptId: z.string().min(1),
});
const ListTranscriptEntriesSchema = z.object({
conferenceId: z.string().min(1),
transcriptId: z.string().min(1),
pageSize: z.number().min(1).max(100).optional(),
pageToken: z.string().optional(),
});
// ─────────────────────────────────────────────────────────────────────────────
// HANDLERS
// ─────────────────────────────────────────────────────────────────────────────
export function createMeetHandlers(meetService: MeetService) {
return {
// Spaces
meet_createSpace: async (args: unknown) => {
const params = CreateSpaceSchema.parse(args);
const space = await meetService.createSpace(params);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
space: {
name: space.name,
meetingUri: space.meetingUri,
meetingCode: space.meetingCode,
config: space.config,
hasActiveConference: !!space.activeConference
}
}, null, 2)
}]
};
},
meet_getSpace: async (args: unknown) => {
const { spaceId } = GetSpaceSchema.parse(args);
const space = await meetService.getSpace(spaceId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
space: {
name: space.name,
meetingUri: space.meetingUri,
meetingCode: space.meetingCode,
config: space.config,
activeConference: space.activeConference
}
}, null, 2)
}]
};
},
meet_updateSpace: async (args: unknown) => {
const params = UpdateSpaceSchema.parse(args);
const space = await meetService.updateSpace(params);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
message: 'Space updated successfully',
space: {
name: space.name,
meetingUri: space.meetingUri,
config: space.config
}
}, null, 2)
}]
};
},
meet_endActiveConference: async (args: unknown) => {
const { spaceId } = EndActiveConferenceSchema.parse(args);
await meetService.endActiveConference(spaceId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
message: 'Active conference ended successfully'
}, null, 2)
}]
};
},
// Members
meet_addMember: async (args: unknown) => {
const params = AddMemberSchema.parse(args);
const member = await meetService.addMember(params);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
message: 'Member added successfully',
member
}, null, 2)
}]
};
},
meet_listMembers: async (args: unknown) => {
const { spaceId, pageSize, pageToken } = ListMembersSchema.parse(args);
const result = await meetService.listMembers(spaceId, { pageSize, pageToken });
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
members: result.members,
totalCount: result.members.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
meet_deleteMember: async (args: unknown) => {
const { spaceId, memberId } = DeleteMemberSchema.parse(args);
await meetService.deleteMember(spaceId, memberId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
message: 'Member removed successfully'
}, null, 2)
}]
};
},
// Conference Records
meet_listConferenceRecords: async (args: unknown) => {
const params = ListConferenceRecordsSchema.parse(args);
const result = await meetService.listConferenceRecords(params);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
conferenceRecords: result.conferenceRecords.map(cr => ({
name: cr.name,
startTime: cr.startTime,
endTime: cr.endTime,
space: cr.space
})),
totalCount: result.conferenceRecords.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
meet_getConferenceRecord: async (args: unknown) => {
const { conferenceId } = GetConferenceRecordSchema.parse(args);
const record = await meetService.getConferenceRecord(conferenceId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
conferenceRecord: record
}, null, 2)
}]
};
},
// Participants
meet_listParticipants: async (args: unknown) => {
const { conferenceId, filter, pageSize, pageToken } = ListParticipantsSchema.parse(args);
const result = await meetService.listParticipants(conferenceId, { filter, pageSize, pageToken });
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
participants: result.participants.map(p => ({
name: p.name,
earliestStartTime: p.earliestStartTime,
latestEndTime: p.latestEndTime,
user: p.signedinUser || p.anonymousUser || p.phoneUser
})),
totalCount: result.participants.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
meet_getParticipant: async (args: unknown) => {
const { conferenceId, participantId } = GetParticipantSchema.parse(args);
const participant = await meetService.getParticipant(conferenceId, participantId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
participant
}, null, 2)
}]
};
},
meet_listParticipantSessions: async (args: unknown) => {
const { conferenceId, participantId, pageSize, pageToken } = ListParticipantSessionsSchema.parse(args);
const result = await meetService.listParticipantSessions(conferenceId, participantId, { pageSize, pageToken });
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
sessions: result.participantSessions,
totalCount: result.participantSessions.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
// Recordings
meet_listRecordings: async (args: unknown) => {
const { conferenceId, pageSize, pageToken } = ListRecordingsSchema.parse(args);
const result = await meetService.listRecordings(conferenceId, { pageSize, pageToken });
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
recordings: result.recordings.map(r => ({
name: r.name,
state: r.state,
startTime: r.startTime,
endTime: r.endTime,
driveFileId: r.driveDestination?.file,
playbackUrl: r.driveDestination?.exportUri
})),
totalCount: result.recordings.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
meet_getRecording: async (args: unknown) => {
const { conferenceId, recordingId } = GetRecordingSchema.parse(args);
const recording = await meetService.getRecording(conferenceId, recordingId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
recording: {
name: recording.name,
state: recording.state,
startTime: recording.startTime,
endTime: recording.endTime,
driveFileId: recording.driveDestination?.file,
playbackUrl: recording.driveDestination?.exportUri
}
}, null, 2)
}]
};
},
// Transcripts
meet_listTranscripts: async (args: unknown) => {
const { conferenceId, pageSize, pageToken } = ListTranscriptsSchema.parse(args);
const result = await meetService.listTranscripts(conferenceId, { pageSize, pageToken });
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
transcripts: result.transcripts.map(t => ({
name: t.name,
state: t.state,
startTime: t.startTime,
endTime: t.endTime,
docsId: t.docsDestination?.document,
docsUrl: t.docsDestination?.exportUri
})),
totalCount: result.transcripts.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
meet_getTranscript: async (args: unknown) => {
const { conferenceId, transcriptId } = GetTranscriptSchema.parse(args);
const transcript = await meetService.getTranscript(conferenceId, transcriptId);
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
transcript: {
name: transcript.name,
state: transcript.state,
startTime: transcript.startTime,
endTime: transcript.endTime,
docsId: transcript.docsDestination?.document,
docsUrl: transcript.docsDestination?.exportUri
}
}, null, 2)
}]
};
},
meet_listTranscriptEntries: async (args: unknown) => {
const { conferenceId, transcriptId, pageSize, pageToken } = ListTranscriptEntriesSchema.parse(args);
const result = await meetService.listTranscriptEntries(conferenceId, transcriptId, { pageSize, pageToken });
return {
content: [{
type: 'text' as const,
text: JSON.stringify({
success: true,
entries: result.transcriptEntries.map(e => ({
name: e.name,
participant: e.participant,
text: e.text,
languageCode: e.languageCode,
startTime: e.startTime,
endTime: e.endTime
})),
totalCount: result.transcriptEntries.length,
nextPageToken: result.nextPageToken
}, null, 2)
}]
};
},
};
}