AgentSkillsCN

mcp-discord-notify

指导Discord通知MCP服务器的开发与扩展。适用于mcp-discord-notify代码库的开发工作:添加工具、新增问题类型、调整配置,或修复Bug。

SKILL.md
--- frontmatter
name: mcp-discord-notify
description: Guides development and extension of the Discord notifications MCP server. Use when working on the mcp-discord-notify codebase: adding tools, new question types, changing config, or fixing bugs.

mcp-discord-notify — Development

This project is an MCP server that exposes Discord (notifications, channel creation, questions with responses) to AI agents. Use this skill when modifying or extending the mcp-discord-notify server.

Architecture

FileRole
main.pyMCP entrypoint (FastMCP, stdio). Defines tools; each tool uses run_async() to call into the Discord bot. Starts the bot at import time.
discord_bot.pyDiscord client in a background thread with its own event loop. Handles Gateway, sends messages, creates channels, and handles interactions (buttons, modal). Exposes run_async(coro), start_bot(token), Views, and register_pending_question(question_id) for two-way questions.
config.pyLoads DISCORD_BOT_TOKEN / DISCORD_TOKEN, optional DISCORD_GUILD_ID, and channel map from env and/or channels.json. save_channel_map() persists keys for create_channel(..., register_key=...).
  • Transport: stdio (JSON-RPC). Client spawns python main.py and talks over stdin/stdout.
  • Threading: Main thread runs MCP stdio; Discord bot runs in a daemon thread. Tools are sync and call run_async(coro, timeout) to run coroutines on the Discord loop and block for the result.
  • Two-way questions: send_question registers a question_idqueue.Queue, posts a message with a View (buttons/modal). When a user interacts, the View callback puts the result in the queue; the tool blocks on q.get(timeout=...).

Adding a new MCP tool

  1. In main.py, add a @mcp.tool() function. Use clear docstring (name, args, behavior).
  2. Resolve channel with _resolve_channel(channel_id_or_key) when the tool targets a channel.
  3. Implement the side effect in an async def _do_it() and call run_async(_do_it(), timeout=...). Use _get_client() from discord_bot to get the Discord client and call client.get_channel(cid), guild.create_text_channel(...), etc.
  4. Return a JSON-serializable result (or json.dumps({...}) for consistency with existing tools).

Adding a new question type

  1. In discord_bot.py, add a new ui.View subclass (e.g. MyQuestionView(question_id, ..., timeout_sec=...)). Add buttons or a button that opens a modal; use custom_id or pass question_id so the callback can find _pending_questions[question_id] and q.put({...}).
  2. Important: Respond to the interaction within 3 seconds. Use await interaction.response.defer() at the start of the callback, then put the result in the queue.
  3. In main.py, in send_question, add a branch for the new question_type, instantiate your View, call register_pending_question(question_id) before posting, post the message with view=view, then q.get(timeout=wait_timeout_seconds) if waiting.

Config and channel map

  • Token: Required. DISCORD_BOT_TOKEN or DISCORD_TOKEN (see config.py).
  • Channel map: Optional. Keys (e.g. general, alerts) → channel IDs. Loaded from DISCORD_CHANNELS env (JSON string) and/or channels.json in DISCORD_MCP_CONFIG_DIR (default: current dir). Agents use keys in send_notification(channel_id_or_key, ...) and send_question(...).
  • Persisting new channels: create_channel(..., register_key="foo") writes the new channel ID into the in-memory map and calls save_channel_map() so channels.json is updated.

Conventions

  • Keep tools sync; use run_async() for any Discord API call. The Discord loop runs in another thread.
  • Use question_id (e.g. uuid.uuid4()) to tie a posted question to its queue; register before sending, unregister on timeout or after first response.
  • Tool return values: prefer json.dumps({"error": "..."}) for errors and json.dumps({...}) for success so the client gets consistent JSON.
  • Views must defer or respond to interactions within 3 seconds; then do any slow work or queue put.

Testing without a real bot

  • Import and unit-test config (load_channel_map, get_token with env set) and discord_bot Views in isolation (no start_bot). Full integration requires a valid DISCORD_BOT_TOKEN and a guild/channel the bot can access.

Quick reference

ChangeWhere
New toolmain.py: @mcp.tool() + run_async(_impl())
New question typediscord_bot.py: new View; main.py: branch in send_question
New config envconfig.py: add getter; document in README and .env.example
Channel map file pathconfig.py: CHANNELS_FILE, DISCORD_MCP_CONFIG_DIR