---
# Google Chat Webhook MCP Server
[](https://github.com/ice3x2/google-chat-webhook-mcp/actions)
[](https://www.npmjs.com/package/google-chat-webhook-mcp)
[](https://opensource.org/licenses/MIT)
An MCP (Model Context Protocol) server that sends messages to Google Chat via webhooks. Automatically converts Markdown to Google Chat Cards V2 format with image validation, structured logging, and fallback handling.
## Features
- π **MCP Protocol Support**: Integrates with Claude Code, GitHub Copilot, and other MCP clients
- οΏ½ **MCP Protocol Support**: Integrates with Claude Code, GitHub Copilot, and other MCP clients
- οΏ½π **Markdown β Cards V2 Auto-conversion**: Supports headers, lists, code blocks, tables, images, and more
- πΌοΈ **Image URL Validation**: Validates with HEAD requests (HTTP status, Content-Type, size)
- π **Auto Fallback**: Automatically falls back to text when Cards V2 fails
- π **Structured Logging**: JSON format with 30-day retention
- β
**Test Automation**: Snapshot tests, integration tests, CI/CD pipeline
## Installation
### npm (Recommended)
```bash
npm install -g google-chat-webhook-mcp
```
### From Source (Development)
```bash
git clone https://github.com/ice3x2/google-chat-webhook-mcp.git
cd google-chat-webhook-mcp
npm install
npm run build
```
## Google Chat Webhook Setup
Before configuring the MCP server, create a Google Chat Webhook URL:
1. Open your Google Chat space
2. Menu β "Apps & integrations" β "Manage webhooks"
3. Click "Add webhook"
4. Enter a name and **copy the URL**
5. Use it in the configuration below
## MCP Client Configuration
### 1. Claude Code
#### Config File Location
- **Windows**: `%USERPROFILE%\.claude.json`
- **macOS/Linux**: `~/.claude.json`
**Note**: Claude Desktop uses different paths:
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`
#### npm Installation
```json
{
"mcpServers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
}
}
}
}
```
#### Source Installation
```json
{
"mcpServers": {
"google-chat": {
"command": "node",
"args": ["C:\\path\\to\\google-chat-webhook-mcp\\dist\\index.js"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
}
}
}
}
```
**β οΈ Note**: Use `\\` or `/` for Windows paths (e.g., `C:/path/to/...`)
#### Configuration Scopes
Claude Code supports three configuration scopes:
1. **User Scope** (Global): `~/.claude.json` - Available across all projects
2. **Project Scope** (Shared): `.mcp.json` in project root - Version-controlled, team-shared
3. **Local Scope** (Private): Project-specific, personal settings
**Priority**: Local > Project > User
#### Project-Scoped Configuration (.mcp.json)
For team-shared MCP servers, create `.mcp.json` in your project root:
```json
{
"mcpServers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "${GOOGLE_CHAT_WEBHOOK_URL}"
}
}
}
}
```
**Benefits**:
- β
Version-controlled with Git
- β
Team-shared configuration
- β
Environment variable support: `${VAR}` or `${VAR:-default}`
- β
Project-specific MCP servers
**Environment Variables**: Each team member can set their own webhook URL:
```bash
# Linux/macOS
export GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
# Windows (PowerShell)
$env:GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
```
#### How to Apply
**User-scoped configuration** (~/.claude.json):
1. Edit `~/.claude.json` (or `%USERPROFILE%\.claude.json` on Windows)
2. Save the file
3. Restart Claude Code if already running
**Project-scoped configuration** (.mcp.json):
1. Create `.mcp.json` in project root
2. Set environment variables for sensitive data
3. Commit `.mcp.json` to version control
4. Use commands like "Send a message to Google Chat"
### 2. GitHub Copilot (VS Code)
[VS Code GitHub Copilot](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode) supports MCP through **agent mode**. Configure MCP servers in workspace or user settings.
#### Configuration File Locations
Choose one of the following:
- **User Settings**: `~/.vscode/settings.json` or `%APPDATA%\Code\User\settings.json` (Windows)
- **Workspace Settings**: `.vscode/settings.json` in your project root
- **Claude Code Config** (Auto-import): Copy from `~/.claude.json`
#### Configuration (mcp.json format)
Add to `settings.json`:
```json
{
"github.copilot.chat.mcp.servers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
}
}
}
}
```
#### Features
- **Agent Mode Integration**: MCP tools available in agent workflow
- **Per-Session Tool Selection**: Choose which tools to enable per session
- **STDIO & SSE Support**: Both transport types supported
- **Debugging**: Restart commands and output logging built-in
#### Using with Agent Mode
1. Open GitHub Copilot Chat in VS Code
2. Enable agent mode (if not already enabled)
3. Start a conversation - Copilot will automatically access MCP tools
4. Tools require approval before execution
**Example:**
```
@workspace Send a deployment summary to Google Chat
```
**π Note**: GitHub Copilot's MCP support includes agent mode, allowing sophisticated workflows. Make sure you're using the latest VS Code and GitHub Copilot extension.
### 3. Other MCP Clients
Works with any MCP-compatible client:
```json
{
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "your-webhook-url"
}
}
```
## Usage
### MCP Tools (3 Tools)
Available tools in Claude Code or other MCP clients:
#### 1. `send_google_chat_text`
Send simple text messages
**Example (Claude Code):**
```
Send "Hello from Claude!" to Google Chat
```
**Parameters:**
```json
{
"text": "Hello, Google Chat!"
}
```
#### 2. `send_google_chat_cards_v2`
Send Cards V2 format directly (advanced users)
**Parameters:**
```json
{
"text": "Card Message",
"cardsV2": [
{
"cardId": "unique-card",
"card": {
"header": { "title": "Card Title" },
"sections": [
{
"widgets": [
{ "textParagraph": { "text": "Card content" } }
]
}
]
}
}
]
}
```
#### 3. `send_google_chat_markdown` β **Recommended**
Convert Markdown to Cards V2 and send
**Example (Claude Code):**
```
Send this markdown to Google Chat:
# Project Update
- Task 1: β
Completed
- Task 2: π§ In Progress
**Deadline**: Tomorrow
```
**Parameters:**
```json
{
"markdown": "# Title\n\n**Bold** and *italic*\n\n- List item 1\n- List item 2\n\n```python\nprint('Hello')\n```",
"cardTitle": "Markdown Message",
"fallbackToText": true
}
```
**Options:**
- `cardTitle`: Title shown at the top of the card (optional)
- `fallbackToText`: Auto-send as text on conversion failure (default: false)
### Claude Code Usage Example
After setup, Claude will automatically use MCP tools when you chat naturally:
**π€ User:**
> "Send a project status update to Google Chat. Show 3 completed tasks and 2 in-progress tasks as a markdown list."
**π€ Claude:**
> (Automatically calls `send_google_chat_markdown` tool)
>
> I've sent the message to Google Chat. The project status has been updated.
### Supported Markdown Syntax
Markdown written in Claude or MCP clients is automatically converted to Google Chat Cards V2.
| Syntax | Markdown Example | Google Chat Rendering |
|--------|------------------|----------------------|
| **Headers** | `# H1`, `## H2`, `### H3` | Bold with size differences |
| **Bold** | `**bold**` or `__bold__` | **bold** |
| **Italic** | `*italic*` or `_italic_` | *italic* |
| **Inline Code** | `` `code` `` | `code` (monospace) |
| **Code Block** | ` ```python\ncode\n``` ` | Syntax-highlighted box |
| **Ordered List** | `1. First\n2. Second` | 1. First<br>2. Second |
| **Unordered List** | `- Item` or `* Item` | β’ Item |
| **Nested List** | ` - nested` (2-space indent) | γβ’ nested (Em space) |
| **Table** | `\| A \| B \|\n\|--\|--\|` | Monospace table |
| **Image** | `` | Image widget (after validation) |
| **Link** | `[text](https://...)` | Clickable link |
| **Horizontal Rule** | `---` or `***` | Divider |
| **Blockquote** | `> quote` | Indented + gray text |
**Example Markdown:**
```markdown
# Project Deployment Complete π
## Key Changes
- **Performance**: API response 30% faster
- **Bug Fix**: Login error resolved
- New feature added
## Deployment Status
| Environment | Status | Version |
|-------------|--------|---------|
| Production | β
| v2.1.0 |
| Staging | β
| v2.1.0 |
## Next Steps
1. Monitor for 24 hours
2. Collect user feedback
3. Plan next sprint
Code example:
```python
def deploy():
print("Deploying v2.1.0...")
return True
```
See [documentation](https://docs.example.com) for details.
```
**Result:** Headers, lists, tables, and code blocks are all visually distinguished in Google Chat.
## Environment Variables
### Required
| Variable | Description | Example |
|----------|-------------|---------|
| `GOOGLE_CHAT_WEBHOOK_URL` | Google Chat Webhook URL | `https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx` |
### Optional (Logging)
| Variable | Description | Default | Values |
|----------|-------------|---------|--------|
| `LOG_LEVEL` | Log level | `INFO` | `DEBUG`, `INFO`, `WARN`, `ERROR` |
| `LOG_DIR` | Log directory path | `./logs` | Absolute/relative path |
| `LOG_RETENTION_DAYS` | Days to keep logs | `30` | Number (days) |
| `LOG_ENABLE_CONSOLE` | Enable console output | `true` | `true`, `false` |
### Configuration Methods
#### Claude Code (~/.claude.json)
```json
{
"mcpServers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx",
"LOG_LEVEL": "INFO",
"LOG_RETENTION_DAYS": "30"
}
}
}
}
```
#### .env File (Development)
Create `.env` in project root:
```env
GOOGLE_CHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx
LOG_LEVEL=INFO
LOG_DIR=./logs
LOG_RETENTION_DAYS=30
LOG_ENABLE_CONSOLE=true
```
## Limitations
### Google Chat API Constraints
| Item | Limit | Workaround |
|------|-------|------------|
| **Image Protocol** | HTTPS only | HTTP URLs replaced with text links |
| **Image Size** | Max 5MB | Show as link on validation failure |
| **Image Auth** | Public URLs only | No access if auth required |
| **Content-Type** | `image/*` only | HTML pages rejected |
| **Markdown Support** | Limited | Unsupported syntax approximated |
### Markdown Conversion Limitations
**β
Fully Supported:**
- Headers (H1~H6)
- Bold, italic, inline code
- Ordered/unordered lists (up to 3 levels)
- Code blocks (syntax highlighting)
- Tables (monospace)
- Links, images
**β οΈ Partial Support:**
- Complex nesting β Simplified
- HTML tags β Converted to text
- Blockquotes β Shown as indents
**β Not Supported:**
- Footnotes
- Definition lists
- Math formulas (LaTeX)
- Task checkboxes (`- [ ]`, `- [x]`)
- Emoji shortcodes (`:smile:`, Unicode emojis work)
## FAQ
### Q: Images not displaying
**A**: Image validation failure causes:
1. **HTTPS only** (HTTP not supported)
2. **File size**: Must be under 5MB
3. **Public access**: Must be accessible without auth
4. **Content-Type**: Response header must be `image/*`
**Debug:**
```bash
cat logs/app-YYYY-MM-DD.log | grep "image_validation_failed"
```
### Q: Cards V2 conversion fails
**A**: Use `fallbackToText` option:
```json
{
"markdown": "...",
"fallbackToText": true
}
```
Check logs for details:
```bash
cat logs/errors-YYYY-MM-DD.log
```
### Q: Too many log files
**A**: Adjust with environment variables:
```json
{
"env": {
"LOG_LEVEL": "WARN",
"LOG_RETENTION_DAYS": "7"
}
}
```
### Q: Multiple Google Chat spaces
**A**: Register separate MCP server instances:
```json
{
"mcpServers": {
"google-chat-team-a": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/.../team-a/..."
}
},
"google-chat-team-b": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/.../team-b/..."
}
}
}
}
```
## License
MIT License - [LICENSE](LICENSE)
## Links
- [Model Context Protocol](https://github.com/modelcontextprotocol)
- [Claude Code](https://claude.ai/desktop)
- [Google Chat API](https://developers.google.com/chat)
---
Korean:
[](https://github.com/ice3x2/google-chat-webhook-mcp/actions)
[](https://www.npmjs.com/package/google-chat-webhook-mcp)
[](https://opensource.org/licenses/MIT)
MCP (Model Context Protocol) μλ²λ‘ Google Chat μΉν
μ ν΅ν΄ λ©μμ§λ₯Ό μ μ‘ν©λλ€. Markdownμ Google Chat Cards V2 νμμΌλ‘ μλ λ³ννλ©°, μ΄λ―Έμ§ κ²μ¦, μλ λ‘κΉ
, ν΄λ°± μ²λ¦¬λ₯Ό μ§μν©λλ€.
## μ£Όμ κΈ°λ₯
- π **MCP νλ‘ν μ½ μ§μ**: Claude Code, GitHub Copilot λ±κ³Ό ν΅ν©
- π **Markdown β Cards V2 μλ λ³ν**: ν€λ, 리μ€νΈ, μ½λλΈλ‘, ν, μ΄λ―Έμ§ λ± μ§μ
- πΌοΈ **μ΄λ―Έμ§ URL κ²μ¦**: HEAD μμ²μΌλ‘ μ ν¨μ± νμΈ (HTTP μν, Content-Type, ν¬κΈ°)
- π **μλ ν΄λ°±**: Cards V2 μ€ν¨ μ ν
μ€νΈλ‘ μλ μ ν
- π **ꡬ쑰νλ λ‘κΉ
**: JSON νμ, 30μΌ μλ 보κ΄
- β
**ν
μ€νΈ μλν**: μ€λ
μ· ν
μ€νΈ, ν΅ν© ν
μ€νΈ, CI/CD νμ΄νλΌμΈ
## μ€μΉ
### npm μ€μΉ (κΆμ₯)
```bash
npm install -g google-chat-webhook-mcp
```
### μμ€ μ€μΉ (κ°λ°μ©)
```bash
git clone https://github.com/ice3x2/google-chat-webhook-mcp.git
cd google-chat-webhook-mcp
npm install
npm run build
```
## Google Chat Webhook URL μμ±
MCP μλ² μ€μ μ μ λ¨Όμ Google Chat Webhook URLμ μμ±ν΄μΌ ν©λλ€:
1. Google Chat μ€νμ΄μ€ μ΄κΈ°
2. μλ¨ λ©λ΄ β "μ± λ° ν΅ν©" β "Webhook κ΄λ¦¬"
3. "Webhook μΆκ°" ν΄λ¦
4. μ΄λ¦ μ
λ ₯ ν **URL 볡μ¬**
5. μλ μ€μ μμ μ¬μ©
## MCP ν΄λΌμ΄μΈνΈ μ€μ
### 1. Claude Code
#### μ€μ νμΌ μμΉ
- **Windows**: `%USERPROFILE%\.claude.json`
- **macOS/Linux**: `~/.claude.json`
**μ°Έκ³ **: Claude Desktopμ λ€λ₯Έ κ²½λ‘λ₯Ό μ¬μ©ν©λλ€:
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`
#### npm μ€μΉ μ
```json
{
"mcpServers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
}
}
}
}
```
#### μμ€ μ€μΉ μ
```json
{
"mcpServers": {
"google-chat": {
"command": "node",
"args": ["C:\\path\\to\\google-chat-webhook-mcp\\dist\\index.js"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
}
}
}
}
```
**β οΈ μ£Όμ**: Windows κ²½λ‘λ `\\` λλ `/` μ¬μ© (μ: `C:/path/to/...`)
#### μ€μ μ€μ½ν
Claude Codeλ 3κ°μ§ μ€μ μ€μ½νλ₯Ό μ§μν©λλ€:
1. **User Scope** (μ μ): `~/.claude.json` - λͺ¨λ νλ‘μ νΈμμ μ¬μ© κ°λ₯
2. **Project Scope** (곡μ ): νλ‘μ νΈ λ£¨νΈμ `.mcp.json` - λ²μ κ΄λ¦¬, ν 곡μ κ°λ₯
3. **Local Scope** (κ°μΈ): νλ‘μ νΈλ³ κ°μΈ μ€μ
**μ°μ μμ**: Local > Project > User
#### νλ‘μ νΈ μμ€ μ€μ (.mcp.json)
νκ³Ό 곡μ ν MCP μλ² μ€μ μ νλ‘μ νΈ λ£¨νΈμ `.mcp.json` νμΌμ μμ±νμΈμ:
```json
{
"mcpServers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "${GOOGLE_CHAT_WEBHOOK_URL}"
}
}
}
}
```
**μ₯μ **:
- β
GitμΌλ‘ λ²μ κ΄λ¦¬ κ°λ₯
- β
νκ³Ό μ€μ 곡μ
- β
νκ²½ λ³μ μ§μ: `${VAR}` λλ `${VAR:-κΈ°λ³Έκ°}`
- β
νλ‘μ νΈλ³ MCP μλ² μ€μ
**νκ²½ λ³μ μ€μ **: κ° νμμ΄ μμ μ Webhook URLμ μ€μ ν μ μμ΅λλ€:
```bash
# Linux/macOS
export GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
# Windows (PowerShell)
$env:GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
```
#### μ μ© λ°©λ²
**User μμ€ μ€μ ** (~/.claude.json):
1. `~/.claude.json` νΈμ§ (Windowsλ `%USERPROFILE%\.claude.json`)
2. νμΌ μ μ₯
3. Claude Codeκ° μ€ν μ€μ΄λ©΄ μ¬μμ
**Project μμ€ μ€μ ** (.mcp.json):
1. νλ‘μ νΈ λ£¨νΈμ `.mcp.json` μμ±
2. λ―Όκ°ν λ°μ΄ν°λ νκ²½ λ³μλ‘ μ€μ
3. `.mcp.json`μ λ²μ κ΄λ¦¬ μμ€ν
μ 컀λ°
4. "Send a message to Google Chat" κ°μ λͺ
λ Ή μ¬μ©
### 2. GitHub Copilot (VS Code)
[VS Code GitHub Copilot](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode)μ **μμ΄μ νΈ λͺ¨λ**λ₯Ό ν΅ν΄ MCPλ₯Ό μ§μν©λλ€. μν¬μ€νμ΄μ€ λλ μ¬μ©μ μ€μ μμ MCP μλ²λ₯Ό ꡬμ±ν μ μμ΅λλ€.
#### μ€μ νμΌ μμΉ
λ€μ μ€ νλλ₯Ό μ ν:
- **μ¬μ©μ μ€μ **: `~/.vscode/settings.json` λλ `%APPDATA%\Code\User\settings.json` (Windows)
- **μν¬μ€νμ΄μ€ μ€μ **: νλ‘μ νΈ λ£¨νΈμ `.vscode/settings.json`
- **Claude Code μ€μ ** (μλ κ°μ Έμ€κΈ°): `~/.claude.json`μμ 볡μ¬
#### μ€μ λ°©λ² (mcp.json νμ)
`settings.json`μ μΆκ°:
```json
{
"github.copilot.chat.mcp.servers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
}
}
}
}
```
#### κΈ°λ₯
- **μμ΄μ νΈ λͺ¨λ ν΅ν©**: μμ΄μ νΈ μν¬νλ‘μ°μμ MCP λꡬ μ¬μ© κ°λ₯
- **μΈμ
λ³ λꡬ μ ν**: μΈμ
λ§λ€ νμ±νν λꡬ μ ν κ°λ₯
- **STDIO & SSE μ§μ**: λ μ μ‘ λ°©μ λͺ¨λ μ§μ
- **λλ²κΉ
**: μ¬μμ λͺ
λ Ή λ° μΆλ ₯ λ‘κΉ
λ΄μ₯
#### μμ΄μ νΈ λͺ¨λμμ μ¬μ©νκΈ°
1. VS Codeμμ GitHub Copilot μ±ν
μ΄κΈ°
2. μμ΄μ νΈ λͺ¨λ νμ±ν (κΈ°λ³Έ νμ±νλ κ²½μ°λ μμ)
3. λν μμ - Copilotμ΄ μλμΌλ‘ MCP λꡬμ μ κ·Ό
4. λꡬ μ€ν μ μΉμΈ νμ
**μμ:**
```
@workspace λ°°ν¬ μμ½μ Google Chatμ μ μ‘ν΄μ€
```
**π μ°Έκ³ **: GitHub Copilotμ MCP μ§μμ μμ΄μ νΈ λͺ¨λλ₯Ό ν¬ν¨νμ¬ μ κ΅ν μν¬νλ‘μ°λ₯Ό μ§μν©λλ€. μ΅μ λ²μ μ VS Codeμ GitHub Copilot νμ₯μ μ¬μ©νμΈμ.
### 3. κΈ°ν MCP ν΄λΌμ΄μΈνΈ
MCP νλ‘ν μ½μ μ§μνλ λͺ¨λ ν΄λΌμ΄μΈνΈμμ μ¬μ© κ°λ₯ν©λλ€:
```json
{
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "your-webhook-url"
}
}
1. Google Chat μ€νμ΄μ€ μ΄κΈ°
2. μλ¨ λ©λ΄ β "μ± λ° ν΅ν©" β "Webhook κ΄λ¦¬"
3. "Webhook μΆκ°" ν΄λ¦
4. μ΄λ¦ μ
λ ₯ ν URL 볡μ¬
5. νκ²½ λ³μμ μ€μ
## μ¬μ©λ²
### MCP λꡬ (3κ°μ§)
Claude Codeμ΄λ λ€λ₯Έ MCP ν΄λΌμ΄μΈνΈμμ λ€μ λꡬλ€μ μ¬μ©ν μ μμ΅λλ€:
#### 1. `send_google_chat_text`
κ°λ¨ν ν
μ€νΈ λ©μμ§ μ μ‘
**μμ (Claude Code):**
```
Send "Hello from Claude!" to Google Chat
```
**νλΌλ―Έν°:**
```json
{
"text": "μλ
νμΈμ, Google Chat!"
}
```
#### 2. `send_google_chat_cards_v2`
Cards V2 νμμΌλ‘ μ§μ μ μ‘ (κ³ κΈ μ¬μ©μμ©)
**νλΌλ―Έν°:**
```json
{
"text": "Card Message",
"cardsV2": [
{
"cardId": "unique-card",
"card": {
"header": { "title": "Card Title" },
"sections": [
{
"widgets": [
{ "textParagraph": { "text": "Card content" } }
]
}
]
}
}
]
}
```
#### 3. `send_google_chat_markdown` β **μΆμ²**
Markdownμ Cards V2λ‘ μλ λ³ννμ¬ μ μ‘
**μμ (Claude Code):**
```
Send this markdown to Google Chat:
# Project Update
- Task 1: β
Completed
- Task 2: π§ In Progress
**Deadline**: Tomorrow
```
**νλΌλ―Έν°:**
```json
{
"markdown": "# μ λͺ©\n\n**κ΅΅μ κΈμ¨**μ *κΈ°μΈμ*\n\n- 리μ€νΈ νλͺ© 1\n- 리μ€νΈ νλͺ© 2\n\n```python\nprint('Hello')\n```",
"cardTitle": "λ§ν¬λ€μ΄ λ©μμ§",
"fallbackToText": true
}
```
**μ΅μ
:**
- `cardTitle`: μΉ΄λ μλ¨μ νμλ μ λͺ© (μ ν)
- `fallbackToText`: λ³ν μ€ν¨ μ ν
μ€νΈλ‘ μλ μ μ‘ (κΈ°λ³Έκ°: false)
### Claude Code μ¬μ© μμ
μ€μ μλ£ ν Claudeμ μμ°μ΄λ‘ λννλ©΄ μλμΌλ‘ MCP λꡬλ₯Ό μ¬μ©ν©λλ€:
**π€ μ¬μ©μ:**
> "Google Chatμ νλ‘μ νΈ μν μ
λ°μ΄νΈλ₯Ό 보λ΄μ€. μλ£λ μμ
3κ°, μ§ν μ€μΈ μμ
2κ°λ₯Ό λ§ν¬λ€μ΄ 리μ€νΈλ‘ μμ±ν΄μ."
**π€ Claude:**
> (μλμΌλ‘ `send_google_chat_markdown` λꡬ νΈμΆ)
>
> Google Chatμ λ©μμ§λ₯Ό μ μ‘νμ΅λλ€. νλ‘μ νΈ μνκ° μ
λ°μ΄νΈλμμ΅λλ€.
**π€ μ¬μ©μ:**
> "λ°©κΈ λ³΄λΈ λ©μμ§μ μ½λ μμ λ μΆκ°ν΄μ€."
**π€ Claude:**
> (λ€μ MarkdownμΌλ‘ λ©μμ§ μμ± λ° μ μ‘)
### μ§μνλ Markdown λ¬Έλ²
Claudeλ MCP ν΄λΌμ΄μΈνΈμμ MarkdownμΌλ‘ λ©μμ§λ₯Ό μμ±νλ©΄ μλμΌλ‘ Google Chat Cards V2λ‘ λ³νλ©λλ€.
| λ¬Έλ² | Markdown μμ | Google Chat λ λλ§ |
|------|---------------|-------------------|
| **ν€λ** | `# H1`, `## H2`, `### H3` | κ΅΅μ κΈμ¨ + ν¬κΈ° μ°¨λ± |
| **κ΅΅κ²** | `**bold**` λλ `__bold__` | **bold** |
| **κΈ°μΈμ** | `*italic*` λλ `_italic_` | *italic* |
| **μΈλΌμΈ μ½λ** | `` `code` `` | `code` (κ³ μ ν ν°νΈ) |
| **μ½λλΈλ‘** | ` ```python\ncode\n``` ` | ꡬ문 κ°μ‘° λ°μ€ |
| **μμ 리μ€νΈ** | `1. First\n2. Second` | 1. First<br>2. Second |
| **λΉμμ 리μ€νΈ** | `- Item` λλ `* Item` | β’ Item |
| **μ€μ²© 리μ€νΈ** | ` - nested` (2μΉΈ λ€μ¬μ°κΈ°) | γβ’ nested (Em space) |
| **ν** | `\| A \| B \|\n\|--\|--\|` | κ³ μ ν ν°νΈ ν |
| **μ΄λ―Έμ§** | `` | μ΄λ―Έμ§ μμ ― (URL κ²μ¦ ν) |
| **λ§ν¬** | `[ν
μ€νΈ](https://...)` | ν΄λ¦ κ°λ₯ν λ§ν¬ |
| **μνμ ** | `---` λλ `***` | ꡬλΆμ |
| **μΈμ©λ¬Έ** | `> quote` | λ€μ¬μ°κΈ° + νμ ν
μ€νΈ |
**μμ Markdown:**
```markdown
# νλ‘μ νΈ λ°°ν¬ μλ£ π
## μ£Όμ λ³κ²½μ¬ν
- **μ±λ₯ κ°μ **: API μλ΅ μλ 30% ν₯μ
- **λ²κ·Έ μμ **: λ‘κ·ΈμΈ μ€λ₯ ν΄κ²°
- μ κΈ°λ₯ μΆκ°
## λ°°ν¬ μν
| νκ²½ | μν | λ²μ |
|------|------|------|
| Production | β
| v2.1.0 |
| Staging | β
| v2.1.0 |
## λ€μ λ¨κ³
1. λͺ¨λν°λ§ 24μκ°
2. μ¬μ©μ νΌλλ°± μμ§
3. λ€μ μ€νλ¦°νΈ κ³ν
μ½λ μμ :
```python
def deploy():
print("Deploying v2.1.0...")
return True
```
μμΈν λ΄μ©μ [λ¬Έμ](https://docs.example.com)λ₯Ό μ°Έμ‘°νμΈμ.
```
**λ³ν κ²°κ³Ό:** Google Chatμμ ν€λ, 리μ€νΈ, ν, μ½λλΈλ‘μ΄ λͺ¨λ μκ°μ μΌλ‘ ꡬλΆλμ΄ νμλ©λλ€.
## νκ²½ λ³μ
### νμ νκ²½ λ³μ
| λ³μλͺ
| μ€λͺ
| μμ |
|--------|------|------|
| `GOOGLE_CHAT_WEBHOOK_URL` | Google Chat Webhook URL | `https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx` |
### μ ν νκ²½ λ³μ (λ‘κΉ
)
| λ³μλͺ
| μ€λͺ
| κΈ°λ³Έκ° | νμ©κ° |
|--------|------|--------|--------|
| `LOG_LEVEL` | λ‘κ·Έ λ 벨 | `INFO` | `DEBUG`, `INFO`, `WARN`, `ERROR` |
| `LOG_DIR` | λ‘κ·Έ λλ ν 리 κ²½λ‘ | `./logs` | μ λ/μλ κ²½λ‘ |
| `LOG_RETENTION_DAYS` | λ‘κ·Έ λ³΄κ΄ μΌμ | `30` | μ«μ (μΌ) |
| `LOG_ENABLE_CONSOLE` | μ½μ μΆλ ₯ μ¬λΆ | `true` | `true`, `false` |
### μ€μ λ°©λ²
#### Claude Code (~/.claude.json)
```json
{
"mcpServers": {
"google-chat": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx",
"LOG_LEVEL": "INFO",
"LOG_RETENTION_DAYS": "30"
}
}
}
}
```
#### .env νμΌ (κ°λ°μ©)
νλ‘μ νΈ λ£¨νΈμ `.env` νμΌ μμ±:
```env
GOOGLE_CHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx
LOG_LEVEL=INFO
LOG_DIR=./logs
LOG_RETENTION_DAYS=30
LOG_ENABLE_CONSOLE=true
```
#### μμ€ν
νκ²½ λ³μ
**Windows (PowerShell):**
```powershell
$env:GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
```
**Linux/macOS (Bash/Zsh):**
```bash
export GOOGLE_CHAT_WEBHOOK_URL="https://chat.googleapis.com/v1/spaces/xxx/messages?key=xxx&token=xxx"
```
## κ°λ°
```bash
# μμ‘΄μ± μ€μΉ
npm install
# λΉλ
npm run build
# κ°λ° λͺ¨λ (TypeScript μ§μ μ€ν)
npm run dev
# Lint κ²μ¬
npm run lint
# Lint μλ μμ
npm run lint:fix
# ν
μ€νΈ
npm run test:snapshot # μ€λ
μ· ν
μ€νΈ (12κ°)
npm run test:logging # λ‘κΉ
μμ€ν
ν
μ€νΈ
npm run test:integration # ν΅ν© ν
μ€νΈ (μΉν
νμ)
npm test # μ 체 ν
μ€νΈ
```
## μν€ν
μ²
```
src/
βββ index.ts # μ§μ
μ
βββ server.ts # MCP μλ² μ€μ
βββ tools/ # MCP λꡬ
β βββ sendTextMessage.ts # ν
μ€νΈ μ μ‘
β βββ sendCardsV2Message.ts # Cards V2 μ μ‘
β βββ sendMarkdownMessage.ts # Markdown μ μ‘ (λ©μΈ)
β βββ markdownToCards.ts # Markdown β Cards V2 λ³ν
βββ utils/ # μ νΈλ¦¬ν°
β βββ imageValidator.ts # μ΄λ―Έμ§ URL κ²μ¦
β βββ cardsV2Validator.ts # Cards V2 μ€ν€λ§ κ²μ¦
β βββ logger.ts # λ‘κΉ
μμ€ν
β βββ logCleaner.ts # λ‘κ·Έ μ 리
βββ types/ # νμ
μ μ
βββ markdown.ts
βββ googleChat.ts
βββ log.ts
```
## λ‘κΉ
### λ‘κ·Έ νμΌ κ΅¬μ‘°
```
logs/
βββ app-2025-10-29.log # μΌλ³ λ‘κ·Έ (λͺ¨λ λ 벨)
βββ errors-2025-10-29.log # μλ¬ μ μ© λ‘κ·Έ
βββ ... # 30μΌ μλ μμ
```
### λ‘κ·Έ ν¬λ§· (JSON)
```json
{
"timestamp": "2025-10-29T12:34:56.789Z",
"level": "INFO",
"module": "sendMarkdownMessage",
"event": "message_sent",
"messageId": "spaces/xxx/messages/yyy",
"elapsed": 123,
"usedFallback": false,
"cardTitle": "Test Card"
}
```
### λ‘κ·Έ μ΄λ²€νΈ
- `message_sent`: λ©μμ§ μ μ‘ μ±κ³΅
- `fallback_used`: ν΄λ°± μ¬μ© (Cards V2 β Text)
- `image_validation_failed`: μ΄λ―Έμ§ κ²μ¦ μ€ν¨
- `send_failed`: μ μ‘ μ€ν¨
- `validation_failed`: κ²μ¦ μ€ν¨
### λ‘κ·Έ μ 리
- μλ² μμ μ μλ μ 리 (30μΌ μ΄μ λ‘κ·Έ μμ )
- 24μκ°λ§λ€ μλ μ€ν
- νκ²½ λ³μ `LOG_RETENTION_DAYS`λ‘ μ€μ κ°λ₯
## μ νμ¬ν
### Google Chat API μ μ½μ¬ν
| νλͺ© | μ ν | λμ λ°©λ² |
|------|------|-----------|
| **μ΄λ―Έμ§ νλ‘ν μ½** | HTTPSλ§ μ§μ | HTTP URLμ ν
μ€νΈ λ§ν¬λ‘ λ체 |
| **μ΄λ―Έμ§ ν¬κΈ°** | μ΅λ 5MB | κ²μ¦ μ€ν¨ μ λ§ν¬λ‘ νμ |
| **μ΄λ―Έμ§ μΈμ¦** | κ³΅κ° URLλ§ κ°λ₯ | μΈμ¦ νμ μ μ κ·Ό λΆκ° |
| **Content-Type** | `image/*`λ§ νμ© | HTML νμ΄μ§ λ±μ κ±°λΆ |
| **Markdown μ§μ** | μ νμ | λ―Έμ§μ λ¬Έλ²μ κ·Όμ¬μΉλ‘ λ³ν |
### Markdown λ³ν μ μ½μ¬ν
**β
μμ μ§μ:**
- ν€λ (H1~H6)
- κ΅΅κ², κΈ°μΈμ, μΈλΌμΈ μ½λ
- μμ/λΉμμ 리μ€νΈ (μ΅λ 3λ¨κ³ μ€μ²©)
- μ½λλΈλ‘ (ꡬ문 κ°μ‘°)
- ν (κ³ μ ν ν°νΈ)
- λ§ν¬, μ΄λ―Έμ§
**β οΈ λΆλΆ μ§μ:**
- 볡μ‘ν μ€μ²© ꡬ쑰 β λ¨μνλ¨
- HTML νκ·Έ β ν
μ€νΈλ‘ λ³ν
- μΈμ©λ¬Έ β λ€μ¬μ°κΈ°λ‘ νν
**β λ―Έμ§μ:**
- κ°μ£Ό (footnotes)
- μ μ 리μ€νΈ (definition lists)
- μν μμ (LaTeX)
- μμ
체ν¬λ°μ€ (`- [ ]`, `- [x]`)
- Emoji λ¨μΆμ½λ (`:smile:` λ±, μ λμ½λ μ΄λͺ¨μ§λ κ°λ₯)
### μ±λ₯ λ° μ ν
- **μ΄λ―Έμ§ κ²μ¦ νμμμ**: 5μ΄
- **Webhook μμ² νμμμ**: 5μ΄
- **λ‘κ·Έ νμΌ ν¬κΈ°**: 무μ ν (30μΌ μλ μμ )
- **λμ μμ²**: μ ν μμ (Google Chat API μ ν μ€μ)
### 보μ κ³ λ €μ¬ν
β οΈ **Webhook URLμ λ―Όκ° μ 보μ
λλ€:**
- Gitμ 컀λ°νμ§ λ§μΈμ
- κ³΅κ° μ μ₯μμ λ
ΈμΆ κΈμ§
- μ κΈ°μ μΌλ‘ μ¬μμ± κΆμ₯
- `.env` νμΌμ `.gitignore`μ ν¬ν¨ νμ
## FAQ
### Q: μ΄λ―Έμ§κ° νμλμ§ μμ΅λλ€
**A**: μ΄λ―Έμ§ URL κ²μ¦ μ€ν¨ μμΈ:
1. **HTTPSλ§ μ§μ** (HTTPλ λΆκ°)
2. **νμΌ ν¬κΈ°**: 5MB μ΄νμ¬μΌ ν¨
3. **κ³΅κ° μ κ·Ό**: μΈμ¦ μμ΄ μ κ·Ό κ°λ₯ν URLμ΄μ΄μΌ ν¨
4. **Content-Type**: μλ΅ ν€λκ° `image/*`μ¬μΌ ν¨
**λλ²κΉ
:**
```bash
# λ‘κ·Έ νμΈ
cat logs/app-YYYY-MM-DD.log | grep "image_validation_failed"
```
**κ²μ¦ μ€ν¨ μ λμ:**
- μ΄λ―Έμ§λ ν
μ€νΈ λ§ν¬λ‘ λ체λ¨
- μ: `β οΈ μ΄λ―Έμ§ λ‘λ μ€ν¨: https://... (HTTP 404: Not Found)`
### Q: Cards V2 λ³νμ΄ μ€ν¨ν©λλ€
**A**: λ€μμ νμΈνμΈμ:
1. **fallbackToText μ΅μ
μ¬μ©**:
```json
{
"markdown": "...",
"fallbackToText": true
}
```
λ³ν μ€ν¨ μ μλμΌλ‘ ν
μ€νΈλ‘ μ μ‘λ©λλ€.
2. **λ‘κ·Έμμ μμΈ νμΈ**:
```bash
cat logs/errors-YYYY-MM-DD.log
```
3. **μ§μνμ§ μλ Markdown λ¬Έλ²**:
- κ°μ£Ό (footnotes)
- μ μ 리μ€νΈ (definition lists)
- 볡μ‘ν HTML νκ·Έ
### Q: λ‘κ·Έ νμΌμ΄ λ무 λ§μ΄ μμ
λλ€
**A**: νκ²½ λ³μλ‘ μ‘°μ :
```json
{
"env": {
"LOG_LEVEL": "WARN",
"LOG_RETENTION_DAYS": "7"
}
}
```
- `LOG_LEVEL=ERROR`: μλ¬λ§ κΈ°λ‘
- `LOG_RETENTION_DAYS=7`: 7μΌλ§ 보κ΄
- `LOG_ENABLE_CONSOLE=false`: μ½μ μΆλ ₯ λΉνμ±ν
### Q: npx μ€ν μ "command not found" μ€λ₯
**A**: Node.jsμ npmμ΄ μ€μΉλμ΄ μλμ§ νμΈ:
```bash
node --version # v18.0.0 μ΄μ κΆμ₯
npm --version
```
μ€μΉλμ§ μμλ€λ©΄:
- **Windows**: https://nodejs.org/ μμ λ€μ΄λ‘λ
- **macOS**: `brew install node`
- **Linux**: `sudo apt install nodejs npm` (Ubuntu/Debian)
### Q: Webhook URLμ μ΄λ»κ² μμ νκ² κ΄λ¦¬νλμ?
**A**:
1. **νκ²½ λ³μ μ¬μ©** (μ€μ νμΌμ μ§μ μ°μ§ λ§μΈμ)
2. **Gitμ 컀λ°νμ§ λ§μΈμ** (.gitignore νμΈ)
3. **μ κΈ°μ μΌλ‘ μ¬μμ±** (μ μΆ μμ¬ μ)
4. **Google Chatμμ Webhook μμ **λ‘ λ¬΄ν¨ν κ°λ₯
### Q: μ¬λ¬ Google Chat μ€νμ΄μ€μ λ©μμ§λ₯Ό 보λ΄κ³ μΆμ΅λλ€
**A**: κ° μ€νμ΄μ€λ§λ€ λ€λ₯Έ MCP μλ² μΈμ€ν΄μ€ λ±λ‘:
```json
{
"mcpServers": {
"google-chat-team-a": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/.../team-a/..."
}
},
"google-chat-team-b": {
"command": "npx",
"args": ["-y", "google-chat-webhook-mcp"],
"env": {
"GOOGLE_CHAT_WEBHOOK_URL": "https://chat.googleapis.com/.../team-b/..."
}
}
}
}
```
Claudeμμ "Send to team-a" λλ "Send to team-b"λ‘ κ΅¬λΆνμ¬ μ¬μ©ν μ μμ΅λλ€.
## CI/CD
GitHub Actionsλ‘ μλν:
- β
Node.js 18.x, 20.x λ§€νΈλ¦μ€ λΉλ
- β
ESLint, λΉλ, ν
μ€νΈ
- β
μ€λ
μ· ν
μ€νΈ (12κ°)
- β
ν΅ν© ν
μ€νΈ (master λΈλμΉ)
μν¬νλ‘μ°: [`.github/workflows/ci.yml`](.github/workflows/ci.yml)
## κΈ°μ¬
μ΄μμ PRμ νμν©λλ€!
## λΌμ΄μ μ€
MIT License - [LICENSE](LICENSE)
## λ¬Έμ
- [λ‘κΉ
μ€κ³](docs/logging-design.md)
- [CI μ€μ κ°μ΄λ](docs/ci-setup.md)
- [Markdown ꡬν κ³ν](docs/markdown-to-cards-implementation.md)
## κ΄λ ¨ νλ‘μ νΈ
- [Model Context Protocol](https://github.com/modelcontextprotocol)
- [Claude Code](https://claude.ai/desktop)
- [Google Chat API](https://developers.google.com/chat)