Skip to main content
Glama

Google Docs & Drive MCP Server

by oregpt
test-integration.ts•18.9 kB
/** * Integration Tests for GoogleDocsMCP - AgenticLedger Platform * * This test suite performs REAL API calls to verify all MCP tools work correctly. * It follows the AgenticLedger platform integration guidelines. * * Requirements: * - Google Cloud Project with Docs API + Drive API enabled * - OAuth 2.0 credentials configured (credentials.json) * - Valid token.json (run server once to authorize) * - .env file with TEST_DOCUMENT_ID (optional - will create if missing) */ import { google } from 'googleapis'; import { OAuth2Client } from 'google-auth-library'; import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; import { authorize } from './dist/auth.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Color codes for terminal output const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', }; interface TestResult { tool: string; status: 'PASS' | 'FAIL' | 'SKIP'; duration: number; request?: any; response?: any; error?: string; } const testResults: TestResult[] = []; // Helper functions function log(message: string, color = colors.reset) { console.log(`${color}${message}${colors.reset}`); } function logSection(title: string) { console.log('\n' + '='.repeat(80)); log(title, colors.bright + colors.cyan); console.log('='.repeat(80) + '\n'); } function logTest(testName: string) { log(`\nšŸ“‹ Testing: ${testName}`, colors.blue); } function logSuccess(message: string) { log(`āœ… ${message}`, colors.green); } function logError(message: string) { log(`āŒ ${message}`, colors.red); } function logWarning(message: string) { log(`āš ļø ${message}`, colors.yellow); } // Initialize Google API clients async function initializeClients() { try { const authClient = await authorize(); const docs = google.docs({ version: 'v1', auth: authClient }); const drive = google.drive({ version: 'v3', auth: authClient }); return { docs, drive, auth: authClient }; } catch (error: any) { logError('Failed to initialize Google API clients'); logError(error.message); throw error; } } // Create a test document async function createTestDocument(drive: any): Promise<string> { logTest('Creating test document'); try { const response = await drive.files.create({ requestBody: { name: `AgenticLedger Test Doc ${new Date().toISOString()}`, mimeType: 'application/vnd.google-apps.document', }, fields: 'id,name', }); const documentId = response.data.id; logSuccess(`Test document created: ${documentId}`); log(` Name: ${response.data.name}`, colors.cyan); return documentId; } catch (error: any) { logError(`Failed to create test document: ${error.message}`); throw error; } } // Test suite async function runTests() { logSection('GoogleDocsMCP Integration Tests - AgenticLedger Platform'); const startTime = Date.now(); const { docs, drive, auth } = await initializeClients(); log(`Authenticated as: ${(auth as OAuth2Client).credentials}`, colors.cyan); // Create or use existing test document let testDocumentId: string; if (process.env.TEST_DOCUMENT_ID) { testDocumentId = process.env.TEST_DOCUMENT_ID; log(`Using existing test document: ${testDocumentId}`, colors.cyan); } else { testDocumentId = await createTestDocument(drive); } // Test 1: readGoogleDoc await testReadDocument(docs, testDocumentId); // Test 2: appendToGoogleDoc await testAppendToDocument(docs, testDocumentId); // Test 3: insertText await testInsertText(docs, testDocumentId); // Test 4: deleteRange await testDeleteRange(docs, testDocumentId); // Test 5: applyTextStyle await testApplyTextStyle(docs, testDocumentId); // Test 6: applyParagraphStyle await testApplyParagraphStyle(docs, testDocumentId); // Test 7: insertTable await testInsertTable(docs, testDocumentId); // Test 8: insertPageBreak await testInsertPageBreak(docs, testDocumentId); // Test 9: listDocumentTabs await testListDocumentTabs(docs, testDocumentId); // Test 10: listGoogleDocs (Drive API) await testListDocuments(drive); // Test 11: getDocumentInfo (Drive API) await testGetDocumentInfo(drive, testDocumentId); // Test 12: createDocument (Drive API) await testCreateDocument(drive); // Print summary printSummary(startTime, testDocumentId); } async function testReadDocument(docs: any, documentId: string) { logTest('readGoogleDoc'); const startTime = Date.now(); try { const request = { documentId, fields: '*' }; const response = await docs.documents.get(request); const duration = Date.now() - startTime; const result: TestResult = { tool: 'readGoogleDoc', status: 'PASS', duration, request, response: { title: response.data.title, revisionId: response.data.revisionId, documentId: response.data.documentId, }, }; testResults.push(result); logSuccess(`Document read (${duration}ms)`); log(` Title: ${response.data.title}`, colors.cyan); log(` Revision: ${response.data.revisionId}`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'readGoogleDoc', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testAppendToDocument(docs: any, documentId: string) { logTest('appendToGoogleDoc'); const startTime = Date.now(); try { // Get current end index const doc = await docs.documents.get({ documentId, fields: 'body' }); const endIndex = doc.data.body.content[doc.data.body.content.length - 1].endIndex - 1; const textToAppend = `\n\nTest appended text at ${new Date().toISOString()}`; const request = { documentId, requests: [{ insertText: { location: { index: endIndex }, text: textToAppend, }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'appendToGoogleDoc', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Text appended (${duration}ms)`); log(` Text: "${textToAppend.substring(0, 50)}..."`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'appendToGoogleDoc', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testInsertText(docs: any, documentId: string) { logTest('insertText'); const startTime = Date.now(); try { const textToInsert = `\nInserted text at ${new Date().toISOString()}\n`; const request = { documentId, requests: [{ insertText: { location: { index: 1 }, text: textToInsert, }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'insertText', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Text inserted (${duration}ms)`); log(` Text: "${textToInsert.substring(0, 50)}..."`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'insertText', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testDeleteRange(docs: any, documentId: string) { logTest('deleteRange'); const startTime = Date.now(); try { // Delete a small range (index 10-15) const request = { documentId, requests: [{ deleteContentRange: { range: { startIndex: 10, endIndex: 15, }, }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'deleteRange', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Range deleted (${duration}ms)`); log(` Range: 10-15`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'deleteRange', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testApplyTextStyle(docs: any, documentId: string) { logTest('applyTextStyle'); const startTime = Date.now(); try { // Apply bold and red color to a range const request = { documentId, requests: [{ updateTextStyle: { range: { startIndex: 1, endIndex: 20, }, textStyle: { bold: true, foregroundColor: { color: { rgbColor: { red: 1, green: 0, blue: 0 }, }, }, }, fields: 'bold,foregroundColor', }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'applyTextStyle', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Text style applied (${duration}ms)`); log(` Style: bold + red color`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'applyTextStyle', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testApplyParagraphStyle(docs: any, documentId: string) { logTest('applyParagraphStyle'); const startTime = Date.now(); try { // Apply center alignment to a paragraph const request = { documentId, requests: [{ updateParagraphStyle: { range: { startIndex: 1, endIndex: 50, }, paragraphStyle: { alignment: 'CENTER', }, fields: 'alignment', }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'applyParagraphStyle', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Paragraph style applied (${duration}ms)`); log(` Style: center alignment`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'applyParagraphStyle', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testInsertTable(docs: any, documentId: string) { logTest('insertTable'); const startTime = Date.now(); try { // Get end index const doc = await docs.documents.get({ documentId, fields: 'body' }); const endIndex = doc.data.body.content[doc.data.body.content.length - 1].endIndex - 1; const request = { documentId, requests: [{ insertTable: { location: { index: endIndex }, rows: 3, columns: 3, }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'insertTable', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Table inserted (${duration}ms)`); log(` Size: 3x3`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'insertTable', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testInsertPageBreak(docs: any, documentId: string) { logTest('insertPageBreak'); const startTime = Date.now(); try { // Get end index const doc = await docs.documents.get({ documentId, fields: 'body' }); const endIndex = doc.data.body.content[doc.data.body.content.length - 1].endIndex - 1; const request = { documentId, requests: [{ insertPageBreak: { location: { index: endIndex }, }, }], }; const response = await docs.documents.batchUpdate(request); const duration = Date.now() - startTime; testResults.push({ tool: 'insertPageBreak', status: 'PASS', duration, request, response: { documentId: response.data.documentId, }, }); logSuccess(`Page break inserted (${duration}ms)`); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'insertPageBreak', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testListDocumentTabs(docs: any, documentId: string) { logTest('listDocumentTabs'); const startTime = Date.now(); try { const request = { documentId, fields: 'tabs' }; const response = await docs.documents.get(request); const duration = Date.now() - startTime; testResults.push({ tool: 'listDocumentTabs', status: 'PASS', duration, request, response: { tabCount: response.data.tabs?.length || 0, }, }); logSuccess(`Tabs listed (${duration}ms)`); log(` Tab count: ${response.data.tabs?.length || 0}`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'listDocumentTabs', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testListDocuments(drive: any) { logTest('listGoogleDocs'); const startTime = Date.now(); try { const request = { q: "mimeType='application/vnd.google-apps.document'", fields: 'files(id,name,createdTime,modifiedTime)', pageSize: 10, }; const response = await drive.files.list(request); const duration = Date.now() - startTime; testResults.push({ tool: 'listGoogleDocs', status: 'PASS', duration, request, response: { documentCount: response.data.files?.length || 0, }, }); logSuccess(`Documents listed (${duration}ms)`); log(` Document count: ${response.data.files?.length || 0}`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'listGoogleDocs', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testGetDocumentInfo(drive: any, documentId: string) { logTest('getDocumentInfo'); const startTime = Date.now(); try { const request = { fileId: documentId, fields: 'id,name,mimeType,createdTime,modifiedTime,owners,permissions', }; const response = await drive.files.get(request); const duration = Date.now() - startTime; testResults.push({ tool: 'getDocumentInfo', status: 'PASS', duration, request, response: { name: response.data.name, createdTime: response.data.createdTime, }, }); logSuccess(`Document info retrieved (${duration}ms)`); log(` Name: ${response.data.name}`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'getDocumentInfo', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } async function testCreateDocument(drive: any) { logTest('createDocument'); const startTime = Date.now(); try { const request = { requestBody: { name: `Test Doc ${Date.now()}`, mimeType: 'application/vnd.google-apps.document', }, fields: 'id,name', }; const response = await drive.files.create(request); const duration = Date.now() - startTime; testResults.push({ tool: 'createDocument', status: 'PASS', duration, request, response: { id: response.data.id, name: response.data.name, }, }); logSuccess(`Document created (${duration}ms)`); log(` ID: ${response.data.id}`, colors.cyan); log(` Name: ${response.data.name}`, colors.cyan); // Clean up - delete the created document await drive.files.delete({ fileId: response.data.id }); log(` Cleaned up test document`, colors.cyan); } catch (error: any) { const duration = Date.now() - startTime; testResults.push({ tool: 'createDocument', status: 'FAIL', duration, error: error.message, }); logError(`Failed: ${error.message}`); } } function printSummary(startTime: number, testDocumentId: string) { const totalDuration = Date.now() - startTime; logSection('Test Summary'); const passedTests = testResults.filter(r => r.status === 'PASS'); const failedTests = testResults.filter(r => r.status === 'FAIL'); const skippedTests = testResults.filter(r => r.status === 'SKIP'); log(`Total Tests: ${testResults.length}`, colors.bright); logSuccess(`Passed: ${passedTests.length}`); if (failedTests.length > 0) { logError(`Failed: ${failedTests.length}`); } if (skippedTests.length > 0) { logWarning(`Skipped: ${skippedTests.length}`); } log(`\nTotal Duration: ${totalDuration}ms`, colors.cyan); log(`\nTest Document: https://docs.google.com/document/d/${testDocumentId}/edit`, colors.cyan); if (failedTests.length > 0) { logSection('Failed Tests'); failedTests.forEach(test => { logError(`${test.tool}: ${test.error}`); }); } // Save results to JSON file for the PLATFORM_INTEGRATION_REPORT const resultsPath = path.join(__dirname, 'test-results.json'); fs.writeFileSync(resultsPath, JSON.stringify(testResults, null, 2)); log(`\nšŸ“„ Results saved to: ${resultsPath}`, colors.cyan); // Exit with appropriate code process.exit(failedTests.length > 0 ? 1 : 0); } // Run tests runTests().catch(error => { logError(`Fatal error: ${error.message}`); console.error(error); process.exit(1); });

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/oregpt/Agenticledger_MCP_DocsOnly'

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