Labsco
shawntony logo

Claude Google Apps Script MCP Guide

β˜… 1

from shawntony

Integrate Claude AI with Google Apps Script to automate tasks in Google Sheets and Gmail.

πŸ”₯πŸ”₯βœ“ VerifiedFreeQuick setup

Claude MCP Google Apps Script 연동 μ™„μ „ κ°€μ΄λ“œ

Claude와 Google Apps Scriptλ₯Ό MCP(Model Context Protocol)둜 μ—°κ²°ν•˜μ—¬ κ°•λ ₯ν•œ 업무 μžλ™ν™” μ‹œμŠ€ν…œ κ΅¬μΆ•ν•˜κΈ°

Google Apps Script Node.js Claude

πŸ“‹ λͺ©μ°¨

κ°œμš”

이 κ°€μ΄λ“œλŠ” Claude AI와 Google Apps Scriptλ₯Ό MCP ν”„λ‘œν† μ½œμ„ 톡해 μ—°κ²°ν•˜λŠ” μ™„μ „ν•œ 방법을 μ œκ³΅ν•©λ‹ˆλ‹€. μ—°κ²° ν›„ λ‹€μŒκ³Ό 같은 κ°•λ ₯ν•œ κΈ°λŠ₯을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

πŸš€ μ£Όμš” κΈ°λŠ₯

  • πŸ“Š Google Sheets μžλ™ν™”: μ‹€μ‹œκ°„ 데이터 읽기/μ“°κΈ°, 뢄석, 리포트 생성
  • πŸ“§ Gmail 톡합: 슀마트 이메일 λΆ„λ₯˜, μžλ™ 응닡, λŒ€λŸ‰ λ°œμ†‘
  • πŸ€– 업무 μžλ™ν™”: 일정 관리, 데이터 뢄석, μ•Œλ¦Ό μ‹œμŠ€ν…œ
  • πŸ“ˆ μ‹€μ‹œκ°„ λŒ€μ‹œλ³΄λ“œ: μ—¬λŸ¬ 데이터 μ†ŒμŠ€ 톡합 λͺ¨λ‹ˆν„°λ§

μ‹œμŠ€ν…œ μš”κ΅¬μ‚¬ν•­

ν•„μˆ˜ 쀀비사항

μ„€μΉ˜ 확인

node -v    # v18.0.0 이상
npm -v     # 9.0.0 이상

1단계: Google Apps Script ν”„λ‘œμ νŠΈ 생성

1.1 μƒˆ ν”„λ‘œμ νŠΈ 생성

  1. Google Apps Script 접속
  2. "μƒˆ ν”„λ‘œμ νŠΈ" 클릭
  3. ν”„λ‘œμ νŠΈ 이름을 **"Claude MCP Server"**둜 λ³€κ²½
  4. κΈ°λ³Έ myFunction() μ½”λ“œλ₯Ό λͺ¨λ‘ μ‚­μ œ

1.2 확인 사항

  • βœ… ν”„λ‘œμ νŠΈ 이름이 "Claude MCP Server"둜 ν‘œμ‹œ
  • βœ… μ½”λ“œ 에디터가 μ™„μ „νžˆ λΉ„μ–΄μžˆλŠ” μƒνƒœ

2단계: MCP μ„œλ²„ μ½”λ“œ μž‘μ„±

2.1 μ½”λ“œ 볡사 및 λΆ™μ—¬λ„£κΈ°

Google Apps Script 에디터에 λ‹€μŒ μ½”λ“œλ₯Ό λ³΅μ‚¬ν•˜μ—¬ λΆ™μ—¬λ„£μœΌμ„Έμš”:

/**
 * Claude MCP Server for Google Apps Script
 * ν΄λ‘œλ“œμ™€ ꡬ글 μ›Œν¬μŠ€νŽ˜μ΄μŠ€λ₯Ό μ—°κ²°ν•˜λŠ” MCP μ„œλ²„
 */

