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
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,41 @@ npm run build

## 🔧 Usage

### Output Format

The server supports two output formats controlled by the `OUTPUT_FORMAT` environment variable:

- **`text`** (default): Returns human-readable text responses
- **`json`**: Returns structured JSON responses for programmatic consumption

When `OUTPUT_FORMAT=json`:
- `gdrive_search` returns a JSON object with a `files` array containing file objects with `id`, `name`, `mimeType`, `modifiedTime`, and `size` fields
- `gdrive_read_file` returns a JSON object with `fileId`, `mimeType`, and `content` fields

Example JSON output from `gdrive_search`:
```json
{
"files": [
{
"id": "mock_spreadsheet_123",
"name": "Rent Value Update Report",
"mimeType": "application/vnd.google-apps.spreadsheet",
"modifiedTime": "2024-01-01T00:00:00.000Z",
"size": "1024"
}
],
"total": 1
}
```

### As a Command Line Tool

```bash
# Start the server
# Start the server with default text output
node dist/index.js

# Start the server with JSON output
OUTPUT_FORMAT=json node dist/index.js
```

### Integration with Desktop App
Expand All @@ -155,7 +185,8 @@ Add this configuration to your app's server settings:
"args": ["path/to/gdrive-mcp-server/dist/index.js"],
"env": {
"GOOGLE_APPLICATION_CREDENTIALS": "path/to/gdrive-mcp-server/credentials/gcp-oauth.keys.json",
"MCP_GDRIVE_CREDENTIALS": "path/to/gdrive-mcp-server/credentials/.gdrive-server-credentials.json"
"MCP_GDRIVE_CREDENTIALS": "path/to/gdrive-mcp-server/credentials/.gdrive-server-credentials.json",
"OUTPUT_FORMAT": "json"
}
}
}
Expand Down
124 changes: 86 additions & 38 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,36 @@ import path from "path";

const drive = google.drive("v3");

// Get output format from environment variable (defaults to 'text')
const OUTPUT_FORMAT = process.env.OUTPUT_FORMAT || 'text';

// Helper function to check if JSON output is enabled
function isJsonOutput(): boolean {
return OUTPUT_FORMAT === 'json';
}

// Helper function to create MCP tool response
function createResponse(data: string | object, isError: boolean = false) {
let text: string;

// If data is an object and JSON output is enabled, stringify it
if (typeof data === 'object' && isJsonOutput()) {
text = JSON.stringify(data, null, 2);
} else {
text = String(data);
}

return {
content: [
{
type: "text",
text: text,
},
],
isError: isError,
};
}

const server = new Server(
{
name: "gdrive",
Expand Down Expand Up @@ -181,18 +211,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
fields: "files(id, name, mimeType, modifiedTime, size)",
});

const fileList = res.data.files
?.map((file: any) => `${file.name} (${file.mimeType}) - ID: ${file.id}`)
const files = res.data.files || [];

// Return JSON format if OUTPUT_FORMAT=json
if (isJsonOutput()) {
const jsonResponse = {
files: files.map((file: any) => ({
id: file.id,
name: file.name,
mimeType: file.mimeType,
modifiedTime: file.modifiedTime,
size: file.size,
})),
total: files.length,
};
return createResponse(jsonResponse, false);
}

// Return text format (default)
const fileList = files
.map((file: any) => `${file.name} (${file.mimeType}) - ID: ${file.id}`)
.join("\n");
return {
content: [
{
type: "text",
text: `Found ${res.data.files?.length ?? 0} files:\n${fileList}`,
},
],
isError: false,
};
return createResponse(`Found ${files.length} files:\n${fileList}`, false);
} catch (error: any) {
// Extract more detailed error information from Google API errors
let errorMessage = error.message || error.toString();
Expand Down Expand Up @@ -235,15 +275,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
};
process.stderr.write('Google Drive API error: ' + JSON.stringify(errorLog, null, 2) + '\n');

return {
content: [
{
type: "text",
text: `Error searching Google Drive: ${errorMessage}${errorCodeSuffix}`,
},
],
isError: true,
};
// Return JSON format for errors if OUTPUT_FORMAT=json
if (isJsonOutput()) {
const errorResponse = {
error: true,
message: errorMessage,
code: error.code || error.response?.data?.error?.code || 'unknown',
};
return createResponse(errorResponse, true);
}

return createResponse(`Error searching Google Drive: ${errorMessage}${errorCodeSuffix}`, true);
}
} else if (request.params.name === "gdrive_read_file") {
const fileId = request.params.arguments?.file_id as string;
Expand All @@ -253,25 +295,31 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {

try {
const result = await readFileContent(fileId);
return {
content: [
{
type: "text",
text: result.content,
},
],
isError: false,
};

// Return JSON format if OUTPUT_FORMAT=json
if (isJsonOutput()) {
const jsonResponse = {
fileId: fileId,
mimeType: result.mimeType,
content: result.content,
};
return createResponse(jsonResponse, false);
}

// Return text format (default)
return createResponse(result.content as string, false);
} catch (error: any) {
return {
content: [
{
type: "text",
text: `Error reading file: ${error.message}`,
},
],
isError: true,
};
// Return JSON format for errors if OUTPUT_FORMAT=json
if (isJsonOutput()) {
const errorResponse = {
error: true,
message: error.message || error.toString(),
fileId: fileId,
};
return createResponse(errorResponse, true);
}

return createResponse(`Error reading file: ${error.message}`, true);
}
}
throw new Error("Tool not found");
Expand Down