Skip to main content
Glama
update-note.ts4.95 kB
import type { OAuth2Client } from "google-auth-library"; import { google } from "googleapis"; import { z } from "zod"; export const tool = { name: "gdrive_update_doc_note", description: "Update the text of an existing note in a Google Doc by its note number. The note number and formatting (📝 NOTE #:) remain the same, only the text content is updated.", inputSchema: z.object({ doc_id_or_url: z .string() .describe( "Google Docs URL (e.g., https://docs.google.com/document/d/...) or document ID", ), note_number: z .number() .describe("The note number to update (e.g., 1 for 📝 NOTE 1:)"), new_text: z .string() .describe("The new note text (without the '📝 NOTE #:' prefix)"), }), annotations: { readOnlyHint: false, idempotentHint: true, destructiveHint: false, }, } as const; export const handler = async ( args: z.infer<typeof tool.inputSchema>, auth: OAuth2Client, ) => { const { doc_id_or_url, note_number, new_text } = args; try { const result = await updateNote(auth, doc_id_or_url, note_number, new_text); return { content: [ { type: "text" as const, text: result, }, ], }; } catch (error: unknown) { if (error instanceof Error) { return { content: [ { type: "text" as const, text: `Error: ${error.message}`, }, ], isError: true, }; } throw error; } }; const extractDocId = (input: string): string | null => { // Try to extract document ID from URL const urlMatch = input.match(/\/document\/d\/([a-zA-Z0-9-_]+)/); if (urlMatch) { return urlMatch[1] ?? null; } // If it's not a URL, assume it's already a document ID if (/^[a-zA-Z0-9-_]+$/.test(input)) { return input; } return null; }; const updateNote = async ( auth: OAuth2Client, docIdOrUrl: string, noteNumber: number, newNoteText: string, ): Promise<string> => { const docId = extractDocId(docIdOrUrl); if (!docId) { throw new Error("Invalid document ID or URL"); } const docs = google.docs({ version: "v1", auth }); try { // Get the document to find the note const doc = await docs.documents.get({ documentId: docId, }); if (!doc.data.body?.content) { throw new Error("Document has no content"); } // Find the note by number let noteStartIndex: number | null = null; let noteEndIndex: number | null = null; let oldNoteText = ""; for (const element of doc.data.body.content) { if (element.paragraph) { // Concatenate all text runs in the paragraph to handle notes split across runs let paragraphText = ""; const paragraphStartIndex = element.startIndex ?? 0; for (const textElement of element.paragraph.elements || []) { paragraphText += textElement.textRun?.content || ""; } const pattern = new RegExp( `📝 NOTE ${noteNumber}: (.+?)(?=\\n|$)`, "s", ); const match = paragraphText.match(pattern); if (match) { const notePrefix = `📝 NOTE ${noteNumber}: `; const notePrefixIndex = paragraphText.indexOf(notePrefix); noteStartIndex = paragraphStartIndex + notePrefixIndex + notePrefix.length; oldNoteText = match[1] || ""; // Find the end of the note text (before the newline) const noteTextStart = notePrefixIndex + notePrefix.length; const restOfText = paragraphText.substring(noteTextStart); const endOfNote = restOfText.indexOf("\n"); if (endOfNote !== -1) { noteEndIndex = paragraphStartIndex + noteTextStart + endOfNote; } else { noteEndIndex = paragraphStartIndex + noteTextStart + restOfText.length; } break; } } } if (noteStartIndex === null || noteEndIndex === null) { throw new Error( `Could not find NOTE ${noteNumber} in the document. Use list_doc_notes to see all notes.`, ); } // Delete old note text and insert new one const requests = [ { deleteContentRange: { range: { startIndex: noteStartIndex, endIndex: noteEndIndex, }, }, }, { insertText: { location: { index: noteStartIndex, }, text: newNoteText, }, }, ]; await docs.documents.batchUpdate({ documentId: docId, requestBody: { requests, }, }); return `Note ${noteNumber} updated successfully!\n\nOld text: ${oldNoteText}\nNew text: ${newNoteText}`; } catch (error: unknown) { if (error instanceof Error) { throw new Error(`Failed to update note: ${error.message}`); } throw error; } };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/benjamine/gdrive-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server