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 +21 -0
- package/README.md +102 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +223 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|