mcp-google-multi 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,133 @@
1
+ import { z } from 'zod';
2
+ import { google } from 'googleapis';
3
+ import { ACCOUNTS } from '../accounts.js';
4
+ import { getClient } from '../client.js';
5
+ import { handleGoogleApiError } from './_errors.js';
6
+ const accountEnum = z.enum(ACCOUNTS);
7
+ export function registerMeetTools(server) {
8
+ // ─── Conference records (past meetings) ────────────────────────────────
9
+ server.registerTool('meet_conference_records_list', {
10
+ description: 'List past conference records (completed Meet sessions) the user has access to',
11
+ inputSchema: {
12
+ account: accountEnum.describe('Google account alias'),
13
+ pageSize: z.number().min(1).max(100).optional(),
14
+ pageToken: z.string().optional(),
15
+ filter: z.string().optional().describe('Filter expression, e.g. "space.meeting_code=abc-defg-hij"'),
16
+ },
17
+ }, async ({ account, pageSize, pageToken, filter }) => {
18
+ try {
19
+ const auth = await getClient(account);
20
+ const meet = google.meet({ version: 'v2', auth });
21
+ const res = await meet.conferenceRecords.list({
22
+ pageSize: pageSize ?? 20,
23
+ pageToken,
24
+ filter,
25
+ });
26
+ return {
27
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
28
+ };
29
+ }
30
+ catch (error) {
31
+ return handleMeetError(error, account);
32
+ }
33
+ });
34
+ server.registerTool('meet_conference_record_get', {
35
+ description: 'Get a single conference record by resource name (e.g. conferenceRecords/abc123)',
36
+ inputSchema: {
37
+ account: accountEnum.describe('Google account alias'),
38
+ name: z.string().describe('Resource name, format: conferenceRecords/{conference_record}'),
39
+ },
40
+ }, async ({ account, name }) => {
41
+ try {
42
+ const auth = await getClient(account);
43
+ const meet = google.meet({ version: 'v2', auth });
44
+ const res = await meet.conferenceRecords.get({ name });
45
+ return {
46
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
47
+ };
48
+ }
49
+ catch (error) {
50
+ return handleMeetError(error, account);
51
+ }
52
+ });
53
+ // ─── Recordings ────────────────────────────────────────────────────────
54
+ server.registerTool('meet_recordings_list', {
55
+ description: 'List recordings within a conference record. Returns Drive file IDs that can be downloaded via drive_download.',
56
+ inputSchema: {
57
+ account: accountEnum.describe('Google account alias'),
58
+ parent: z.string().describe('Parent conference record, format: conferenceRecords/{id}'),
59
+ pageSize: z.number().min(1).max(100).optional(),
60
+ pageToken: z.string().optional(),
61
+ },
62
+ }, async ({ account, parent, pageSize, pageToken }) => {
63
+ try {
64
+ const auth = await getClient(account);
65
+ const meet = google.meet({ version: 'v2', auth });
66
+ const res = await meet.conferenceRecords.recordings.list({
67
+ parent,
68
+ pageSize: pageSize ?? 20,
69
+ pageToken,
70
+ });
71
+ return {
72
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
73
+ };
74
+ }
75
+ catch (error) {
76
+ return handleMeetError(error, account);
77
+ }
78
+ });
79
+ // ─── Transcripts ───────────────────────────────────────────────────────
80
+ server.registerTool('meet_transcripts_list', {
81
+ description: 'List transcripts within a conference record',
82
+ inputSchema: {
83
+ account: accountEnum.describe('Google account alias'),
84
+ parent: z.string().describe('Parent conference record, format: conferenceRecords/{id}'),
85
+ pageSize: z.number().min(1).max(100).optional(),
86
+ pageToken: z.string().optional(),
87
+ },
88
+ }, async ({ account, parent, pageSize, pageToken }) => {
89
+ try {
90
+ const auth = await getClient(account);
91
+ const meet = google.meet({ version: 'v2', auth });
92
+ const res = await meet.conferenceRecords.transcripts.list({
93
+ parent,
94
+ pageSize: pageSize ?? 20,
95
+ pageToken,
96
+ });
97
+ return {
98
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
99
+ };
100
+ }
101
+ catch (error) {
102
+ return handleMeetError(error, account);
103
+ }
104
+ });
105
+ server.registerTool('meet_transcript_entries_list', {
106
+ description: 'List entries (per-speaker text segments) within a transcript. The actual transcript content.',
107
+ inputSchema: {
108
+ account: accountEnum.describe('Google account alias'),
109
+ parent: z.string().describe('Parent transcript, format: conferenceRecords/{id}/transcripts/{id}'),
110
+ pageSize: z.number().min(1).max(1000).optional(),
111
+ pageToken: z.string().optional(),
112
+ },
113
+ }, async ({ account, parent, pageSize, pageToken }) => {
114
+ try {
115
+ const auth = await getClient(account);
116
+ const meet = google.meet({ version: 'v2', auth });
117
+ const res = await meet.conferenceRecords.transcripts.entries.list({
118
+ parent,
119
+ pageSize: pageSize ?? 200,
120
+ pageToken,
121
+ });
122
+ return {
123
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
124
+ };
125
+ }
126
+ catch (error) {
127
+ return handleMeetError(error, account);
128
+ }
129
+ });
130
+ }
131
+ function handleMeetError(error, account) {
132
+ return handleGoogleApiError(error, account, "Meet API requires the meetings.space.readonly scope and the Google Meet API enabled in Cloud Console. Confirm both for this account.");
133
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerSearchConsoleTools(server: McpServer): void;
@@ -0,0 +1,258 @@
1
+ import { z } from 'zod';
2
+ import { google } from 'googleapis';
3
+ import { ACCOUNTS } from '../accounts.js';
4
+ import { getClient } from '../client.js';
5
+ import { handleGoogleApiError } from './_errors.js';
6
+ const accountEnum = z.enum(ACCOUNTS);
7
+ export function registerSearchConsoleTools(server) {
8
+ // ─── Sites ───────────────────────────────────────────────
9
+ server.registerTool('searchconsole_sites_list', {
10
+ description: 'List all sites (properties) the account has access to in Google Search Console',
11
+ inputSchema: {
12
+ account: accountEnum.describe('Google account alias'),
13
+ },
14
+ }, async ({ account }) => {
15
+ try {
16
+ const auth = await getClient(account);
17
+ const wm = google.webmasters({ version: 'v3', auth });
18
+ const res = await wm.sites.list();
19
+ return {
20
+ content: [{ type: 'text', text: JSON.stringify(res.data.siteEntry ?? [], null, 2) }],
21
+ };
22
+ }
23
+ catch (error) {
24
+ return handleSearchConsoleError(error, account);
25
+ }
26
+ });
27
+ server.registerTool('searchconsole_sites_get', {
28
+ description: 'Get details for a specific site (property) in Google Search Console',
29
+ inputSchema: {
30
+ account: accountEnum.describe('Google account alias'),
31
+ siteUrl: z.string().describe('Site URL exactly as it appears in Search Console (e.g. "https://example.com/" or "sc-domain:example.com")'),
32
+ },
33
+ }, async ({ account, siteUrl }) => {
34
+ try {
35
+ const auth = await getClient(account);
36
+ const wm = google.webmasters({ version: 'v3', auth });
37
+ const res = await wm.sites.get({ siteUrl });
38
+ return {
39
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
40
+ };
41
+ }
42
+ catch (error) {
43
+ return handleSearchConsoleError(error, account);
44
+ }
45
+ });
46
+ server.registerTool('searchconsole_sites_add', {
47
+ description: 'Add a site (property) to Google Search Console. You still need to verify ownership separately.',
48
+ inputSchema: {
49
+ account: accountEnum.describe('Google account alias'),
50
+ siteUrl: z.string().describe('Site URL to add (e.g. "https://example.com/" or "sc-domain:example.com")'),
51
+ },
52
+ }, async ({ account, siteUrl }) => {
53
+ try {
54
+ const auth = await getClient(account);
55
+ const wm = google.webmasters({ version: 'v3', auth });
56
+ await wm.sites.add({ siteUrl });
57
+ return {
58
+ content: [{ type: 'text', text: JSON.stringify({ success: true, siteUrl }, null, 2) }],
59
+ };
60
+ }
61
+ catch (error) {
62
+ return handleSearchConsoleError(error, account);
63
+ }
64
+ });
65
+ server.registerTool('searchconsole_sites_delete', {
66
+ description: 'Remove a site (property) from Google Search Console',
67
+ inputSchema: {
68
+ account: accountEnum.describe('Google account alias'),
69
+ siteUrl: z.string().describe('Site URL to remove'),
70
+ },
71
+ }, async ({ account, siteUrl }) => {
72
+ try {
73
+ const auth = await getClient(account);
74
+ const wm = google.webmasters({ version: 'v3', auth });
75
+ await wm.sites.delete({ siteUrl });
76
+ return {
77
+ content: [{ type: 'text', text: JSON.stringify({ success: true, deleted: siteUrl }, null, 2) }],
78
+ };
79
+ }
80
+ catch (error) {
81
+ return handleSearchConsoleError(error, account);
82
+ }
83
+ });
84
+ // ─── Sitemaps ────────────────────────────────────────────
85
+ server.registerTool('searchconsole_sitemaps_list', {
86
+ description: 'List all sitemaps submitted for a site in Google Search Console',
87
+ inputSchema: {
88
+ account: accountEnum.describe('Google account alias'),
89
+ siteUrl: z.string().describe('Site URL (e.g. "https://example.com/" or "sc-domain:example.com")'),
90
+ },
91
+ }, async ({ account, siteUrl }) => {
92
+ try {
93
+ const auth = await getClient(account);
94
+ const wm = google.webmasters({ version: 'v3', auth });
95
+ const res = await wm.sitemaps.list({ siteUrl });
96
+ return {
97
+ content: [{ type: 'text', text: JSON.stringify(res.data.sitemap ?? [], null, 2) }],
98
+ };
99
+ }
100
+ catch (error) {
101
+ return handleSearchConsoleError(error, account);
102
+ }
103
+ });
104
+ server.registerTool('searchconsole_sitemaps_get', {
105
+ description: 'Get details for a specific sitemap submitted to Google Search Console',
106
+ inputSchema: {
107
+ account: accountEnum.describe('Google account alias'),
108
+ siteUrl: z.string().describe('Site URL'),
109
+ feedpath: z.string().describe('Full URL of the sitemap (e.g. "https://example.com/sitemap.xml")'),
110
+ },
111
+ }, async ({ account, siteUrl, feedpath }) => {
112
+ try {
113
+ const auth = await getClient(account);
114
+ const wm = google.webmasters({ version: 'v3', auth });
115
+ const res = await wm.sitemaps.get({ siteUrl, feedpath });
116
+ return {
117
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
118
+ };
119
+ }
120
+ catch (error) {
121
+ return handleSearchConsoleError(error, account);
122
+ }
123
+ });
124
+ server.registerTool('searchconsole_sitemaps_submit', {
125
+ description: 'Submit a sitemap to Google Search Console for a site',
126
+ inputSchema: {
127
+ account: accountEnum.describe('Google account alias'),
128
+ siteUrl: z.string().describe('Site URL'),
129
+ feedpath: z.string().describe('Full URL of the sitemap to submit (e.g. "https://example.com/sitemap.xml")'),
130
+ },
131
+ }, async ({ account, siteUrl, feedpath }) => {
132
+ try {
133
+ const auth = await getClient(account);
134
+ const wm = google.webmasters({ version: 'v3', auth });
135
+ await wm.sitemaps.submit({ siteUrl, feedpath });
136
+ return {
137
+ content: [{ type: 'text', text: JSON.stringify({ success: true, siteUrl, feedpath }, null, 2) }],
138
+ };
139
+ }
140
+ catch (error) {
141
+ return handleSearchConsoleError(error, account);
142
+ }
143
+ });
144
+ server.registerTool('searchconsole_sitemaps_delete', {
145
+ description: 'Delete a sitemap from Google Search Console',
146
+ inputSchema: {
147
+ account: accountEnum.describe('Google account alias'),
148
+ siteUrl: z.string().describe('Site URL'),
149
+ feedpath: z.string().describe('Full URL of the sitemap to delete'),
150
+ },
151
+ }, async ({ account, siteUrl, feedpath }) => {
152
+ try {
153
+ const auth = await getClient(account);
154
+ const wm = google.webmasters({ version: 'v3', auth });
155
+ await wm.sitemaps.delete({ siteUrl, feedpath });
156
+ return {
157
+ content: [{ type: 'text', text: JSON.stringify({ success: true, deleted: feedpath }, null, 2) }],
158
+ };
159
+ }
160
+ catch (error) {
161
+ return handleSearchConsoleError(error, account);
162
+ }
163
+ });
164
+ // ─── Search Analytics ────────────────────────────────────
165
+ server.registerTool('searchconsole_searchanalytics_query', {
166
+ description: 'Query Google Search Console search analytics data. Returns clicks, impressions, CTR, and position for your site. Supports filtering by query, page, country, device, search type, and date range.',
167
+ inputSchema: {
168
+ account: accountEnum.describe('Google account alias'),
169
+ siteUrl: z.string().describe('Site URL (e.g. "https://example.com/" or "sc-domain:example.com")'),
170
+ startDate: z.string().describe('Start date (YYYY-MM-DD). Data is available starting ~3 days ago.'),
171
+ endDate: z.string().describe('End date (YYYY-MM-DD)'),
172
+ dimensions: z.array(z.enum(['query', 'page', 'country', 'device', 'searchAppearance', 'date'])).optional()
173
+ .describe('Dimensions to group by. Common: ["query", "page"], ["date"], ["query", "date"]'),
174
+ type: z.enum(['web', 'image', 'video', 'news', 'discover', 'googleNews']).optional()
175
+ .describe('Search type filter (default: web)'),
176
+ dimensionFilterGroups: z.array(z.object({
177
+ groupType: z.enum(['and']).optional(),
178
+ filters: z.array(z.object({
179
+ dimension: z.enum(['query', 'page', 'country', 'device', 'searchAppearance']),
180
+ operator: z.enum(['contains', 'equals', 'notContains', 'notEquals', 'includingRegex', 'excludingRegex']),
181
+ expression: z.string(),
182
+ })),
183
+ })).optional()
184
+ .describe('Filters to narrow results. Example: filter by page containing "/blog/" or query containing "keyword"'),
185
+ rowLimit: z.number().min(1).max(25000).optional()
186
+ .describe('Max rows to return (default: 1000, max: 25000)'),
187
+ startRow: z.number().min(0).optional()
188
+ .describe('Zero-based row offset for pagination (default: 0)'),
189
+ aggregationType: z.enum(['auto', 'byPage', 'byProperty']).optional()
190
+ .describe('How to aggregate data (default: auto)'),
191
+ dataState: z.enum(['all', 'final']).optional()
192
+ .describe('"all" includes fresh (not finalized) data; "final" only finalized data'),
193
+ },
194
+ }, async ({ account, siteUrl, startDate, endDate, dimensions, type, dimensionFilterGroups, rowLimit, startRow, aggregationType, dataState }) => {
195
+ try {
196
+ const auth = await getClient(account);
197
+ const wm = google.webmasters({ version: 'v3', auth });
198
+ const requestBody = { startDate, endDate };
199
+ if (dimensions)
200
+ requestBody.dimensions = dimensions;
201
+ if (type)
202
+ requestBody.type = type;
203
+ if (dimensionFilterGroups)
204
+ requestBody.dimensionFilterGroups = dimensionFilterGroups;
205
+ if (rowLimit)
206
+ requestBody.rowLimit = rowLimit;
207
+ if (startRow !== undefined)
208
+ requestBody.startRow = startRow;
209
+ if (aggregationType)
210
+ requestBody.aggregationType = aggregationType;
211
+ if (dataState)
212
+ requestBody.dataState = dataState;
213
+ const res = await wm.searchanalytics.query({ siteUrl, requestBody });
214
+ const summary = {
215
+ rowCount: res.data.rows?.length ?? 0,
216
+ responseAggregationType: res.data.responseAggregationType,
217
+ rows: res.data.rows ?? [],
218
+ };
219
+ return {
220
+ content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }],
221
+ };
222
+ }
223
+ catch (error) {
224
+ return handleSearchConsoleError(error, account);
225
+ }
226
+ });
227
+ // ─── URL Inspection ──────────────────────────────────────
228
+ server.registerTool('searchconsole_url_inspect', {
229
+ description: 'Inspect a URL using the Google Search Console URL Inspection API. Returns indexing status, crawl info, rich results, AMP status, and mobile usability for a specific URL.',
230
+ inputSchema: {
231
+ account: accountEnum.describe('Google account alias'),
232
+ siteUrl: z.string().describe('Site URL as registered in Search Console (e.g. "https://example.com/" or "sc-domain:example.com")'),
233
+ inspectionUrl: z.string().describe('The fully-qualified URL to inspect (must be under the siteUrl property)'),
234
+ languageCode: z.string().optional().describe('BCP-47 language code for localized results (e.g. "en-US", "fr")'),
235
+ },
236
+ }, async ({ account, siteUrl, inspectionUrl, languageCode }) => {
237
+ try {
238
+ const auth = await getClient(account);
239
+ const searchconsole = google.searchconsole({ version: 'v1', auth });
240
+ const res = await searchconsole.urlInspection.index.inspect({
241
+ requestBody: {
242
+ inspectionUrl,
243
+ siteUrl,
244
+ languageCode: languageCode ?? 'en',
245
+ },
246
+ });
247
+ return {
248
+ content: [{ type: 'text', text: JSON.stringify(res.data, null, 2) }],
249
+ };
250
+ }
251
+ catch (error) {
252
+ return handleSearchConsoleError(error, account);
253
+ }
254
+ });
255
+ }
256
+ function handleSearchConsoleError(error, account) {
257
+ return handleGoogleApiError(error, account);
258
+ }
@@ -0,0 +1,34 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerSheetsTools(server: McpServer): void;
3
+ export declare function buildCellFormat(input: {
4
+ backgroundColor?: {
5
+ red?: number;
6
+ green?: number;
7
+ blue?: number;
8
+ alpha?: number;
9
+ };
10
+ textFormat?: {
11
+ foregroundColor?: {
12
+ red?: number;
13
+ green?: number;
14
+ blue?: number;
15
+ alpha?: number;
16
+ };
17
+ bold?: boolean;
18
+ italic?: boolean;
19
+ underline?: boolean;
20
+ strikethrough?: boolean;
21
+ fontFamily?: string;
22
+ fontSize?: number;
23
+ };
24
+ horizontalAlignment?: string;
25
+ verticalAlignment?: string;
26
+ wrapStrategy?: string;
27
+ numberFormat?: {
28
+ type: string;
29
+ pattern?: string;
30
+ };
31
+ }): {
32
+ format: any;
33
+ fields: string;
34
+ };