Skip to main content
Glama

Claude Code DingTalk MCP Server

by sfyyy
index.ts14 kB
#!/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 { DingTalkClient, DingTalkConfig } from './dingtalk.js'; import { execSync } from 'child_process'; class DingTalkMCPServer { private server: Server; private dingTalkClient: DingTalkClient | null = null; constructor() { this.server = new Server( { name: 'dingtalk-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.setupHandlers(); this.initializeFromEnv(); } private getGitUsername(): string { try { const username = execSync('git config --get user.name', { encoding: 'utf8' }).trim(); return username || 'Unknown User'; } catch (error) { return 'Unknown User'; } } private initializeFromEnv() { const webhook = process.env.DINGTALK_WEBHOOK; const secret = process.env.DINGTALK_SECRET; const keywords = process.env.DINGTALK_KEYWORDS; if (webhook) { const config: DingTalkConfig = { webhook, secret, keywords: keywords ? keywords.split(',').map(k => k.trim()) : undefined }; this.dingTalkClient = new DingTalkClient(config); console.error('✅ DingTalk client initialized from environment variables'); } } private setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'dingtalk_configure', description: 'Configure DingTalk webhook settings', inputSchema: { type: 'object', properties: { webhook: { type: 'string', description: 'DingTalk webhook URL with access token', }, secret: { type: 'string', description: 'Optional secret for signature verification', }, keywords: { type: 'array', items: { type: 'string' }, description: 'Optional keywords for security validation', }, }, required: ['webhook'], }, }, { name: 'dingtalk_send_text', description: 'Send a text message to DingTalk group', inputSchema: { type: 'object', properties: { content: { type: 'string', description: 'Text content to send', }, atAll: { type: 'boolean', description: 'Whether to @all members', default: false, }, }, required: ['content'], }, }, { name: 'dingtalk_send_markdown', description: 'Send a markdown message to DingTalk group', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Message title', }, text: { type: 'string', description: 'Markdown formatted text content', }, atAll: { type: 'boolean', description: 'Whether to @all members', default: false, }, }, required: ['title', 'text'], }, }, { name: 'dingtalk_send_link', description: 'Send a link message to DingTalk group', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Link title', }, text: { type: 'string', description: 'Link description text', }, messageUrl: { type: 'string', description: 'Target URL', }, picUrl: { type: 'string', description: 'Optional image URL', }, }, required: ['title', 'text', 'messageUrl'], }, }, { name: 'dingtalk_notify_session_end', description: 'Send a session completion notification with automatic stats', inputSchema: { type: 'object', properties: { sessionType: { type: 'string', description: 'Type of session (e.g., "开发协助", "代码审查", "问题解决")', default: '开发协助' }, duration: { type: 'string', description: 'Session duration (e.g., "30分钟", "1小时20分")', }, mainTasks: { type: 'array', items: { type: 'string' }, description: 'List of main tasks completed in this session', }, summary: { type: 'string', description: 'Brief summary of the session', default: '会话已完成' }, filesCount: { type: 'number', description: 'Number of files modified/created', default: 0 }, toolsUsed: { type: 'number', description: 'Number of tools/commands used', default: 0 }, atAll: { type: 'boolean', description: 'Whether to @all members', default: false } }, required: [] }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'dingtalk_configure': return await this.handleConfigure(args as unknown as DingTalkConfig); case 'dingtalk_send_text': return await this.handleSendText(args as unknown as { content: string; atAll?: boolean }); case 'dingtalk_send_markdown': return await this.handleSendMarkdown(args as unknown as { title: string; text: string; atAll?: boolean }); case 'dingtalk_send_link': return await this.handleSendLink(args as unknown as { title: string; text: string; messageUrl: string; picUrl?: string }); case 'dingtalk_notify_session_end': return await this.handleNotifySessionEnd(args as unknown as { sessionType?: string; duration?: string; mainTasks?: string[]; summary?: string; filesCount?: number; toolsUsed?: number; atAll?: boolean; }); case 'dingtalk_notify_task_complete': return await this.handleNotifyTaskComplete(args as unknown as { taskName: string; status: 'success' | 'failed' | 'warning'; details?: string; duration?: string; atAll?: boolean; }); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }); } private async handleConfigure(config: DingTalkConfig) { this.dingTalkClient = new DingTalkClient(config); return { content: [ { type: 'text', text: '✅ DingTalk client configured successfully', }, ], }; } private async handleSendText(args: { content: string; atAll?: boolean }) { if (!this.dingTalkClient) { throw new Error('DingTalk client not configured. Use dingtalk_configure first or set environment variables (DINGTALK_WEBHOOK, DINGTALK_SECRET).'); } const gitUsername = this.getGitUsername(); const contentWithUser = `${args.content}\n\n---\n👤 发送者: ${gitUsername}`; const success = await this.dingTalkClient.sendText(contentWithUser, args.atAll); return { content: [ { type: 'text', text: success ? '✅ Text message sent successfully' : '❌ Failed to send text message', }, ], }; } private async handleSendMarkdown(args: { title: string; text: string; atAll?: boolean }) { if (!this.dingTalkClient) { throw new Error('DingTalk client not configured. Use dingtalk_configure first or set environment variables (DINGTALK_WEBHOOK, DINGTALK_SECRET).'); } const gitUsername = this.getGitUsername(); const textWithUser = `${args.text}\n\n---\n👤 **发送者:** ${gitUsername}`; const success = await this.dingTalkClient.sendMarkdown(args.title, textWithUser, args.atAll); return { content: [ { type: 'text', text: success ? '✅ Markdown message sent successfully' : '❌ Failed to send markdown message', }, ], }; } private async handleSendLink(args: { title: string; text: string; messageUrl: string; picUrl?: string }) { if (!this.dingTalkClient) { throw new Error('DingTalk client not configured. Use dingtalk_configure first or set environment variables (DINGTALK_WEBHOOK, DINGTALK_SECRET).'); } const success = await this.dingTalkClient.sendLink(args.title, args.text, args.messageUrl, args.picUrl); return { content: [ { type: 'text', text: success ? '✅ Link message sent successfully' : '❌ Failed to send link message', }, ], }; } private async handleNotifySessionEnd(args: { sessionType?: string; duration?: string; mainTasks?: string[]; summary?: string; filesCount?: number; toolsUsed?: number; atAll?: boolean; }) { if (!this.dingTalkClient) { throw new Error('DingTalk client not configured. Use dingtalk_configure first or set environment variables (DINGTALK_WEBHOOK, DINGTALK_SECRET).'); } const sessionType = args.sessionType || '开发协助'; const duration = args.duration || '刚刚完成'; const mainTasks = args.mainTasks || []; const summary = args.summary || '会话已完成'; const filesCount = args.filesCount || 0; const toolsUsed = args.toolsUsed || 0; const now = new Date(); const gitUsername = this.getGitUsername(); const title = `🤖 Claude Code ${sessionType}完成`; let content = `## 🤖 Claude Code ${sessionType}完成 **完成时间:** ${now.toLocaleString('zh-CN')} **会话时长:** ${duration} **操作者:** ${gitUsername} ### 📋 本次会话 ${summary}`; if (mainTasks.length > 0) { content += ` ### ✅ 主要任务 ${mainTasks.map(task => `- ${task.trim()}`).join('\n')}`; } content += ` ### 📊 操作统计 - **文件操作:** ${filesCount} 个 - **工具使用:** ${toolsUsed} 次 --- *Claude Code 自动通知 | ${now.toLocaleDateString('zh-CN')}*`; const success = await this.dingTalkClient.sendMarkdown(title, content, args.atAll); return { content: [ { type: 'text', text: success ? '✅ Session completion notification sent successfully' : '❌ Failed to send session completion notification', }, ], }; } private async handleNotifyTaskComplete(args: { taskName: string; status: 'success' | 'failed' | 'warning'; details?: string; duration?: string; atAll?: boolean; }) { if (!this.dingTalkClient) { throw new Error('DingTalk client not configured. Use dingtalk_configure first or set environment variables (DINGTALK_WEBHOOK, DINGTALK_SECRET).'); } const statusEmoji = { success: '✅', failed: '❌', warning: '⚠️' }; const statusText = { success: '任务完成', failed: '任务失败', warning: '任务警告' }; const timestamp = new Date().toLocaleString('zh-CN'); const gitUsername = this.getGitUsername(); let markdownText = `## ${statusEmoji[args.status]} ${statusText[args.status]} **任务名称:** ${args.taskName} **状态:** ${statusText[args.status]} **时间:** ${timestamp} **操作者:** ${gitUsername}`; if (args.duration) { markdownText += `\n**耗时:** ${args.duration}`; } if (args.details) { markdownText += `\n\n**详情:**\n${args.details}`; } markdownText += '\n\n---\n*来自 Claude Code MCP Server*'; const success = await this.dingTalkClient.sendMarkdown( `${statusEmoji[args.status]} ${args.taskName} - ${statusText[args.status]}`, markdownText, args.atAll ); return { content: [ { type: 'text', text: success ? '✅ Task completion notification sent successfully' : '❌ Failed to send task completion notification', }, ], }; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('DingTalk MCP server running on stdio'); } } const server = new DingTalkMCPServer(); server.run().catch(console.error);

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/sfyyy/claude-code-dingtalk-mcp'

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