Skip to content
Open
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
44 changes: 38 additions & 6 deletions dist/tools/gdrive_read_file.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { google } from "googleapis";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
export const schema = {
name: "gdrive_read_file",
description: "Read contents of a file from Google Drive",
description: "Read contents of a file from Google Drive. Binary files (PDF, images, etc.) are saved locally and the file path is returned.",
inputSchema: {
type: "object",
properties: {
Expand All @@ -14,13 +17,25 @@ export const schema = {
},
};
const drive = google.drive("v3");
const DOWNLOAD_DIR = path.join(os.tmpdir(), "gdrive-downloads");
export async function readFile(args) {
const result = await readGoogleDriveFile(args.fileId);
if (result.localPath) {
return {
content: [
{
type: "text",
text: `File "${result.name}" (${result.contents.mimeType}) saved to:\n${result.localPath}\n\nUse the Read tool to view its contents.`,
},
],
isError: false,
};
}
return {
content: [
{
type: "text",
text: `Contents of ${result.name}:\n\n${result.contents.text || result.contents.blob}`,
text: `Contents of ${result.name}:\n\n${result.contents.text}`,
},
],
isError: false,
Expand All @@ -31,6 +46,7 @@ async function readGoogleDriveFile(fileId) {
const file = await drive.files.get({
fileId,
fields: "mimeType,name",
supportsAllDrives: true,
});
// For Google Docs/Sheets/etc we need to export
if (file.data.mimeType?.startsWith("application/vnd.google-apps")) {
Expand Down Expand Up @@ -61,17 +77,33 @@ async function readGoogleDriveFile(fileId) {
};
}
// For regular files download content
const res = await drive.files.get({ fileId, alt: "media" }, { responseType: "arraybuffer" });
const res = await drive.files.get({ fileId, alt: "media", supportsAllDrives: true }, { responseType: "arraybuffer" });
const mimeType = file.data.mimeType || "application/octet-stream";
const isText = mimeType.startsWith("text/") || mimeType === "application/json";
const content = Buffer.from(res.data);
// Binary files: save locally and return the path
if (!isText) {
const localPath = saveToLocal(file.data.name || fileId, content);
return {
name: file.data.name || fileId,
contents: { mimeType },
localPath,
};
}
// Text files: return content directly
return {
name: file.data.name || fileId,
contents: {
mimeType,
...(isText
? { text: content.toString("utf-8") }
: { blob: content.toString("base64") }),
text: content.toString("utf-8"),
},
};
}
function saveToLocal(fileName, content) {
if (!fs.existsSync(DOWNLOAD_DIR)) {
fs.mkdirSync(DOWNLOAD_DIR, { recursive: true });
}
const filePath = path.join(DOWNLOAD_DIR, fileName);
fs.writeFileSync(filePath, content);
return filePath;
}
9 changes: 9 additions & 0 deletions dist/tools/gdrive_search.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,17 @@ export async function search(args) {
const drive = google.drive("v3");
const userQuery = args.query.trim();
let searchQuery = "";
// Drive API query operators that indicate a raw query
const driveQueryOperators = [" in ", "mimeType", "modifiedTime", "trashed", "fullText", "sharedWithMe", "owners"];
const isRawQuery = driveQueryOperators.some(op => userQuery.includes(op));
// If query is empty, list all files
if (!userQuery) {
searchQuery = "trashed = false";
}
else if (isRawQuery) {
// Pass through raw Drive API queries directly
searchQuery = userQuery.includes("trashed") ? userQuery : `(${userQuery}) and trashed = false`;
}
else {
// Escape special characters in the query
const escapedQuery = userQuery.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
Expand All @@ -50,6 +57,8 @@ export async function search(args) {
pageToken: args.pageToken,
orderBy: "modifiedTime desc",
fields: "nextPageToken, files(id, name, mimeType, modifiedTime, size)",
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});
const fileList = res.data.files
?.map((file) => `${file.id} ${file.name} (${file.mimeType})`)
Expand Down
50 changes: 43 additions & 7 deletions tools/gdrive_read_file.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { google } from "googleapis";
import { GDriveReadFileInput, InternalToolResponse } from "./types.js";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";

export const schema = {
name: "gdrive_read_file",
description: "Read contents of a file from Google Drive",
description: "Read contents of a file from Google Drive. Binary files (PDF, images, etc.) are saved locally and the file path is returned.",
inputSchema: {
type: "object",
properties: {
Expand All @@ -17,6 +20,7 @@ export const schema = {
} as const;

const drive = google.drive("v3");
const DOWNLOAD_DIR = path.join(os.tmpdir(), "gdrive-downloads");

interface FileContent {
uri?: string;
Expand All @@ -29,11 +33,24 @@ export async function readFile(
args: GDriveReadFileInput,
): Promise<InternalToolResponse> {
const result = await readGoogleDriveFile(args.fileId);

if (result.localPath) {
return {
content: [
{
type: "text",
text: `File "${result.name}" (${result.contents.mimeType}) saved to:\n${result.localPath}\n\nUse the Read tool to view its contents.`,
},
],
isError: false,
};
}

return {
content: [
{
type: "text",
text: `Contents of ${result.name}:\n\n${result.contents.text || result.contents.blob}`,
text: `Contents of ${result.name}:\n\n${result.contents.text}`,
},
],
isError: false,
Expand All @@ -42,11 +59,12 @@ export async function readFile(

async function readGoogleDriveFile(
fileId: string,
): Promise<{ name: string; contents: FileContent }> {
): Promise<{ name: string; contents: FileContent; localPath?: string }> {
// First get file metadata to check mime type
const file = await drive.files.get({
fileId,
fields: "mimeType,name",
supportsAllDrives: true,
});

// For Google Docs/Sheets/etc we need to export
Expand Down Expand Up @@ -85,22 +103,40 @@ async function readGoogleDriveFile(

// For regular files download content
const res = await drive.files.get(
{ fileId, alt: "media" },
{ fileId, alt: "media", supportsAllDrives: true },
{ responseType: "arraybuffer" },
);
const mimeType = file.data.mimeType || "application/octet-stream";
const isText =
mimeType.startsWith("text/") || mimeType === "application/json";
const content = Buffer.from(res.data as ArrayBuffer);

// Binary files: save locally and return the path
if (!isText) {
const localPath = saveToLocal(file.data.name || fileId, content);
return {
name: file.data.name || fileId,
contents: { mimeType },
localPath,
};
}

// Text files: return content directly
return {
name: file.data.name || fileId,
contents: {
mimeType,
...(isText
? { text: content.toString("utf-8") }
: { blob: content.toString("base64") }),
text: content.toString("utf-8"),
},
};
}

function saveToLocal(fileName: string, content: Buffer): string {
if (!fs.existsSync(DOWNLOAD_DIR)) {
fs.mkdirSync(DOWNLOAD_DIR, { recursive: true });
}
const filePath = path.join(DOWNLOAD_DIR, fileName);
fs.writeFileSync(filePath, content);
return filePath;
}

9 changes: 9 additions & 0 deletions tools/gdrive_search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,16 @@ export async function search(
const userQuery = args.query.trim();
let searchQuery = "";

// Drive API query operators that indicate a raw query
const driveQueryOperators = [" in ", "mimeType", "modifiedTime", "trashed", "fullText", "sharedWithMe", "owners"];
const isRawQuery = driveQueryOperators.some(op => userQuery.includes(op));

// If query is empty, list all files
if (!userQuery) {
searchQuery = "trashed = false";
} else if (isRawQuery) {
// Pass through raw Drive API queries directly
searchQuery = userQuery.includes("trashed") ? userQuery : `(${userQuery}) and trashed = false`;
} else {
// Escape special characters in the query
const escapedQuery = userQuery.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
Expand All @@ -60,6 +67,8 @@ export async function search(
pageToken: args.pageToken,
orderBy: "modifiedTime desc",
fields: "nextPageToken, files(id, name, mimeType, modifiedTime, size)",
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});

const fileList = res.data.files
Expand Down