// MCP μ„œλ²„ μ„€μ •
const MCP_CONFIG = {
  name: "google-workspace-mcp",
  version: "1.0.0",
  description: "Google Workspace integration for Claude via MCP",
};

// 도ꡬ λͺ©λ‘ μ •μ˜
const TOOLS = {
  READ_SHEET: {
    name: "read_google_sheet",
    description: "Google Sheetsμ—μ„œ 데이터λ₯Ό μ½μ–΄μ˜΅λ‹ˆλ‹€",
    inputSchema: {
      type: "object",
      properties: {
        spreadsheetId: {
          type: "string",
          description: "μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID",
        },
        range: {
          type: "string",
          description: "읽을 λ²”μœ„ (예: A1:C10)",
          default: "A:Z",
        },
      },
      required: ["spreadsheetId"],
    },
  },

  WRITE_SHEET: {
    name: "write_google_sheet",
    description: "Google Sheets에 데이터λ₯Ό μ”λ‹ˆλ‹€",
    inputSchema: {
      type: "object",
      properties: {
        spreadsheetId: {
          type: "string",
          description: "μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID",
        },
        range: {
          type: "string",
          description: "μ“Έ λ²”μœ„ (예: A1:C10)",
        },
        values: {
          type: "array",
          description: "μ“Έ 데이터 (2차원 λ°°μ—΄)",
        },
      },
      required: ["spreadsheetId", "range", "values"],
    },
  },

  SEND_EMAIL: {
    name: "send_gmail",
    description: "Gmail을 톡해 이메일을 λ°œμ†‘ν•©λ‹ˆλ‹€",
    inputSchema: {
      type: "object",
      properties: {
        to: {
          type: "string",
          description: "λ°›λŠ” μ‚¬λžŒ 이메일",
        },
        subject: {
          type: "string",
          description: "이메일 제λͺ©",
        },
        body: {
          type: "string",
          description: "이메일 λ‚΄μš©",
        },
      },
      required: ["to", "subject", "body"],
    },
  },
};

/**
 * MCP μ„œλ²„ 메인 ν•Έλ“€λŸ¬
 */
function doPost(e) {
  try {
    const request = JSON.parse(e.postData.contents);
    console.log("MCP Request:", request);

    // μš”μ²­ νƒ€μž…μ— λ”°λ₯Έ 처리
    switch (request.method) {
      case "tools/list":
        return createResponse(handleToolsList());

      case "tools/call":
        return createResponse(handleToolCall(request.params));

      default:
        return createErrorResponse("Unknown method: " + request.method);
    }
  } catch (error) {
    console.error("MCP Error:", error);
    return createErrorResponse("Server error: " + error.message);
  }
}

/**
 * 도ꡬ λͺ©λ‘ λ°˜ν™˜
 */
function handleToolsList() {
  return {
    tools: Object.values(TOOLS),
  };
}

/**
 * 도ꡬ μ‹€ν–‰
 */
function handleToolCall(params) {
  const { name, arguments: args } = params;

  try {
    switch (name) {
      case "read_google_sheet":
        return readGoogleSheet(args);

      case "write_google_sheet":
        return writeGoogleSheet(args);

      case "send_gmail":
        return sendGmail(args);

      default:
        throw new Error("Unknown tool: " + name);
    }
  } catch (error) {
    return {
      isError: true,
      content: [
        {
          type: "text",
          text: `도ꡬ μ‹€ν–‰ 였λ₯˜: ${error.message}`,
        },
      ],
    };
  }
}

/**
 * Google Sheets 데이터 읽기
 */
