Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

MCP-server voor Google Drive

Bouw een MCP-server die bestanden in Google Drive kan doorzoeken, lezen, aanmaken en organiseren, klaar voor gebruik als RAG-bron voor je AI-assistent.

Wat je bouwt

Een MCP-server die je AI-assistent toegang geeft tot Google Drive: bestanden zoeken op naam of inhoud, documenten lezen, nieuwe bestanden aanmaken en mappen beheren. Dat is bijzonder nuttig voor RAG-achtige workflows waarbij de kennisbank in Drive staat en je niet voor elk project een aparte indexeringspijplijn wilt bouwen.

In dit artikel gebruiken we de officiele MCP TypeScript SDK met de aanbevolen registerTool- en registerResource-methoden, samen met de Google Drive v3 API via het googleapis-pakket.

info

Vereisten

Je hebt een Google Cloud-project nodig met de Drive API ingeschakeld, een OAuth-client en een geldige OAuth2Client met de juiste scopes (bijvoorbeeld drive.readonly om te lezen of drive voor lezen en schrijven). Geef altijd de smalste scope die je workflow echt nodig heeft.

Setup

npm install @modelcontextprotocol/sdk googleapis google-auth-library zod

Drive-client initialiseren

import { google, drive_v3 } from "googleapis";
import { OAuth2Client } from "google-auth-library";

function getDriveClient(auth: OAuth2Client): drive_v3.Drive {
  return google.drive({ version: "v3", auth });
}

Bestanden zoeken

In de nieuwere SDK geef je naam, metadata en het Zod-schema als één configuratie-object mee aan registerTool. De oudere server.tool(...)-vorm werkt nog voor bestaande code, maar registerTool is de aanbevolen aanpak voor nieuwe servers.

