send_google_chat_markdown
Send formatted messages to Google Chat by converting Markdown to Cards V2 format with automatic fallback to plain text when needed.
Instructions
Convert Markdown to Cards V2 and send to configured Google Chat webhook. Falls back to text on failure.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| markdown | Yes | ||
| cardTitle | No | ||
| fallbackToText | No |
Implementation Reference
- src/server.ts:123-131 (handler)The MCP tool handler function that receives tool parameters, calls the sendMarkdownMessage helper, and returns structured MCP response or error.const sendMarkdownHandler = (async ({ markdown, cardTitle, fallbackToText }: { markdown: string; cardTitle?: string; fallbackToText?: boolean }) => { try { const result = await sendMarkdownMessage(webhook || '', { markdown, cardTitle, fallbackToText }); return { content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result }; } catch (err: unknown) { const e = err as Error; return { content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true }; } }) as any;
- src/server.ts:138-139 (schema)Zod schemas defining the input parameters (markdown required, cardTitle and fallbackToText optional) and output structure (success, usedFallback, messageId).inputSchema: ( { markdown: z.string(), cardTitle: z.string().optional(), fallbackToText: z.boolean().optional() } as unknown ) as any, outputSchema: ( { success: z.boolean(), usedFallback: z.boolean().optional(), messageId: z.string().optional() } as unknown ) as any
- src/server.ts:133-142 (registration)MCP server registration of the 'send_google_chat_markdown' tool with title, description, schemas, and handler reference.server.registerTool( 'send_google_chat_markdown', { title: 'Send Google Chat Markdown', description: 'Convert Markdown to Cards V2 and send to configured Google Chat webhook. Falls back to text on failure.', inputSchema: ( { markdown: z.string(), cardTitle: z.string().optional(), fallbackToText: z.boolean().optional() } as unknown ) as any, outputSchema: ( { success: z.boolean(), usedFallback: z.boolean().optional(), messageId: z.string().optional() } as unknown ) as any }, sendMarkdownHandler );
- The core implementation that converts Markdown to Google Chat Cards V2, validates the cards, sends the message, and falls back to plain text if Cards V2 fails.export async function sendMarkdownMessage(webhookUrl: string, options: SendMarkdownOptions): Promise<SendMarkdownResult> { const { markdown, cardTitle, fallbackToText = true } = options; const startTime = Date.now(); // Phase0: 시도형 스텁 - 변환 후 바로 sendCardsV2Message 호출 try { const cardsV2 = await markdownToCardsV2(markdown, cardTitle); // validate before sending try { validateCardsV2(cardsV2); } catch (valErr) { logger.warn('sendMarkdownMessage', 'validation_failed', { error: String(valErr), cardTitle, }); throw new Error(`Cards V2 validation failed: ${valErr}`); } const response = await sendCardsV2Message({ text: cardTitle || 'Markdown Message', cardsV2 }, webhookUrl); const elapsed = Date.now() - startTime; logger.info('sendMarkdownMessage', 'message_sent', { messageId: (response && response.name) || 'unknown', elapsed, usedFallback: false, cardTitle, }); return { success: true, messageId: (response && response.name) || undefined, usedFallback: false }; } catch (err: any) { if (fallbackToText) { // 폴백: 원본 Markdown 그대로 텍스트 전송 try { const r = await sendTextMessage({ text: markdown }, webhookUrl); const elapsed = Date.now() - startTime; logger.warn('sendMarkdownMessage', 'fallback_used', { messageId: (r && r.name) || 'unknown', reason: String(err), elapsed, }); return { success: true, messageId: r && r.name, usedFallback: true, fallbackReason: String(err) }; } catch (sendErr: any) { logger.error('sendMarkdownMessage', 'send_failed', { error: String(sendErr), cardTitle, markdown: markdown.substring(0, 100), // 처음 100자만 로깅 }); return { success: false, error: String(sendErr) }; } } logger.error('sendMarkdownMessage', 'send_failed', { error: String(err), cardTitle, markdown: markdown.substring(0, 100), // 처음 100자만 로깅 }); return { success: false, error: String(err) }; } }