function readGoogleSheet(args) {
  const { spreadsheetId, range = "A:Z" } = args;

  try {
    const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
    const sheet = spreadsheet.getActiveSheet();
    const data = sheet.getRange(range).getValues();

    // 빈 ν–‰ 제거
    const filteredData = data.filter((row) => row.some((cell) => cell !== ""));

    return {
      content: [
        {
          type: "text",
          text: `μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 데이터 (${
            filteredData.length
          }ν–‰):\n${JSON.stringify(filteredData, null, 2)}`,
        },
      ],
    };
  } catch (error) {
    throw new Error(`Sheets 읽기 μ‹€νŒ¨: ${error.message}`);
  }
}

/**
 * Google Sheets 데이터 μ“°κΈ°
 */
function writeGoogleSheet(args) {
  const { spreadsheetId, range, values } = args;

  try {
    const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
    const sheet = spreadsheet.getActiveSheet();

    if (values.length > 0) {
      sheet.getRange(range).setValues(values);
    }

    return {
      content: [
        {
          type: "text",
          text: `μ„±κ³΅μ μœΌλ‘œ ${values.length}ν–‰μ˜ 데이터λ₯Ό ${range} λ²”μœ„μ— μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€.`,
        },
      ],
    };
  } catch (error) {
    throw new Error(`Sheets μ“°κΈ° μ‹€νŒ¨: ${error.message}`);
  }
}

/**
 * Gmail 이메일 λ°œμ†‘
 */
function sendGmail(args) {
  const { to, subject, body } = args;

  try {
    GmailApp.sendEmail(to, subject, body);

    return {
      content: [
        {
          type: "text",
          text: `이메일이 μ„±κ³΅μ μœΌλ‘œ ${to}μ—κ²Œ λ°œμ†‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.`,
        },
      ],
    };
  } catch (error) {
    throw new Error(`이메일 λ°œμ†‘ μ‹€νŒ¨: ${error.message}`);
  }
}

/**
 * 응닡 생성 헬퍼
 */
function createResponse(data) {
  return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(
    ContentService.MimeType.JSON
  );
}

function createErrorResponse(message) {
  return ContentService.createTextOutput(
    JSON.stringify({
      error: {
        code: -1,
        message: message,
      },
    })
  ).setMimeType(ContentService.MimeType.JSON);
}

/**
 * ν…ŒμŠ€νŠΈ ν•¨μˆ˜
 */
function testMCPServer() {
  console.log("MCP μ„œλ²„ ν…ŒμŠ€νŠΈ μ‹œμž‘");

  // 도ꡬ λͺ©λ‘ ν…ŒμŠ€νŠΈ
  const toolsList = handleToolsList();
  console.log("도ꡬ λͺ©λ‘:", toolsList);

  console.log("MCP μ„œλ²„κ°€ μ •μƒμ μœΌλ‘œ μ„€μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€!");
}

2.2 μ €μž₯

  • Ctrl + S λ˜λŠ” πŸ’Ύ μ €μž₯ λ²„νŠΌ 클릭
  • 문법 였λ₯˜κ°€ μ—†λŠ”μ§€ 확인 (λΉ¨κ°„ 밑쀄이 μ—†μ–΄μ•Ό 함)

3단계: μ›Ή μ•± 배포

3.1 배포 μ„€μ •

  1. μš°μƒλ‹¨ "배포" λ²„νŠΌ 클릭
  2. "μƒˆ 배포" 선택
  3. ν†±λ‹ˆλ°”ν€΄ βš™οΈ μ•„μ΄μ½˜ 클릭 β†’ "μ›Ή μ•±" 선택
  4. μ„€μ •κ°’ μž…λ ₯:
    • μ„€λͺ…: "Claude MCP Server v1.0"
    • λ‹€μŒ μ‚¬μš©μžλ‘œ μ‹€ν–‰: "λ‚˜"
    • μ•‘μ„ΈμŠ€ κΆŒν•œ: "λͺ¨λ“  μ‚¬μš©μž"