server.registerTool(
  "search_drive",
  {
    title: "Drive doorzoeken",
    description:
      "Zoek bestanden in Google Drive op naam, type of inhoud. Geeft een lijst van gevonden bestanden met ID, naam en type.",
    inputSchema: z.object({
      query: z
        .string()
        .describe("Zoekopdracht, bijvoorbeeld 'rapport 2025' of een trefwoord"),
      max_results: z.number().int().min(1).max(50).default(10),
      folder_id: z
        .string()
        .optional()
        .describe("Beperk de zoekopdracht tot een map"),
    }),
  },
  async ({ query, max_results, folder_id }) => {
    const safe = query.replace(/['\\]/g, "\\$&");
    let q = `name contains '${safe}' and trashed=false`;
    if (folder_id) q += ` and '${folder_id}' in parents`;

    const res = await drive.files.list({
      q,
      pageSize: max_results,
      fields: "files(id,name,mimeType,modifiedTime,size,webViewLink)",
    });
    return {
      content: [{ type: "text", text: JSON.stringify(res.data.files) }],
    };
  }
);
warning

Escape de zoekterm correct

De Drive-query is een string. Zowel een enkele quote als een backslash in de gebruikersinvoer kan de query breken of misbruikt worden. Escape daarom beide tekens (zie de replace hierboven) voordat je de waarde in de q-parameter plakt.

Bestand lezen

Google Docs, Sheets en Slides zijn geen binaire bestanden maar worden via de export-API omgezet naar een leesbaar formaat.

const EXPORT_FORMATS: Record<string, string> = {
  "application/vnd.google-apps.document": "text/plain",
  "application/vnd.google-apps.spreadsheet": "text/csv",
  "application/vnd.google-apps.presentation": "text/plain",
};

server.registerTool(
  "read_file",
  {
    title: "Bestand lezen",
    description:
      "Lees de inhoud van een bestand in Google Drive. Geeft platte tekst van documenten, CSV van spreadsheets of de binaire inhoud als base64.",
    inputSchema: z.object({
      file_id: z.string().describe("Het Drive-bestand-ID"),
    }),
  },
  async ({ file_id }) => {
    const meta = await drive.files.get({
      fileId: file_id,
      fields: "id,name,mimeType",
    });
    const mimeType = meta.data.mimeType ?? "";
    const exportMime = EXPORT_FORMATS[mimeType];

    if (exportMime) {
      const res = await drive.files.export(
        { fileId: file_id, mimeType: exportMime },
        { responseType: "text" }
      );
      return {
        content: [{ type: "text", text: res.data as string }],
      };
    }

    const res = await drive.files.get(
      { fileId: file_id, alt: "media" },
      { responseType: "arraybuffer" }
    );
    return {
      content: [
        {
          type: "resource",
          resource: {
            uri: `drive://${file_id}`,
            blob: Buffer.from(res.data as ArrayBuffer).toString("base64"),
            mimeType,
          },
        },
      ],
    };
  }
);

Document aanmaken

server.registerTool(
  "create_document",
  {
    title: "Document aanmaken",
    description: "Maak een nieuw Google Docs-document aan met opgegeven inhoud.",
    inputSchema: z.object({
      title: z.string(),
      content: z
        .string()
        .describe("Wordt als platte tekst in het nieuwe document opgeslagen"),
      folder_id: z.string().optional(),
    }),
  },
  async ({ title, content, folder_id }) => {
    const fileMetadata: drive_v3.Schema$File = {
      name: title,
      mimeType: "application/vnd.google-apps.document",
      parents: folder_id ? [folder_id] : undefined,
    };
    const media = { mimeType: "text/plain", body: content };
    const res = await drive.files.create({
      requestBody: fileMetadata,
      media,
      fields: "id,webViewLink",
    });
    return {
      content: [
        {
          type: "text",
          text: `Document aangemaakt: ${res.data.webViewLink}`,
        },
      ],
    };
  }
);

Resources voor Drive-bestanden

Naast tools kun je bestanden als MCP-resources aanbieden. Zo kan de assistent een document rechtstreeks als context laden via een drive://{fileId}-URI.

import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";

server.registerResource(
  "drive-file",
  new ResourceTemplate("drive://{fileId}", { list: undefined }),
  {
    title: "Drive-bestand",
    description: "Inhoud van een Google Drive-bestand",
    mimeType: "text/plain",
  },
  async (uri, { fileId }) => {
    const meta = await drive.files.get({
      fileId,
      fields: "id,name,mimeType",
    });
    const exportMime = EXPORT_FORMATS[meta.data.mimeType ?? ""] ?? "text/plain";
    const res = await drive.files.export(
      { fileId, mimeType: exportMime },
      { responseType: "text" }
    );
    return {
      contents: [
        {
          uri: uri.href,
          text: res.data as string,
          mimeType: exportMime,
        },
      ],
    };
  }
);
lightbulb

Drive als RAG-bron

Door Drive-bestanden als MCP-resources te registreren, kan je AI-assistent documenten laden als context voor complexe vragen, zonder dat je een aparte RAG-pijplijn hoeft te bouwen voor intern gebruik. Combineer dit met search_drive zodat de assistent eerst het juiste bestand vindt en het daarna als resource ophaalt.

Mappen beheren

server.registerTool(
  "list_folder_contents",
  {
    title: "Mapinhoud opvragen",
    description: "Geef de inhoud van een Drive-map terug.",
    inputSchema: z.object({
      folder_id: z
        .string()
        .describe("Map-ID, of 'root' voor de hoofdmap"),
    }),
  },
  async ({ folder_id }) => {
    const res = await drive.files.list({
      q: `'${folder_id}' in parents and trashed=false`,
      fields: "files(id,name,mimeType,modifiedTime)",
      orderBy: "name",
    });
    return {
      content: [{ type: "text", text: JSON.stringify(res.data.files) }],
    };
  }
);

Veilig houden

Een server met schrijfrechten op Drive is krachtig, dus bouw een paar grenzen in.

warning

Beperk de blast radius

Geef de OAuth-client de smalst mogelijke scope, log elke schrijf- of verwijderactie en overweeg een allowlist van map-ID's waarbinnen de server mag werken. Zo voorkom je dat een verkeerd geinterpreteerde prompt per ongeluk de verkeerde bestanden raakt.

Hoe zoek ik op bestandsinhoud in plaats van naam?

Gebruik fullText contains 'zoekterm' in de query in plaats van name contains. Dit doorzoekt ook de inhoud van Google Docs, niet alleen de bestandsnaam.

Kan ik ook bestanden delen via de API?

Ja, via drive.permissions.create. Voeg een aparte tool toe met een role-parameter (reader, writer of commenter) en een emailAddress. Beperk dit tot vertrouwde workflows, want delen is moeilijk terug te draaien.

Hoe ga ik om met grote bestanden?

Exporteer grote documenten in stukken of gebruik de streaming-opties van de Drive API. Houd losse tool-responses klein, in de orde van enkele tientallen kilobytes tekst, zodat je het contextvenster van het model niet overbelast.

Kan ik ook in Shared Drives zoeken?

Ja, voeg supportsAllDrives: true en includeItemsFromAllDrives: true toe aan de API-aanroep, zodat ook gedeelde drives in de resultaten meekomen.

Werkt server.tool nog of moet ik registerTool gebruiken?

Beide werken. De oudere server.tool(...)- en server.resource(...)-vormen blijven beschikbaar voor compatibiliteit, maar registerTool en registerResource zijn de aanbevolen methoden voor nieuwe servers en houden naam, metadata en schema netjes bij elkaar.