import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import { google } from "googleapis";
// Initialize Google Drive API
// For simplicity, we expect GOOGLE_APPLICATION_CREDENTIALS to be set
// or we can use default auth which looks for it.
const auth = new google.auth.GoogleAuth({
scopes: ["https://www.googleapis.com/auth/drive.readonly"],
});
const drive = google.drive({ version: "v3", auth });
/**
* Create an MCP server with capabilities to list and read Google Drive files.
*/
const server = new Server({
name: "google-drive-mcp-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
/**
* Handler that lists available tools.
* Exposes tools for listing files and reading file metadata.
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "list_files",
description: "List files from Google Drive",
inputSchema: {
type: "object",
properties: {
pageSize: {
type: "number",
description: "Number of files to return (max 100)",
default: 10,
},
q: {
type: "string",
description: "Google Drive search query (e.g., \"name contains 'Meeting'\")",
},
},
},
},
{
name: "get_file_info",
description: "Get detailed information about a specific file by ID",
inputSchema: {
type: "object",
properties: {
fileId: {
type: "string",
description: "The ID of the file",
},
},
required: ["fileId"],
},
},
],
};
});
/**
* Handler for the tools.
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case "list_files": {
const pageSize = Number(request.params.arguments?.pageSize) || 10;
const q = String(request.params.arguments?.q || "");
try {
const response = await drive.files.list({
pageSize,
fields: "nextPageToken, files(id, name, mimeType)",
q,
});
const files = response.data.files || [];
const result = files.map(f => `${f.name} (ID: ${f.id}, Type: ${f.mimeType})`).join("\n");
return {
content: [
{
type: "text",
text: result || "No files found.",
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error listing files: ${error.message}`,
},
],
isError: true,
};
}
}
case "get_file_info": {
const fileId = String(request.params.arguments?.fileId);
try {
const response = await drive.files.get({
fileId,
fields: "*",
});
return {
content: [
{
type: "text",
text: JSON.stringify(response.data, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error getting file info: ${error.message}`,
},
],
isError: true,
};
}
}
default:
throw new Error("Unknown tool");
}
});
/**
* Start the server using stdio transport.
* This allows Claude Desktop to communicate with the server via stdin/stdout.
*/
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});