3.2 κΆŒν•œ 승인

  1. "배포" λ²„νŠΌ 클릭
  2. "μ•‘μ„ΈμŠ€ 승인" νŒμ—…μ—μ„œ 클릭
  3. Google 계정 선택
  4. "κ³ κΈ‰" β†’ "μ•ˆμ „ν•˜μ§€ μ•ŠμŒ(unsafe)" β†’ "이동" 클릭
  5. "ν—ˆμš©" λ²„νŠΌ 클릭

3.3 μ›Ή μ•± URL 볡사

  • 배포 μ™„λ£Œ ν›„ λ‚˜νƒ€λ‚˜λŠ” μ›Ή μ•± URL을 λ³΅μ‚¬ν•΄μ„œ μ €μž₯
  • ν˜•νƒœ: https://script.google.com/macros/s/ABC...XYZ/exec

4단계: ν…ŒμŠ€νŠΈ μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 생성

4.1 μƒˆ μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 생성

  1. Google Sheets 접속
  2. "μƒˆλ‘œ λ§Œλ“€κΈ°" β†’ "빈 μŠ€ν”„λ ˆλ“œμ‹œνŠΈ" 클릭
  3. μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 이름을 **"Claude MCP Test Sheet"**둜 λ³€κ²½

4.2 ν…ŒμŠ€νŠΈ 데이터 μž…λ ₯

A1 μ…€λΆ€ν„° λ‹€μŒ 데이터λ₯Ό λ³΅μ‚¬ν•΄μ„œ λΆ™μ—¬λ„£μœΌμ„Έμš”:

이름	λΆ€μ„œ	직급	κΈ‰μ—¬
κΉ€μ² μˆ˜	κ°œλ°œνŒ€	κ³Όμž₯	5000
이영희	λ§ˆμΌ€νŒ…νŒ€	λŒ€λ¦¬	4000
λ°•λ―Όμˆ˜	μΈμ‚¬νŒ€	μ°¨μž₯	6000
μ΅œμ€μ •	μž¬λ¬΄νŒ€	사원	3500

4.3 μŠ€ν”„λ ˆλ“œμ‹œνŠΈ μ„€μ •

  1. μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID 볡사

    • λΈŒλΌμš°μ € μ£Όμ†Œμ°½μ—μ„œ ID λΆ€λΆ„ 볡사
    • https://docs.google.com/spreadsheets/d/[이뢀뢄이ID]/edit
  2. 곡유 μ„€μ •

    • μš°μƒλ‹¨ "곡유" λ²„νŠΌ 클릭
    • "링크가 μžˆλŠ” λͺ¨λ“  μ‚¬μš©μž" β†’ "νŽΈμ§‘μž" κΆŒν•œμœΌλ‘œ λ³€κ²½
    • "μ™„λ£Œ" 클릭

5단계: MCP μ„œλ²„ ν…ŒμŠ€νŠΈ

5.1 κΈ°λ³Έ ν…ŒμŠ€νŠΈ

  1. Google Apps Script둜 λŒμ•„κ°€κΈ°
  2. ν•¨μˆ˜ 선택 λ“œλ‘­λ‹€μš΄μ—μ„œ testMCPServer 선택
  3. ▢️ "μ‹€ν–‰" λ²„νŠΌ 클릭
  4. κΆŒν•œ 승인 (처음 μ‹€ν–‰μ‹œ)

5.2 μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기 ν…ŒμŠ€νŠΈ

κΈ°μ‘΄ μ½”λ“œ 맨 μ•„λž˜μ— λ‹€μŒ ν•¨μˆ˜λ₯Ό μΆ”κ°€:

/**
 * μ‹€μ œ μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기 ν…ŒμŠ€νŠΈ
 */
function testReadSheet() {
  const spreadsheetId = "YOUR_SHEET_ID"; // μ‹€μ œ μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID둜 λ³€κ²½

  try {
    const result = readGoogleSheet({
      spreadsheetId: spreadsheetId,
      range: "A1:D10",
    });

    console.log("μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기 성곡!");
    console.log("κ²°κ³Ό:", result);
  } catch (error) {
    console.error("μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기 μ‹€νŒ¨:", error);
  }
}

