diff --git a/src/gdrive/README.md b/src/gdrive/README.md index b9d48738..5fd0e0dc 100644 --- a/src/gdrive/README.md +++ b/src/gdrive/README.md @@ -2,15 +2,37 @@ This MCP server integrates with Google Drive to allow listing, reading, and searching over files. +## Components + +### Tools + +- **search** + - Search for files in Google Drive + - Input: `query` (string): Search query + - Returns file names and MIME types of matching files + +### Resources + +The server provides access to Google Drive files: + +- **Files** (`gdrive:///`) + - Supports all file types + - Google Workspace files are automatically exported: + - Docs → Markdown + - Sheets → CSV + - Presentations → Plain text + - Drawings → PNG + - Other files are provided in their native format + ## Getting started -1. Create a new Google Cloud project -2. Enable the Google Drive API -3. Configure an OAuth consent screen ("internal" is fine for testing) +1. [Create a new Google Cloud project](https://console.cloud.google.com/projectcreate) +2. [Enable the Google Drive API](https://console.cloud.google.com/workspace-api/products) +3. [Configure an OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) ("internal" is fine for testing) 4. Add OAuth scope `https://www.googleapis.com/auth/drive.readonly` -5. Create an OAuth Client ID for application type "Desktop App" +5. [Create an OAuth Client ID](https://console.cloud.google.com/apis/credentials/oauthclient) for application type "Desktop App" 6. Download the JSON file of your client's OAuth keys -7. Rename the key file to `gcp-oauth.keys.json` and place into the root of this repo +7. Rename the key file to `gcp-oauth.keys.json` and place into the root of this repo (i.e. `servers/gcp-oauth.keys.json`) Make sure to build the server with either `npm run build` or `npm run watch`. @@ -18,16 +40,18 @@ Make sure to build the server with either `npm run build` or `npm run watch`. To authenticate and save credentials: -1. Run the server with the `auth` argument: `node build/gdrive auth` +1. Run the server with the `auth` argument: `node ./dist auth` 2. This will open an authentication flow in your system browser 3. Complete the authentication process -4. Credentials will be saved for future use - -### Running the server +4. Credentials will be saved in the root of this repo (i.e. `servers/.gdrive-server-credentials.json`) -After authenticating: +### Usage with Desktop App -1. Run the server normally: `node build/gdrive` -2. The server will load the saved credentials and start +To integrate this server with the desktop app, add the following to your app's server configuration: -Note: If you haven't authenticated yet, the server will prompt you to run with the `auth` argument first. +```json +{ + "mcp-server-gdrive": { + "command": "mcp-server-gdrive" + } +} \ No newline at end of file diff --git a/src/gdrive/index.ts b/src/gdrive/index.ts index 1a8ac82e..b9c6d910 100644 --- a/src/gdrive/index.ts +++ b/src/gdrive/index.ts @@ -44,7 +44,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async (request) => { return { resources: files.map((file) => ({ - uri: `gdrive://${file.id}`, + uri: `gdrive:///${file.id}`, mimeType: file.mimeType, name: file.name, })), @@ -53,7 +53,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async (request) => { }); server.setRequestHandler(ReadResourceRequestSchema, async (request) => { - const fileId = request.params.uri.replace("gdrive://", ""); + const fileId = request.params.uri.replace("gdrive:///", ""); // First get file metadata to check mime type const file = await drive.files.get({ @@ -149,14 +149,16 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "search") { - const query = request.params.arguments?.query as string; - + const userQuery = request.params.arguments?.query as string; + const escapedQuery = userQuery.replace(/\\/g, "\\\\").replace(/'/g, "\\'"); + const formattedQuery = `fullText contains '${escapedQuery}'`; + const res = await drive.files.list({ - q: query, + q: formattedQuery, pageSize: 10, fields: "files(id, name, mimeType, modifiedTime, size)", }); - + const fileList = res.data.files ?.map((file: any) => `${file.name} (${file.mimeType})`) .join("\n"); @@ -175,7 +177,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const credentialsPath = path.join( path.dirname(new URL(import.meta.url).pathname), - "../../.gdrive-server-credentials.json", + "../../../.gdrive-server-credentials.json", ); async function authenticateAndSaveCredentials() { @@ -183,7 +185,7 @@ async function authenticateAndSaveCredentials() { const auth = await authenticate({ keyfilePath: path.join( path.dirname(new URL(import.meta.url).pathname), - "../../gcp-oauth.keys.json", + "../../../gcp-oauth.keys.json", ), scopes: ["https://www.googleapis.com/auth/drive.readonly"], });