Skip to main content
Glama

Spreadsheet MCP Server

by HosakaKeigo
# Spreadsheet API仕様書 この文書は、将来的なGoogle Apps Script Web AppでのAPI実装のためのOpenAPI仕様を定義します。 ```yaml openapi: 3.0.3 info: title: Spreadsheet API description: Google スプレッドシートデータにアクセスするためのAPI version: 1.0.0 servers: - url: https://script.google.com/macros/s/{DEPLOYMENT_ID}/exec description: Google Apps Script Web App URL paths: /getSpreadsheetInfo: get: summary: スプレッドシートの基本情報を取得する description: スプレッドシートのメタデータとシート一覧を返します parameters: - name: id in: query description: スプレッドシートID required: true schema: type: string - name: apiKey in: query description: API認証キー required: true schema: type: string responses: '200': description: 成功 content: application/json: schema: $ref: '#/components/schemas/SpreadsheetInfo' '400': description: 無効なリクエスト content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: 認証エラー content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: スプレッドシートが見つからない content: application/json: schema: $ref: '#/components/schemas/Error' /getSheetData: get: summary: シートのデータを取得する description: 指定されたシートのデータを二次元配列で返します parameters: - name: id in: query description: スプレッドシートID required: true schema: type: string - name: sheetName in: query description: シート名 required: true schema: type: string - name: apiKey in: query description: API認証キー required: true schema: type: string - name: maxRows in: query description: 取得する最大行数(省略時は全行) required: false schema: type: integer default: 1000 - name: maxCols in: query description: 取得する最大列数(省略時は全列) required: false schema: type: integer - name: startRow in: query description: 取得開始行(0スタート、省略時は0) required: false schema: type: integer default: 0 - name: startCol in: query description: 取得開始列(0スタート、省略時は0) required: false schema: type: integer default: 0 responses: '200': description: 成功 content: application/json: schema: $ref: '#/components/schemas/SheetData' '400': description: 無効なリクエスト content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: 認証エラー content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: スプレッドシートまたはシートが見つからない content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: SpreadsheetInfo: type: object required: - id - name - url - lastModified - sheets properties: id: type: string description: スプレッドシートのID name: type: string description: スプレッドシートの名前 url: type: string description: スプレッドシートのURL lastModified: type: string format: date-time description: 最終更新日時(ISO 8601形式) sheets: type: array description: シート一覧 items: $ref: '#/components/schemas/SheetInfo' SheetInfo: type: object required: - id - name - index - rowCount - columnCount properties: id: type: integer description: シートのID name: type: string description: シート名 index: type: integer description: シートのインデックス(0から始まる) rowCount: type: integer description: 行数 columnCount: type: integer description: 列数 SheetData: type: object required: - spreadsheetId - sheetName - data properties: spreadsheetId: type: string description: スプレッドシートのID sheetName: type: string description: シート名 data: type: array description: シートデータの二次元配列 items: type: array items: oneOf: - type: string - type: number - type: boolean - type: 'null' totalRows: type: integer description: シート内の実際の合計行数 totalCols: type: integer description: シート内の実際の合計列数 startRow: type: integer description: 返却データの開始行(0から始まる) startCol: type: integer description: 返却データの開始列(0から始まる) Error: type: object required: - error - message properties: error: type: string description: エラーコード message: type: string description: エラーメッセージ ``` ## Google Apps Script 実装例 以下は、上記API仕様に基づくGoogle Apps Script実装の一例です: ```javascript // SpreadsheetアクセスのためのAPIを提供するGAS Web App // 設定 const API_KEY = 'your-api-key-here'; // APIキー const MAX_ROWS = 1000; // デフォルトの最大行数 /** * Webアプリのエントリポイント */ function doGet(e) { try { // APIキー検証 if (!validateApiKey(e.parameter.apiKey)) { return createErrorResponse(401, 'Invalid API key'); } const action = e.parameter.action; // リクエスト処理 if (action === 'getSpreadsheetInfo') { return handleGetSpreadsheetInfo(e); } else if (action === 'getSheetData') { return handleGetSheetData(e); } else { return createErrorResponse(400, 'Invalid action parameter'); } } catch (error) { return createErrorResponse(500, error.toString()); } } /** * getSpreadsheetInfoリクエストを処理 */ function handleGetSpreadsheetInfo(e) { const id = e.parameter.id; if (!id) { return createErrorResponse(400, 'Missing required parameter: id'); } try { const ss = SpreadsheetApp.openById(id); const sheets = ss.getSheets(); const result = { id: id, name: ss.getName(), url: ss.getUrl(), lastModified: ss.getLastUpdated().toISOString(), sheets: sheets.map(sheet => ({ id: sheet.getSheetId(), name: sheet.getName(), index: sheet.getIndex(), rowCount: sheet.getLastRow(), columnCount: sheet.getLastColumn() })) }; return ContentService.createTextOutput(JSON.stringify(result)) .setMimeType(ContentService.MimeType.JSON); } catch (error) { if (error.toString().includes('no item with the given ID')) { return createErrorResponse(404, 'Spreadsheet not found'); } return createErrorResponse(500, error.toString()); } } /** * getSheetDataリクエストを処理 */ function handleGetSheetData(e) { const id = e.parameter.id; const sheetName = e.parameter.sheetName; if (!id || !sheetName) { return createErrorResponse(400, 'Missing required parameters: id and/or sheetName'); } const maxRows = e.parameter.maxRows ? parseInt(e.parameter.maxRows) : MAX_ROWS; const maxCols = e.parameter.maxCols ? parseInt(e.parameter.maxCols) : null; const startRow = e.parameter.startRow ? parseInt(e.parameter.startRow) : 0; const startCol = e.parameter.startCol ? parseInt(e.parameter.startCol) : 0; try { const ss = SpreadsheetApp.openById(id); const sheet = ss.getSheetByName(sheetName); if (!sheet) { return createErrorResponse(404, `Sheet '${sheetName}' not found`); } const lastRow = sheet.getLastRow(); const lastCol = sheet.getLastColumn(); if (lastRow === 0 || lastCol === 0) { // 空のシート return ContentService.createTextOutput(JSON.stringify({ spreadsheetId: id, sheetName: sheetName, data: [], totalRows: 0, totalCols: 0, startRow: startRow, startCol: startCol })).setMimeType(ContentService.MimeType.JSON); } // データを取得する範囲を計算 const endRow = Math.min(lastRow, startRow + maxRows); const endCol = maxCols ? Math.min(lastCol, startCol + maxCols) : lastCol; const numRows = endRow - startRow; const numCols = endCol - startCol; if (numRows <= 0 || numCols <= 0) { // 有効な範囲がない return createErrorResponse(400, 'Invalid data range'); } // データ取得 const dataRange = sheet.getRange(startRow + 1, startCol + 1, numRows, numCols); const data = dataRange.getValues(); const result = { spreadsheetId: id, sheetName: sheetName, data: data, totalRows: lastRow, totalCols: lastCol, startRow: startRow, startCol: startCol }; return ContentService.createTextOutput(JSON.stringify(result)) .setMimeType(ContentService.MimeType.JSON); } catch (error) { if (error.toString().includes('no item with the given ID')) { return createErrorResponse(404, 'Spreadsheet not found'); } return createErrorResponse(500, error.toString()); } } /** * APIキーの検証 */ function validateApiKey(key) { return key === API_KEY; } /** * エラーレスポンスの作成 */ function createErrorResponse(code, message) { const error = { error: code.toString(), message: message }; return ContentService.createTextOutput(JSON.stringify(error)) .setMimeType(ContentService.MimeType.JSON); } ``` ## 統合方法 この仕様に基づくAPIを実装した後、MCPサーバーの`api/spreadsheet.ts`を修正して、モック実装ではなく実際のAPIコールを行うように変更します。以下の手順を行います: 1. 認証情報を設定する(環境変数またはconfigファイル) 2. `getSpreadsheetInfo`と`getSheetData`関数を書き換えて、APIエンドポイントにHTTPリクエストを送信する 3. レスポンス形式をMCPツールの期待する形式に変換する この変更により、MCPサーバーは実際のスプレッドシートデータにアクセスできるようになります。

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/HosakaKeigo/spreadsheet-mcp-server'

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