@piotr-agier/google-drive-mcp 1.5.0 → 1.6.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.
- package/README.md +36 -0
- package/dist/index.js +336 -5
- package/dist/index.js.map +3 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -402,6 +402,24 @@ Add the server to your Claude Desktop configuration:
|
|
|
402
402
|
- `exportMimeType`: For Google Workspace files, MIME type to export as (optional, e.g., 'application/pdf', 'text/csv')
|
|
403
403
|
- `overwrite`: Whether to overwrite existing files (optional, default: false)
|
|
404
404
|
|
|
405
|
+
#### PDF Ingestion and Conversion (v1.6.0)
|
|
406
|
+
- **convertPdfToGoogleDoc** - Convert a PDF already stored in Drive into an editable Google Doc
|
|
407
|
+
- `fileId`: Source PDF file ID
|
|
408
|
+
- `newName`: Optional destination doc name
|
|
409
|
+
- `parentFolderId`: Optional destination folder
|
|
410
|
+
|
|
411
|
+
- **bulkConvertFolderPdfs** - Convert all PDFs in a folder and return per-file success/failure summary
|
|
412
|
+
- `folderId`: Source folder ID
|
|
413
|
+
- `maxResults`: Maximum PDFs to process (optional, default: 100)
|
|
414
|
+
- `continueOnError`: Continue processing after individual failures (optional, default: true)
|
|
415
|
+
|
|
416
|
+
- **uploadPdfWithSplit** - Upload a local PDF, optionally split into chunked PDF parts before upload
|
|
417
|
+
- `localPath`: Absolute local path to PDF
|
|
418
|
+
- `split`: Enable split mode metadata output (optional, default: false)
|
|
419
|
+
- `maxPagesPerChunk`: Advisory chunk size for split planning (optional)
|
|
420
|
+
- `parentFolderId`: Optional destination folder
|
|
421
|
+
- `namePrefix`: Optional uploaded file name prefix
|
|
422
|
+
|
|
405
423
|
### Folder Operations
|
|
406
424
|
- **createFolder** - Create a new folder
|
|
407
425
|
- `name`: Folder name
|
|
@@ -433,6 +451,24 @@ Add the server to your Claude Desktop configuration:
|
|
|
433
451
|
- `documentId`: Document ID
|
|
434
452
|
- `includeContent`: Include content summary (character count) for each tab (optional)
|
|
435
453
|
|
|
454
|
+
- **addDocumentTab** - Add a new tab in a Google Doc
|
|
455
|
+
- `documentId`: Document ID
|
|
456
|
+
- `title`: Tab title
|
|
457
|
+
|
|
458
|
+
- **renameDocumentTab** - Rename an existing tab in a Google Doc
|
|
459
|
+
- `documentId`: Document ID
|
|
460
|
+
- `tabId`: Tab ID
|
|
461
|
+
- `title`: New tab title
|
|
462
|
+
|
|
463
|
+
- **insertSmartChip** - Insert a person smart chip (mention) at a document index. Only person chips are supported by the Docs API; date and file chips are read-only.
|
|
464
|
+
- `documentId`: Document ID
|
|
465
|
+
- `index`: Insertion index (1-based)
|
|
466
|
+
- `chipType`: `person` (only supported type)
|
|
467
|
+
- `personEmail`: Email address for the person mention
|
|
468
|
+
|
|
469
|
+
- **readSmartChips** - Read smart chip-like elements (person mentions, rich links, date chips) from the default tab of a document. Only the default tab is scanned; other tabs are not included.
|
|
470
|
+
- `documentId`: Document ID
|
|
471
|
+
|
|
436
472
|
- **listGoogleDocs** - List Google Documents with optional filtering
|
|
437
473
|
- `query`: Search query to filter by name or content (optional)
|
|
438
474
|
- `maxResults`: Maximum documents to return, 1-100 (optional, default: 20)
|
package/dist/index.js
CHANGED
|
@@ -609,7 +609,7 @@ async function authenticate() {
|
|
|
609
609
|
// src/index.ts
|
|
610
610
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
611
611
|
import { readFileSync } from "fs";
|
|
612
|
-
import { join as
|
|
612
|
+
import { join as join4, dirname as dirname4 } from "path";
|
|
613
613
|
|
|
614
614
|
// src/utils.ts
|
|
615
615
|
function buildCalendarEventUpdate(existing, overrides) {
|
|
@@ -690,6 +690,10 @@ __export(drive_exports, {
|
|
|
690
690
|
});
|
|
691
691
|
import { z } from "zod";
|
|
692
692
|
import { existsSync as existsSync2, statSync as statSync2, createReadStream } from "fs";
|
|
693
|
+
import { mkdtemp, readFile as readFile3, writeFile as writeFile2, rm } from "fs/promises";
|
|
694
|
+
import { tmpdir } from "os";
|
|
695
|
+
import { basename as basename2, extname as extname2, join as join3 } from "path";
|
|
696
|
+
import { PDFDocument } from "pdf-lib";
|
|
693
697
|
|
|
694
698
|
// src/download-file.ts
|
|
695
699
|
import { createWriteStream, existsSync, renameSync, statSync, unlinkSync } from "fs";
|
|
@@ -986,6 +990,44 @@ var ShareFileSchema = z.object({
|
|
|
986
990
|
role: z.enum(["writer", "commenter", "reader"]).default("reader"),
|
|
987
991
|
sendNotificationEmail: z.boolean().optional().default(true)
|
|
988
992
|
});
|
|
993
|
+
var ConvertPdfToGoogleDocSchema = z.object({
|
|
994
|
+
fileId: z.string().min(1, "File ID is required"),
|
|
995
|
+
newName: z.string().optional(),
|
|
996
|
+
parentFolderId: z.string().optional()
|
|
997
|
+
});
|
|
998
|
+
var BulkConvertFolderPdfsSchema = z.object({
|
|
999
|
+
folderId: z.string().min(1, "Folder ID is required"),
|
|
1000
|
+
maxResults: z.number().int().min(1).max(200).optional().default(100),
|
|
1001
|
+
continueOnError: z.boolean().optional().default(true)
|
|
1002
|
+
});
|
|
1003
|
+
var UploadPdfWithSplitSchema = z.object({
|
|
1004
|
+
localPath: z.string().min(1, "Local file path is required"),
|
|
1005
|
+
split: z.boolean().optional().default(false),
|
|
1006
|
+
maxPagesPerChunk: z.number().int().min(1).max(500).optional(),
|
|
1007
|
+
parentFolderId: z.string().optional(),
|
|
1008
|
+
namePrefix: z.string().optional()
|
|
1009
|
+
});
|
|
1010
|
+
async function splitPdfIntoChunkFiles(localPath, maxPagesPerChunk) {
|
|
1011
|
+
const sourceBytes = await readFile3(localPath);
|
|
1012
|
+
const source = await PDFDocument.load(sourceBytes);
|
|
1013
|
+
const pageCount = source.getPageCount();
|
|
1014
|
+
if (pageCount === 0) {
|
|
1015
|
+
throw new Error("PDF contains no pages.");
|
|
1016
|
+
}
|
|
1017
|
+
const tempDir = await mkdtemp(join3(tmpdir(), "gdrive-mcp-split-"));
|
|
1018
|
+
const files = [];
|
|
1019
|
+
for (let start = 0, part = 1; start < pageCount; start += maxPagesPerChunk, part++) {
|
|
1020
|
+
const end = Math.min(start + maxPagesPerChunk, pageCount);
|
|
1021
|
+
const chunkDoc = await PDFDocument.create();
|
|
1022
|
+
const pages = await chunkDoc.copyPages(source, Array.from({ length: end - start }, (_, i) => start + i));
|
|
1023
|
+
for (const page of pages) chunkDoc.addPage(page);
|
|
1024
|
+
const chunkBytes = await chunkDoc.save();
|
|
1025
|
+
const chunkPath = join3(tempDir, `part-${part}.pdf`);
|
|
1026
|
+
await writeFile2(chunkPath, chunkBytes);
|
|
1027
|
+
files.push(chunkPath);
|
|
1028
|
+
}
|
|
1029
|
+
return { tempDir, files };
|
|
1030
|
+
}
|
|
989
1031
|
var toolDefinitions = [
|
|
990
1032
|
{
|
|
991
1033
|
name: "search",
|
|
@@ -1208,6 +1250,47 @@ var toolDefinitions = [
|
|
|
1208
1250
|
},
|
|
1209
1251
|
required: ["fileId", "emailAddress"]
|
|
1210
1252
|
}
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
name: "convertPdfToGoogleDoc",
|
|
1256
|
+
description: "Convert an existing PDF in Drive into an editable Google Doc",
|
|
1257
|
+
inputSchema: {
|
|
1258
|
+
type: "object",
|
|
1259
|
+
properties: {
|
|
1260
|
+
fileId: { type: "string", description: "PDF file ID in Google Drive" },
|
|
1261
|
+
newName: { type: "string", description: "Optional name for converted Doc" },
|
|
1262
|
+
parentFolderId: { type: "string", description: "Optional destination folder ID" }
|
|
1263
|
+
},
|
|
1264
|
+
required: ["fileId"]
|
|
1265
|
+
}
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
name: "bulkConvertFolderPdfs",
|
|
1269
|
+
description: "Convert all PDFs in a folder into Google Docs and return per-file results",
|
|
1270
|
+
inputSchema: {
|
|
1271
|
+
type: "object",
|
|
1272
|
+
properties: {
|
|
1273
|
+
folderId: { type: "string", description: "Folder ID containing PDFs" },
|
|
1274
|
+
maxResults: { type: "number", description: "Maximum PDFs to process (1-200, default: 100)" },
|
|
1275
|
+
continueOnError: { type: "boolean", description: "Continue conversion when one file fails (default: true)" }
|
|
1276
|
+
},
|
|
1277
|
+
required: ["folderId"]
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
name: "uploadPdfWithSplit",
|
|
1282
|
+
description: "Upload PDF and optionally split into chunked parts (metadata split plan for now)",
|
|
1283
|
+
inputSchema: {
|
|
1284
|
+
type: "object",
|
|
1285
|
+
properties: {
|
|
1286
|
+
localPath: { type: "string", description: "Absolute path to local PDF" },
|
|
1287
|
+
split: { type: "boolean", description: "Enable split mode" },
|
|
1288
|
+
maxPagesPerChunk: { type: "number", description: "Target max pages per chunk (advisory metadata)" },
|
|
1289
|
+
parentFolderId: { type: "string", description: "Optional destination folder ID" },
|
|
1290
|
+
namePrefix: { type: "string", description: "Optional output name prefix" }
|
|
1291
|
+
},
|
|
1292
|
+
required: ["localPath"]
|
|
1293
|
+
}
|
|
1211
1294
|
}
|
|
1212
1295
|
];
|
|
1213
1296
|
async function handleTool(toolName, args, ctx) {
|
|
@@ -1712,6 +1795,127 @@ ${lines.join("\n")}` }], isError: false };
|
|
|
1712
1795
|
isError: false
|
|
1713
1796
|
};
|
|
1714
1797
|
}
|
|
1798
|
+
case "convertPdfToGoogleDoc": {
|
|
1799
|
+
const validation = ConvertPdfToGoogleDocSchema.safeParse(args);
|
|
1800
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
1801
|
+
const data = validation.data;
|
|
1802
|
+
const source = await ctx.getDrive().files.get({
|
|
1803
|
+
fileId: data.fileId,
|
|
1804
|
+
fields: "id,name,mimeType,parents",
|
|
1805
|
+
supportsAllDrives: true
|
|
1806
|
+
});
|
|
1807
|
+
if (source.data.mimeType !== "application/pdf") {
|
|
1808
|
+
return errorResponse(`File ${data.fileId} is not a PDF (mimeType=${source.data.mimeType || "unknown"})`);
|
|
1809
|
+
}
|
|
1810
|
+
const parentId = data.parentFolderId || source.data.parents?.[0];
|
|
1811
|
+
const converted = await ctx.getDrive().files.copy({
|
|
1812
|
+
fileId: data.fileId,
|
|
1813
|
+
requestBody: {
|
|
1814
|
+
name: data.newName || `${source.data.name || "Converted PDF"} (Doc)`,
|
|
1815
|
+
mimeType: "application/vnd.google-apps.document",
|
|
1816
|
+
...parentId ? { parents: [parentId] } : {}
|
|
1817
|
+
},
|
|
1818
|
+
fields: "id,name,webViewLink,mimeType",
|
|
1819
|
+
supportsAllDrives: true
|
|
1820
|
+
});
|
|
1821
|
+
return { content: [{ type: "text", text: `Converted PDF to Google Doc: ${converted.data.name}
|
|
1822
|
+
ID: ${converted.data.id}
|
|
1823
|
+
Link: ${converted.data.webViewLink}` }], isError: false };
|
|
1824
|
+
}
|
|
1825
|
+
case "bulkConvertFolderPdfs": {
|
|
1826
|
+
const validation = BulkConvertFolderPdfsSchema.safeParse(args);
|
|
1827
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
1828
|
+
const data = validation.data;
|
|
1829
|
+
const list = await ctx.getDrive().files.list({
|
|
1830
|
+
q: `'${escapeDriveQuery(data.folderId)}' in parents and mimeType='application/pdf' and trashed=false`,
|
|
1831
|
+
pageSize: data.maxResults,
|
|
1832
|
+
fields: "files(id,name,mimeType)",
|
|
1833
|
+
includeItemsFromAllDrives: true,
|
|
1834
|
+
supportsAllDrives: true
|
|
1835
|
+
});
|
|
1836
|
+
const files = list.data.files || [];
|
|
1837
|
+
const results = [];
|
|
1838
|
+
for (const f of files) {
|
|
1839
|
+
try {
|
|
1840
|
+
const converted = await ctx.getDrive().files.copy({
|
|
1841
|
+
fileId: f.id,
|
|
1842
|
+
requestBody: {
|
|
1843
|
+
name: `${f.name || "Converted PDF"} (Doc)`,
|
|
1844
|
+
mimeType: "application/vnd.google-apps.document",
|
|
1845
|
+
parents: [data.folderId]
|
|
1846
|
+
},
|
|
1847
|
+
fields: "id,name",
|
|
1848
|
+
supportsAllDrives: true
|
|
1849
|
+
});
|
|
1850
|
+
results.push({ id: f.id || void 0, name: f.name || void 0, docId: converted.data.id || void 0, ok: true });
|
|
1851
|
+
} catch (err) {
|
|
1852
|
+
const message = err?.message || "Unknown conversion error";
|
|
1853
|
+
results.push({ id: f.id || void 0, name: f.name || void 0, ok: false, error: message });
|
|
1854
|
+
if (!data.continueOnError) break;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
const ok = results.filter((r) => r.ok).length;
|
|
1858
|
+
const fail = results.length - ok;
|
|
1859
|
+
return {
|
|
1860
|
+
content: [{ type: "text", text: `Bulk PDF conversion finished. Processed=${results.length}, Success=${ok}, Failed=${fail}
|
|
1861
|
+
|
|
1862
|
+
${results.map((r) => r.ok ? `\u2705 ${r.name} -> ${r.docId}` : `\u274C ${r.name}: ${r.error}`).join("\n")}` }],
|
|
1863
|
+
isError: false
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
case "uploadPdfWithSplit": {
|
|
1867
|
+
const validation = UploadPdfWithSplitSchema.safeParse(args);
|
|
1868
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
1869
|
+
const data = validation.data;
|
|
1870
|
+
if (!existsSync2(data.localPath)) return errorResponse(`File not found: ${data.localPath}`);
|
|
1871
|
+
const parentId = await ctx.resolveFolderId(data.parentFolderId);
|
|
1872
|
+
if (!data.split) {
|
|
1873
|
+
const fileName = data.namePrefix || data.localPath.split(/[\\/]/).pop() || "upload.pdf";
|
|
1874
|
+
const uploaded = await ctx.getDrive().files.create({
|
|
1875
|
+
requestBody: { name: fileName, parents: [parentId] },
|
|
1876
|
+
media: { mimeType: "application/pdf", body: createReadStream(data.localPath) },
|
|
1877
|
+
fields: "id,name,webViewLink",
|
|
1878
|
+
supportsAllDrives: true
|
|
1879
|
+
});
|
|
1880
|
+
return {
|
|
1881
|
+
content: [{ type: "text", text: `Uploaded PDF without split: ${uploaded.data.name}
|
|
1882
|
+
ID: ${uploaded.data.id}` }],
|
|
1883
|
+
isError: false
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
const maxPagesPerChunk = data.maxPagesPerChunk ?? 25;
|
|
1887
|
+
const baseName = data.namePrefix || basename2(data.localPath, extname2(data.localPath));
|
|
1888
|
+
let tempDir;
|
|
1889
|
+
try {
|
|
1890
|
+
const splitResult = await splitPdfIntoChunkFiles(data.localPath, maxPagesPerChunk);
|
|
1891
|
+
tempDir = splitResult.tempDir;
|
|
1892
|
+
const uploadedParts = [];
|
|
1893
|
+
for (let i = 0; i < splitResult.files.length; i++) {
|
|
1894
|
+
const partPath = splitResult.files[i];
|
|
1895
|
+
const partName = `${baseName}-part-${i + 1}.pdf`;
|
|
1896
|
+
const uploaded = await ctx.getDrive().files.create({
|
|
1897
|
+
requestBody: { name: partName, parents: [parentId] },
|
|
1898
|
+
media: { mimeType: "application/pdf", body: createReadStream(partPath) },
|
|
1899
|
+
fields: "id,name,webViewLink",
|
|
1900
|
+
supportsAllDrives: true
|
|
1901
|
+
});
|
|
1902
|
+
uploadedParts.push({ id: uploaded.data.id, name: uploaded.data.name });
|
|
1903
|
+
}
|
|
1904
|
+
const lines = uploadedParts.map((p, idx) => `- part ${idx + 1}: ${p.name} (ID: ${p.id})`);
|
|
1905
|
+
return {
|
|
1906
|
+
content: [{
|
|
1907
|
+
type: "text",
|
|
1908
|
+
text: `Uploaded split PDF into ${uploadedParts.length} part(s) using maxPagesPerChunk=${maxPagesPerChunk}
|
|
1909
|
+
${lines.join("\n")}`
|
|
1910
|
+
}],
|
|
1911
|
+
isError: false
|
|
1912
|
+
};
|
|
1913
|
+
} finally {
|
|
1914
|
+
if (tempDir) {
|
|
1915
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1715
1919
|
default:
|
|
1716
1920
|
return null;
|
|
1717
1921
|
}
|
|
@@ -1725,7 +1929,7 @@ __export(docs_exports, {
|
|
|
1725
1929
|
});
|
|
1726
1930
|
import { z as z2 } from "zod";
|
|
1727
1931
|
import { createReadStream as createReadStream2, existsSync as existsSync3 } from "fs";
|
|
1728
|
-
import { basename as
|
|
1932
|
+
import { basename as basename3, extname as extname3 } from "path";
|
|
1729
1933
|
function hexToRgbColor(hex) {
|
|
1730
1934
|
if (!hex) return null;
|
|
1731
1935
|
let hexClean = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
@@ -2003,7 +2207,7 @@ async function uploadImageToDriveHelper(ctx, localFilePath, parentFolderId, make
|
|
|
2003
2207
|
if (!existsSync3(localFilePath)) {
|
|
2004
2208
|
throw new Error(`Image file not found: ${localFilePath}`);
|
|
2005
2209
|
}
|
|
2006
|
-
const fileName =
|
|
2210
|
+
const fileName = basename3(localFilePath);
|
|
2007
2211
|
const mimeTypeMap = {
|
|
2008
2212
|
".jpg": "image/jpeg",
|
|
2009
2213
|
".jpeg": "image/jpeg",
|
|
@@ -2013,7 +2217,7 @@ async function uploadImageToDriveHelper(ctx, localFilePath, parentFolderId, make
|
|
|
2013
2217
|
".webp": "image/webp",
|
|
2014
2218
|
".svg": "image/svg+xml"
|
|
2015
2219
|
};
|
|
2016
|
-
const ext =
|
|
2220
|
+
const ext = extname3(localFilePath).toLowerCase();
|
|
2017
2221
|
const mimeType = mimeTypeMap[ext] || "application/octet-stream";
|
|
2018
2222
|
const fileMetadata = {
|
|
2019
2223
|
name: fileName,
|
|
@@ -2197,6 +2401,24 @@ var FindAndReplaceInDocSchema = z2.object({
|
|
|
2197
2401
|
matchCase: z2.boolean().optional().default(false),
|
|
2198
2402
|
dryRun: z2.boolean().optional().default(false)
|
|
2199
2403
|
});
|
|
2404
|
+
var AddDocumentTabSchema = z2.object({
|
|
2405
|
+
documentId: z2.string().min(1, "Document ID is required"),
|
|
2406
|
+
title: z2.string().min(1, "Tab title is required")
|
|
2407
|
+
});
|
|
2408
|
+
var RenameDocumentTabSchema = z2.object({
|
|
2409
|
+
documentId: z2.string().min(1, "Document ID is required"),
|
|
2410
|
+
tabId: z2.string().min(1, "Tab ID is required"),
|
|
2411
|
+
title: z2.string().min(1, "Tab title is required")
|
|
2412
|
+
});
|
|
2413
|
+
var InsertSmartChipSchema = z2.object({
|
|
2414
|
+
documentId: z2.string().min(1, "Document ID is required"),
|
|
2415
|
+
index: z2.number().int().min(1, "Index must be at least 1"),
|
|
2416
|
+
chipType: z2.enum(["person"]),
|
|
2417
|
+
personEmail: z2.string().email("Valid email is required for person chip")
|
|
2418
|
+
});
|
|
2419
|
+
var ReadSmartChipsSchema = z2.object({
|
|
2420
|
+
documentId: z2.string().min(1, "Document ID is required")
|
|
2421
|
+
});
|
|
2200
2422
|
var toolDefinitions2 = [
|
|
2201
2423
|
{
|
|
2202
2424
|
name: "createGoogleDoc",
|
|
@@ -2549,6 +2771,56 @@ var toolDefinitions2 = [
|
|
|
2549
2771
|
},
|
|
2550
2772
|
required: ["documentId"]
|
|
2551
2773
|
}
|
|
2774
|
+
},
|
|
2775
|
+
{
|
|
2776
|
+
name: "addDocumentTab",
|
|
2777
|
+
description: "Add a new tab in a Google Doc",
|
|
2778
|
+
inputSchema: {
|
|
2779
|
+
type: "object",
|
|
2780
|
+
properties: {
|
|
2781
|
+
documentId: { type: "string", description: "Document ID" },
|
|
2782
|
+
title: { type: "string", description: "Tab title" }
|
|
2783
|
+
},
|
|
2784
|
+
required: ["documentId", "title"]
|
|
2785
|
+
}
|
|
2786
|
+
},
|
|
2787
|
+
{
|
|
2788
|
+
name: "renameDocumentTab",
|
|
2789
|
+
description: "Rename an existing Google Doc tab",
|
|
2790
|
+
inputSchema: {
|
|
2791
|
+
type: "object",
|
|
2792
|
+
properties: {
|
|
2793
|
+
documentId: { type: "string", description: "Document ID" },
|
|
2794
|
+
tabId: { type: "string", description: "Tab ID" },
|
|
2795
|
+
title: { type: "string", description: "New tab title" }
|
|
2796
|
+
},
|
|
2797
|
+
required: ["documentId", "tabId", "title"]
|
|
2798
|
+
}
|
|
2799
|
+
},
|
|
2800
|
+
{
|
|
2801
|
+
name: "insertSmartChip",
|
|
2802
|
+
description: "Insert a person smart chip (mention) at a document index. Only person chips are supported by the Docs API; date and file chips are read-only.",
|
|
2803
|
+
inputSchema: {
|
|
2804
|
+
type: "object",
|
|
2805
|
+
properties: {
|
|
2806
|
+
documentId: { type: "string", description: "Document ID" },
|
|
2807
|
+
index: { type: "number", description: "Insertion index (1-based)" },
|
|
2808
|
+
chipType: { type: "string", enum: ["person"], description: "Smart chip type (only 'person' is supported)" },
|
|
2809
|
+
personEmail: { type: "string", description: "Email address for the person mention" }
|
|
2810
|
+
},
|
|
2811
|
+
required: ["documentId", "index", "chipType", "personEmail"]
|
|
2812
|
+
}
|
|
2813
|
+
},
|
|
2814
|
+
{
|
|
2815
|
+
name: "readSmartChips",
|
|
2816
|
+
description: "Read smart chip-like elements (person mentions, rich links, date chips) from the default tab of a document",
|
|
2817
|
+
inputSchema: {
|
|
2818
|
+
type: "object",
|
|
2819
|
+
properties: {
|
|
2820
|
+
documentId: { type: "string", description: "Document ID" }
|
|
2821
|
+
},
|
|
2822
|
+
required: ["documentId"]
|
|
2823
|
+
}
|
|
2552
2824
|
}
|
|
2553
2825
|
];
|
|
2554
2826
|
async function handleTool2(toolName, args, ctx) {
|
|
@@ -3660,6 +3932,65 @@ Image URL: ${imageUrl}` }],
|
|
|
3660
3932
|
`;
|
|
3661
3933
|
return { content: [{ type: "text", text: result }], isError: false };
|
|
3662
3934
|
}
|
|
3935
|
+
case "addDocumentTab": {
|
|
3936
|
+
const validation = AddDocumentTabSchema.safeParse(args);
|
|
3937
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
3938
|
+
const a = validation.data;
|
|
3939
|
+
const docs = ctx.google.docs({ version: "v1", auth: ctx.authClient });
|
|
3940
|
+
await docs.documents.batchUpdate({
|
|
3941
|
+
documentId: a.documentId,
|
|
3942
|
+
// createTab is not yet in the googleapis TypeScript types — cast required
|
|
3943
|
+
requestBody: { requests: [{ createTab: { tabProperties: { title: a.title } } }] }
|
|
3944
|
+
});
|
|
3945
|
+
return { content: [{ type: "text", text: `Requested creation of tab "${a.title}" in document ${a.documentId}.` }], isError: false };
|
|
3946
|
+
}
|
|
3947
|
+
case "renameDocumentTab": {
|
|
3948
|
+
const validation = RenameDocumentTabSchema.safeParse(args);
|
|
3949
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
3950
|
+
const a = validation.data;
|
|
3951
|
+
const docs = ctx.google.docs({ version: "v1", auth: ctx.authClient });
|
|
3952
|
+
await docs.documents.batchUpdate({
|
|
3953
|
+
documentId: a.documentId,
|
|
3954
|
+
// updateTabProperties is not yet in the googleapis TypeScript types — cast required
|
|
3955
|
+
requestBody: { requests: [{ updateTabProperties: { tabId: a.tabId, tabProperties: { title: a.title }, fields: "title" } }] }
|
|
3956
|
+
});
|
|
3957
|
+
return { content: [{ type: "text", text: `Renamed tab ${a.tabId} to "${a.title}".` }], isError: false };
|
|
3958
|
+
}
|
|
3959
|
+
case "insertSmartChip": {
|
|
3960
|
+
const validation = InsertSmartChipSchema.safeParse(args);
|
|
3961
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
3962
|
+
const a = validation.data;
|
|
3963
|
+
const docs = ctx.google.docs({ version: "v1", auth: ctx.authClient });
|
|
3964
|
+
await docs.documents.batchUpdate({
|
|
3965
|
+
documentId: a.documentId,
|
|
3966
|
+
requestBody: {
|
|
3967
|
+
requests: [{
|
|
3968
|
+
insertPerson: {
|
|
3969
|
+
personProperties: { email: a.personEmail },
|
|
3970
|
+
location: { index: a.index }
|
|
3971
|
+
}
|
|
3972
|
+
// insertPerson is not yet in the googleapis TypeScript types — cast required
|
|
3973
|
+
}]
|
|
3974
|
+
}
|
|
3975
|
+
});
|
|
3976
|
+
return { content: [{ type: "text", text: `Inserted person smart chip for ${a.personEmail} at index ${a.index}.` }], isError: false };
|
|
3977
|
+
}
|
|
3978
|
+
case "readSmartChips": {
|
|
3979
|
+
const validation = ReadSmartChipsSchema.safeParse(args);
|
|
3980
|
+
if (!validation.success) return errorResponse(validation.error.errors[0].message);
|
|
3981
|
+
const a = validation.data;
|
|
3982
|
+
const docs = ctx.google.docs({ version: "v1", auth: ctx.authClient });
|
|
3983
|
+
const doc = await docs.documents.get({ documentId: a.documentId });
|
|
3984
|
+
const body = doc.data.body?.content || [];
|
|
3985
|
+
const hits = [];
|
|
3986
|
+
for (const block of body) {
|
|
3987
|
+
for (const el of block?.paragraph?.elements || []) {
|
|
3988
|
+
if (el?.richLink) hits.push(`richLink: ${el.richLink.richLinkProperties?.uri || "unknown"}`);
|
|
3989
|
+
if (el?.person) hits.push(`person: ${el.person.personProperties?.email || "unknown"}`);
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
return { content: [{ type: "text", text: hits.length ? hits.join("\n") : "No smart chips detected (note: only the default tab is scanned)." }], isError: false };
|
|
3993
|
+
}
|
|
3663
3994
|
default:
|
|
3664
3995
|
return null;
|
|
3665
3996
|
}
|
|
@@ -6677,7 +7008,7 @@ var authClient = null;
|
|
|
6677
7008
|
var authenticationPromise = null;
|
|
6678
7009
|
var __filename = fileURLToPath2(import.meta.url);
|
|
6679
7010
|
var __dirname = dirname4(__filename);
|
|
6680
|
-
var packageJsonPath =
|
|
7011
|
+
var packageJsonPath = join4(__dirname, "..", "package.json");
|
|
6681
7012
|
var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
6682
7013
|
var VERSION = packageJson.version;
|
|
6683
7014
|
function log(message, data) {
|