5.3 ν…ŒμŠ€νŠΈ μ‹€ν–‰

  1. testReadSheet ν•¨μˆ˜ 선택 ν›„ μ‹€ν–‰
  2. μ‹€ν–‰ λ‘œκ·Έμ—μ„œ ν…ŒμŠ€νŠΈ 데이터가 좜λ ₯λ˜λŠ”μ§€ 확인

6단계: 둜컬 MCP ν΄λΌμ΄μ–ΈνŠΈ μ„€μ •

6.1 ν”„λ‘œμ νŠΈ 폴더 생성

μ›ν•˜λŠ” μœ„μΉ˜μ— λ‹€μŒ ꡬ쑰둜 폴더λ₯Ό μƒμ„±ν•˜μ„Έμš”:

mkdir claude-gas-mcp
cd claude-gas-mcp
mkdir src
mkdir config

6.2 package.json 생성

ν”„λ‘œμ νŠΈ λ£¨νŠΈμ— package.json νŒŒμΌμ„ 생성:

{
  "name": "claude-gas-mcp",
  "version": "1.0.0",
  "description": "Claude와 Google Apps Script 연결을 μœ„ν•œ MCP ν΄λΌμ΄μ–ΈνŠΈ",
  "main": "src/server.js",
  "type": "module",
  "scripts": {
    "start": "node src/server.js",
    "dev": "node --watch src/server.js"
  },
  "keywords": ["claude", "mcp", "google-apps-script"],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^0.4.0",
    "axios": "^1.6.0"
  }
}

6.3 μ˜μ‘΄μ„± μ„€μΉ˜

npm install

6.4 μ„€μ • 파일 생성

config/mcp-config.json νŒŒμΌμ„ 생성:

{
  "name": "Google Apps Script MCP Server",
  "version": "1.0.0",
  "description": "Google Workspace와 Claude μ—°κ²°",
  "config": {
    "webAppUrl": "YOUR_WEB_APP_URL_HERE",
    "testSpreadsheetId": "YOUR_SPREADSHEET_ID_HERE"
  },
  "tools": ["read_google_sheet", "write_google_sheet", "send_gmail"]
}

6.5 MCP μ„œλ²„ μ½”λ“œ 생성

src/server.js νŒŒμΌμ„ 생성:

#!/usr/bin/env node

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 axios from "axios";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// μ„€μ • 파일 λ‘œλ“œ
const configPath = path.join(__dirname, "../config/mcp-config.json");
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));

class GoogleAppsScriptMCPServer {
  constructor() {
    this.server = new Server(
      {
        name: "google-apps-script-mcp",
        version: "1.0.0",
      },
      {
        capabilities: {
          tools: {},
        },
      }
    );

    this.setupToolHandlers();
    this.setupErrorHandling();
  }

  setupErrorHandling() {
    this.server.onerror = (error) => {
      console.error("[MCP Error]", error);
    };

    process.on("SIGINT", async () => {
      await this.server.close();
      process.exit(0);
    });
  }

