ask-a-human 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ayub Dahir
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # ask-a-human
2
+
3
+ An MCP server that lets Claude Code pause and ask you a question on Discord. You reply in a thread, and Claude continues with your answer.
4
+
5
+ **Roadmap:**
6
+ - Slack and Telegram support — March 3, 2026
7
+ - Support for other coding agents and harnesses — March 6, 2026
8
+
9
+ ## Why
10
+
11
+ This isn't a chat interface for Claude Code. It's an escape hatch for fully autonomous workflows.
12
+
13
+ When you let Claude Code run on its own — refactoring a codebase, building a feature end-to-end, debugging a complex issue — it will occasionally hit a fork in the road: a trade-off you didn't anticipate, a design decision with no obvious right answer, or a critical juncture where moving forward without your input could mean wasted work. In those moments, Claude can choose to reach out to you on Discord, get your steer, and continue — rather than guessing wrong or stopping entirely.
14
+
15
+ The goal is to let you stay away from the terminal while Claude works, and only get pulled in when it genuinely matters.
16
+
17
+ ## How it works
18
+
19
+ 1. Claude Code calls the `ask_human` tool with a question
20
+ 2. The bot posts the question to your Discord channel and @mentions you
21
+ 3. A thread is created for the question
22
+ 4. Claude Code waits until you reply in the thread
23
+ 5. Your reply is returned to Claude and it continues working
24
+
25
+ ## Setup
26
+
27
+ ### 1. Create a Discord bot
28
+
29
+ 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
30
+ 2. Click **New Application** and give it a name
31
+ 3. Go to **Bot** in the sidebar
32
+ 4. Click **Reset Token** and copy it — this is your `DISCORD_BOT_TOKEN`
33
+ 5. Enable **Message Content Intent** (scroll down on the Bot page)
34
+ 6. Go to **OAuth2 > URL Generator**
35
+ 7. Check the `bot` scope
36
+ 8. Check these permissions: **Send Messages**, **Create Public Threads**, **Read Message History**, **Embed Links**
37
+ 9. Copy the generated URL and open it to invite the bot to your server
38
+
39
+ ### 2. Get your Discord IDs
40
+
41
+ 1. Open Discord Settings > Advanced > enable **Developer Mode**
42
+ 2. Right-click the channel where you want questions posted > **Copy Channel ID** — this is your `DISCORD_CHANNEL_ID`
43
+ 3. Right-click your username > **Copy User ID** — this is your `DISCORD_USER_ID`
44
+
45
+ ### 3. Add to Claude Code
46
+
47
+ ```sh
48
+ claude mcp add \
49
+ -e DISCORD_BOT_TOKEN=your-bot-token \
50
+ -e DISCORD_CHANNEL_ID=your-channel-id \
51
+ -e DISCORD_USER_ID=your-user-id \
52
+ ask-a-human -- npx ask-a-human
53
+ ```
54
+
55
+ That's it. Claude Code will now have access to the `ask_human` tool.
56
+
57
+ ## Tool
58
+
59
+ ### `ask_human`
60
+
61
+ | Parameter | Type | Required | Description |
62
+ |-----------|------|----------|-------------|
63
+ | `question` | string | Yes | The question to ask |
64
+ | `context` | string | No | Background context to help you understand the question |
65
+ | `options` | string[] | No | Predefined choices when applicable |
66
+
67
+ The question is posted as a rich embed in Discord with an @mention. If `context` or `options` are provided, they appear as additional fields in the embed.
68
+
69
+ **Example:**
70
+
71
+ ```json
72
+ {
73
+ "question": "Should I use PostgreSQL or SQLite for the local dev database?",
74
+ "context": "Building a REST API. Production will use PostgreSQL on Railway.",
75
+ "options": ["PostgreSQL (match prod)", "SQLite (simpler local setup)"]
76
+ }
77
+ ```
78
+
79
+ ## Configuration
80
+
81
+ | Environment Variable | Required | Default | Description |
82
+ |---------------------|----------|---------|-------------|
83
+ | `DISCORD_BOT_TOKEN` | Yes | — | Your Discord bot token |
84
+ | `DISCORD_CHANNEL_ID` | Yes | — | Channel ID where questions are posted |
85
+ | `DISCORD_USER_ID` | No | — | Your user ID for @mentions |
86
+ | `ASK_TIMEOUT_MS` | No | `18000000` | Timeout in milliseconds (default: 5 hours, `0` for no timeout) |
87
+
88
+ ## Edge cases
89
+
90
+ - **Multiple replies**: First thread reply wins; subsequent replies are ignored
91
+ - **Empty messages**: Returned as `(empty message)`
92
+ - **Bot messages**: Filtered out automatically
93
+ - **Disconnections**: Discord.js auto-reconnects; pending questions persist in memory
94
+ - **Shutdown**: Pending questions return an error; cleanup is graceful on SIGINT/SIGTERM
95
+
96
+ ## Contributing
97
+
98
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and testing instructions.
99
+
100
+ ## License
101
+
102
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { Client, EmbedBuilder, Events, GatewayIntentBits, ThreadAutoArchiveDuration, } from "discord.js";
5
+ import { z } from "zod";
6
+ // --- Environment validation ---
7
+ const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN;
8
+ const DISCORD_CHANNEL_ID = process.env.DISCORD_CHANNEL_ID;
9
+ const DISCORD_USER_ID = process.env.DISCORD_USER_ID;
10
+ const ASK_TIMEOUT_MS = Number(process.env.ASK_TIMEOUT_MS) || 5 * 60 * 60 * 1000; // 5 hours default
11
+ if (!DISCORD_BOT_TOKEN) {
12
+ console.error("Missing DISCORD_BOT_TOKEN");
13
+ process.exit(1);
14
+ }
15
+ if (!DISCORD_CHANNEL_ID) {
16
+ console.error("Missing DISCORD_CHANNEL_ID");
17
+ process.exit(1);
18
+ }
19
+ // --- Helpers ---
20
+ const EMBED_DESCRIPTION_LIMIT = 4000;
21
+ const EMBED_FIELD_VALUE_LIMIT = 1000;
22
+ const THREAD_NAME_LIMIT = 100;
23
+ function truncate(text, max) {
24
+ return text.length > max ? `${text.slice(0, max)}...` : text;
25
+ }
26
+ // Sentinels use Symbols to prevent collision with user input
27
+ const SENTINEL_CANCELLED = Symbol("cancelled");
28
+ const SENTINEL_TIMEOUT = Symbol("timeout");
29
+ const SENTINEL_SHUTDOWN = Symbol("shutdown");
30
+ const pendingQuestions = new Map();
31
+ // --- Discord client setup ---
32
+ const discordClient = new Client({
33
+ intents: [
34
+ GatewayIntentBits.Guilds,
35
+ GatewayIntentBits.GuildMessages,
36
+ GatewayIntentBits.MessageContent,
37
+ ],
38
+ });
39
+ // Cached channel reference, set during startup
40
+ let targetChannel;
41
+ // Listen for thread replies to pending questions
42
+ discordClient.on(Events.MessageCreate, (message) => {
43
+ // Ignore bot messages
44
+ if (message.author.bot)
45
+ return;
46
+ // Only process messages in threads
47
+ if (!message.channel.isThread())
48
+ return;
49
+ const pending = pendingQuestions.get(message.channelId);
50
+ if (!pending)
51
+ return;
52
+ // First reply resolves; subsequent replies are ignored
53
+ const replyText = message.content || "(empty message)";
54
+ // Clean up timers and abort listener
55
+ if (pending.timeoutId)
56
+ clearTimeout(pending.timeoutId);
57
+ if (pending.keepaliveId)
58
+ clearInterval(pending.keepaliveId);
59
+ pendingQuestions.delete(message.channelId);
60
+ pending.resolve(replyText);
61
+ });
62
+ // --- MCP server setup ---
63
+ const mcpServer = new McpServer({ name: "ask-a-human", version: "0.1.0" }, { capabilities: { logging: {}, tools: {} } });
64
+ mcpServer.registerTool("ask_human", {
65
+ title: "Ask a Human",
66
+ description: "Pause execution and ask a human a question via Discord. The human replies in a Discord thread and execution resumes with their answer.",
67
+ inputSchema: {
68
+ question: z.string().describe("The specific question to ask the human"),
69
+ context: z
70
+ .string()
71
+ .optional()
72
+ .describe("Background context to help the human understand the question"),
73
+ options: z
74
+ .array(z.string())
75
+ .optional()
76
+ .describe("Predefined choices when applicable"),
77
+ },
78
+ annotations: {
79
+ readOnlyHint: false,
80
+ destructiveHint: false,
81
+ idempotentHint: false,
82
+ openWorldHint: true,
83
+ },
84
+ }, async ({ question, context, options }, extra) => {
85
+ // Build embed
86
+ const embed = new EmbedBuilder()
87
+ .setColor(0x5865F2)
88
+ .setTitle("Claude Code needs your input")
89
+ .setDescription(truncate(question, EMBED_DESCRIPTION_LIMIT))
90
+ .setFooter({ text: "Reply in this thread to respond" });
91
+ if (context) {
92
+ embed.addFields({
93
+ name: "Context",
94
+ value: truncate(context, EMBED_FIELD_VALUE_LIMIT),
95
+ });
96
+ }
97
+ if (options && options.length > 0) {
98
+ const optionsList = options.map((o, i) => `${i + 1}. ${o}`).join("\n");
99
+ embed.addFields({
100
+ name: "Options",
101
+ value: truncate(optionsList, EMBED_FIELD_VALUE_LIMIT),
102
+ });
103
+ }
104
+ // Post message — mention in content (not embed) so the user gets pinged
105
+ const sentMessage = await targetChannel.send({
106
+ content: DISCORD_USER_ID ? `<@${DISCORD_USER_ID}>` : undefined,
107
+ embeds: [embed],
108
+ });
109
+ // Create a thread off the message (truncate the full name to fit the limit)
110
+ const thread = await sentMessage.startThread({
111
+ name: truncate(`Question: ${question}`, THREAD_NAME_LIMIT),
112
+ autoArchiveDuration: ThreadAutoArchiveDuration.OneDay,
113
+ });
114
+ // Wait for reply with keepalives, cancellation, and optional timeout
115
+ const reply = await new Promise((resolve) => {
116
+ const pending = { resolve };
117
+ function cleanup(sentinel) {
118
+ if (pending.timeoutId)
119
+ clearTimeout(pending.timeoutId);
120
+ if (pending.keepaliveId)
121
+ clearInterval(pending.keepaliveId);
122
+ pendingQuestions.delete(thread.id);
123
+ resolve(sentinel);
124
+ }
125
+ // Handle client-initiated cancellation via AbortSignal
126
+ const abortHandler = () => cleanup(SENTINEL_CANCELLED);
127
+ pending.abortHandler = abortHandler;
128
+ extra.signal.addEventListener("abort", abortHandler, { once: true });
129
+ // Progress keepalives every 25 seconds
130
+ pending.keepaliveId = setInterval(() => {
131
+ mcpServer.sendLoggingMessage({
132
+ level: "info",
133
+ data: "Waiting for human reply on Discord...",
134
+ });
135
+ }, 25_000);
136
+ // Optional timeout
137
+ if (ASK_TIMEOUT_MS > 0) {
138
+ pending.timeoutId = setTimeout(() => cleanup(SENTINEL_TIMEOUT), ASK_TIMEOUT_MS);
139
+ }
140
+ pendingQuestions.set(thread.id, pending);
141
+ });
142
+ if (reply === SENTINEL_CANCELLED) {
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: "Request was cancelled by the client.",
148
+ },
149
+ ],
150
+ isError: true,
151
+ };
152
+ }
153
+ if (reply === SENTINEL_TIMEOUT) {
154
+ return {
155
+ content: [
156
+ {
157
+ type: "text",
158
+ text: `Timed out after ${ASK_TIMEOUT_MS / 1000 / 60} minutes waiting for a human reply.`,
159
+ },
160
+ ],
161
+ isError: true,
162
+ };
163
+ }
164
+ if (reply === SENTINEL_SHUTDOWN) {
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: "Server is shutting down.",
170
+ },
171
+ ],
172
+ isError: true,
173
+ };
174
+ }
175
+ return {
176
+ content: [{ type: "text", text: reply }],
177
+ };
178
+ });
179
+ // --- Startup sequence ---
180
+ async function main() {
181
+ // 1. Start Discord client and wait for it to be ready
182
+ const readyPromise = new Promise((resolve) => {
183
+ discordClient.once(Events.ClientReady, () => resolve());
184
+ });
185
+ await discordClient.login(DISCORD_BOT_TOKEN);
186
+ await readyPromise;
187
+ console.error("Discord client ready");
188
+ // 2. Fetch and validate the target channel once at startup
189
+ const channel = await discordClient.channels.fetch(DISCORD_CHANNEL_ID);
190
+ if (!channel || !channel.isTextBased() || channel.isDMBased()) {
191
+ console.error("Configured DISCORD_CHANNEL_ID is not a valid guild text channel");
192
+ process.exit(1);
193
+ }
194
+ targetChannel = channel;
195
+ console.error(`Target channel: ${DISCORD_CHANNEL_ID}`);
196
+ // 3. Connect MCP server to stdio transport
197
+ const transport = new StdioServerTransport();
198
+ await mcpServer.connect(transport);
199
+ console.error("MCP server connected (stdio)");
200
+ }
201
+ // --- Graceful shutdown ---
202
+ async function shutdown() {
203
+ console.error("Shutting down...");
204
+ // Clean up all pending questions
205
+ for (const [, pending] of pendingQuestions) {
206
+ if (pending.timeoutId)
207
+ clearTimeout(pending.timeoutId);
208
+ if (pending.keepaliveId)
209
+ clearInterval(pending.keepaliveId);
210
+ pending.resolve(SENTINEL_SHUTDOWN);
211
+ }
212
+ pendingQuestions.clear();
213
+ discordClient.destroy();
214
+ await mcpServer.close();
215
+ process.exit(0);
216
+ }
217
+ process.on("SIGINT", shutdown);
218
+ process.on("SIGTERM", shutdown);
219
+ main().catch((err) => {
220
+ console.error("Fatal error:", err);
221
+ process.exit(1);
222
+ });
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "ask-a-human",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that lets Claude Code pause and ask humans for input via Discord",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "ask-a-human": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "keywords": [
14
+ "mcp",
15
+ "claude",
16
+ "discord",
17
+ "human-in-the-loop",
18
+ "model-context-protocol"
19
+ ],
20
+ "license": "MIT",
21
+ "engines": {
22
+ "node": ">=20.0.0"
23
+ },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.12.0",
26
+ "discord.js": "^14.25.1",
27
+ "zod": "^3.24.0"
28
+ },
29
+ "devDependencies": {
30
+ "tsx": "^4.19.0",
31
+ "typescript": "^5.7.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "dev": "tsx src/index.ts",
36
+ "start": "node dist/index.js",
37
+ "typecheck": "tsc --noEmit"
38
+ }
39
+ }