  setupToolHandlers() {
    // 도ꡬ λͺ©λ‘ 제곡
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: "read_google_sheet",
            description: "Google Sheetsμ—μ„œ 데이터λ₯Ό μ½μ–΄μ˜΅λ‹ˆλ‹€",
            inputSchema: {
              type: "object",
              properties: {
                spreadsheetId: {
                  type: "string",
                  description: "μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID",
                },
                range: {
                  type: "string",
                  description: "읽을 λ²”μœ„ (예: A1:C10)",
                  default: "A:Z",
                },
              },
              required: ["spreadsheetId"],
            },
          },
          {
            name: "write_google_sheet",
            description: "Google Sheets에 데이터λ₯Ό μ”λ‹ˆλ‹€",
            inputSchema: {
              type: "object",
              properties: {
                spreadsheetId: {
                  type: "string",
                  description: "μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID",
                },
                range: {
                  type: "string",
                  description: "μ“Έ λ²”μœ„ (예: A1:C10)",
                },
                values: {
                  type: "array",
                  description: "μ“Έ 데이터 (2차원 λ°°μ—΄)",
                },
              },
              required: ["spreadsheetId", "range", "values"],
            },
          },
          {
            name: "send_gmail",
            description: "Gmail을 톡해 이메일을 λ°œμ†‘ν•©λ‹ˆλ‹€",
            inputSchema: {
              type: "object",
              properties: {
                to: {
                  type: "string",
                  description: "λ°›λŠ” μ‚¬λžŒ 이메일",
                },
                subject: {
                  type: "string",
                  description: "이메일 제λͺ©",
                },
                body: {
                  type: "string",
                  description: "이메일 λ‚΄μš©",
                },
              },
              required: ["to", "subject", "body"],
            },
          },
        ],
      };
    });

    // 도ꡬ μ‹€ν–‰
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      try {
        switch (name) {
          case "read_google_sheet":
            return await this.readGoogleSheet(args);

          case "write_google_sheet":
            return await this.writeGoogleSheet(args);

          case "send_gmail":
            return await this.sendGmail(args);

          default:
            throw new Error(`μ•Œ 수 μ—†λŠ” 도ꡬ: ${name}`);
        }
      } catch (error) {
        return {
          content: [
            {
              type: "text",
              text: `였λ₯˜ λ°œμƒ: ${error.message}`,
            },
          ],
          isError: true,
        };
      }
    });
  }

  async callGoogleAppsScript(toolName, params) {
    const payload = {
      method: "tools/call",
      params: {
        name: toolName,
        arguments: params,
      },
    };

    try {
      console.log(`[MCP] Google Apps Script 호좜: ${toolName}`, params);

      const response = await axios.post(config.config.webAppUrl, payload, {
        headers: {
          "Content-Type": "application/json",
        },
        timeout: 30000,
      });

      console.log(`[MCP] 응닡 λ°›μŒ:`, response.data);
      return response.data;
    } catch (error) {
      console.error(`[MCP] μš”μ²­ μ‹€νŒ¨:`, error.message);
      throw new Error(`Google Apps Script 호좜 μ‹€νŒ¨: ${error.message}`);
    }
  }

  async readGoogleSheet(args) {
    const result = await this.callGoogleAppsScript("read_google_sheet", args);

    return {
      content: [
        {
          type: "text",
          text: result.content?.[0]?.text || "데이터λ₯Ό μ½μ–΄μ™”μŠ΅λ‹ˆλ‹€.",
        },
      ],
    };
  }

  async writeGoogleSheet(args) {
    const result = await this.callGoogleAppsScript("write_google_sheet", args);

    return {
      content: [
        {
          type: "text",
          text: result.content?.[0]?.text || "데이터λ₯Ό μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€.",
        },
      ],
    };
  }

  async sendGmail(args) {
    const result = await this.callGoogleAppsScript("send_gmail", args);

    return {
      content: [
        {
          type: "text",
          text: result.content?.[0]?.text || "이메일을 λ°œμ†‘ν–ˆμŠ΅λ‹ˆλ‹€.",
        },
      ],
    };
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);

    console.error("Google Apps Script MCP μ„œλ²„κ°€ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
    console.error(`μ›Ή μ•± URL: ${config.config.webAppUrl}`);
    console.error(
      "μ‚¬μš© κ°€λŠ₯ν•œ 도ꡬ: read_google_sheet, write_google_sheet, send_gmail"
    );
  }
}

// μ„œλ²„ μ‹œμž‘
const server = new GoogleAppsScriptMCPServer();
server.run().catch(console.error);

6.6 ν…ŒμŠ€νŠΈ 슀크립트 생성

src/test.js νŒŒμΌμ„ 생성:

import axios from "axios";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// μ„€μ • 파일 λ‘œλ“œ
const configPath = path.join(__dirname, "../config/mcp-config.json");
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));

async function testGoogleAppsScript() {
  console.log("πŸ§ͺ Google Apps Script MCP μ—°κ²° ν…ŒμŠ€νŠΈ μ‹œμž‘\n");

  // ν…ŒμŠ€νŠΈ 1: μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기
  console.log("πŸ“Š ν…ŒμŠ€νŠΈ 1: μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 데이터 읽기");
  try {
    const readPayload = {
      method: "tools/call",
      params: {
        name: "read_google_sheet",
        arguments: {
          spreadsheetId: config.config.testSpreadsheetId,
          range: "A1:D10",
        },
      },
    };

    const readResponse = await axios.post(
      config.config.webAppUrl,
      readPayload,
      {
        headers: { "Content-Type": "application/json" },
        timeout: 10000,
      }
    );

    console.log("βœ… μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기 성곡!");
    console.log("πŸ“‹ 응닡:", readResponse.data);
  } catch (error) {
    console.log("❌ μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기 μ‹€νŒ¨:", error.message);
  }

  console.log("\nπŸŽ‰ ν…ŒμŠ€νŠΈ μ™„λ£Œ!");
}

// ν…ŒμŠ€νŠΈ μ‹€ν–‰
testGoogleAppsScript().catch(console.error);

6.7 μ—°κ²° ν…ŒμŠ€νŠΈ

node src/test.js

7단계: Claude Desktop μ—°κ²°

7.1 Claude Desktop μ„€μ • 파일 μˆ˜μ •

Windowsμ—μ„œ μ„€μ • 파일 μœ„μΉ˜:

%APPDATA%\Claude\claude_desktop_config.json

7.2 μ„€μ • 파일 λ‚΄μš©

κΈ°μ‘΄ 섀정에 λ‹€μŒ λ‚΄μš©μ„ μΆ”κ°€:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "@modelcontextprotocol/server-filesystem",
        "C:\\Users\\[μ‚¬μš©μžλͺ…]\\Desktop"
      ],
      "env": {}
    },
    "google-apps-script": {
      "command": "node",
      "args": ["C:\\[ν”„λ‘œμ νŠΈκ²½λ‘œ]\\claude-gas-mcp\\src\\server.js"],
      "env": {
        "NODE_ENV": "production"
      }
    }
  },
  "darkMode": "dark",
  "scale": 0,
  "locale": "ko-KR"
}

7.3 Claude Desktop μž¬μ‹œμž‘

  1. Claude Desktop 앱을 μ™„μ „νžˆ μ’…λ£Œ
  2. 앱을 λ‹€μ‹œ μ‹œμž‘
  3. μƒˆ λŒ€ν™” μ‹œμž‘

7.4 μ—°κ²° ν…ŒμŠ€νŠΈ

Claude Desktopμ—μ„œ λ‹€μŒκ³Ό 같이 ν…ŒμŠ€νŠΈ:

Google Apps Script MCP μ„œλ²„κ°€ μ—°κ²°λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜κ³ ,
μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID [YOUR_SHEET_ID]의 데이터λ₯Ό μ½μ–΄μ„œ λ³΄μ—¬μ€˜.

μ‚¬μš© μ˜ˆμ‹œ

πŸ“Š κΈ°λ³Έ 데이터 μ‘°μž‘

μŠ€ν”„λ ˆλ“œμ‹œνŠΈ 읽기

"μŠ€ν”„λ ˆλ“œμ‹œνŠΈ ID 13Qy45cJ6oDJsyS007bml2GAyklGAiY7EGd1GZ1vO7roμ—μ„œ
직원 데이터λ₯Ό μ½μ–΄μ„œ λΆ€μ„œλ³„ 평균 κΈ‰μ—¬λ₯Ό κ³„μ‚°ν•΄μ€˜"

데이터 μΆ”κ°€

"μƒˆ 직원 정보λ₯Ό μΆ”κ°€ν•΄μ€˜:
이름-홍길동, λΆ€μ„œ-ITνŒ€, 직급-사원, κΈ‰μ—¬-3200"

이메일 λ°œμ†‘

"λΆ€μ„œλ³„ κΈ‰μ—¬ ν˜„ν™©μ„ μš”μ•½ν•΄μ„œ manager@company.com으둜 λ³΄λ‚΄μ€˜"

πŸ€– κ³ κΈ‰ μžλ™ν™” μš”μ²­

업무 μžλ™ν™” ν•¨μˆ˜ 생성

"맀일 μ•„μΉ¨ 8μ‹œμ— μ „λ‚  맀좜 데이터λ₯Ό λΆ„μ„ν•΄μ„œ
μ΄λ©”μΌλ‘œ λ³΄λ‚΄μ£ΌλŠ” GAS ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄μ€˜"

슀마트 이메일 μ‹œμŠ€ν…œ

"Gmailμ—μ„œ νŠΉμ • ν‚€μ›Œλ“œκ°€ ν¬ν•¨λœ 이메일을 μžλ™μœΌλ‘œ λΆ„λ₯˜ν•˜κ³ 
μ μ ˆν•œ ν…œν”Œλ¦ΏμœΌλ‘œ λ‹΅λ³€ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄μ€˜"

κ²°κ³Ό: ν΄λ‘œλ“œκ°€ ν‚€μ›Œλ“œ 기반 λΆ„λ₯˜ μ•Œκ³ λ¦¬μ¦˜, μžλ™ 응닡 μ‹œμŠ€ν…œ, 라벨링 κΈ°λŠ₯을 ν¬ν•¨ν•œ μ™„μ „ν•œ 이메일 μžλ™ν™” μ‹œμŠ€ν…œμ„ μƒμ„±ν•©λ‹ˆλ‹€.

μ‹€μ‹œκ°„ λŒ€μ‹œλ³΄λ“œ

"μ—¬λŸ¬ μ‹œνŠΈμ˜ 데이터λ₯Ό μ’…ν•©ν•΄μ„œ μ‹€μ‹œκ°„ λŒ€μ‹œλ³΄λ“œλ₯Ό
λ§Œλ“€μ–΄μ£ΌλŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν•΄μ€˜"

κ²°κ³Ό: 닀쀑 데이터 μ†ŒμŠ€λ₯Ό ν†΅ν•©ν•˜μ—¬ μ‹€μ‹œκ°„ μ—…λ°μ΄νŠΈλ˜λŠ” HTML λŒ€μ‹œλ³΄λ“œμ™€ μžλ™ κ°±μ‹  μ‹œμŠ€ν…œμ„ κ΅¬μΆ•ν•©λ‹ˆλ‹€.

데이터 뢄석 μžλ™ν™”

"맀좜 데이터λ₯Ό λΆ„μ„ν•΄μ„œ νŠΈλ Œλ“œ μ˜ˆμΈ‘ν•˜κ³ 
μΈμ‚¬μ΄νŠΈλ₯Ό μš”μ•½ν•΄μ„œ κ²½μ˜μ§„μ—κ²Œ λ³΄κ³ ν•˜λŠ” μ‹œμŠ€ν…œμ„ λ§Œλ“€μ–΄μ€˜"

κ²°κ³Ό: 톡계 뢄석, 예츑 λͺ¨λΈλ§, μ‹œκ°ν™” 차트, μžλ™ 리포트 생성 κΈ°λŠ₯을 ν¬ν•¨ν•œ BI μ‹œμŠ€ν…œμ„ κ΅¬μΆ•ν•©λ‹ˆλ‹€.