@tensakulabs/discord-mcp 0.1.0 → 0.1.2

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 Tensaku Labs
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 CHANGED
@@ -22,7 +22,7 @@ Exposes 6 Discord tools via MCP (Model Context Protocol):
22
22
  ### 1. Install & configure
23
23
 
24
24
  ```bash
25
- npx discord-mcp setup
25
+ npx @tensakulabs/discord-mcp setup
26
26
  ```
27
27
 
28
28
  This will:
@@ -32,35 +32,29 @@ This will:
32
32
 
33
33
  ### 2. Token extraction (step shown during setup)
34
34
 
35
- Open Discord desktop app → DevTools consolepaste:
36
-
37
- ```javascript
38
- window.webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m.find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken()
39
- ```
40
-
41
- Copy the returned string.
35
+ Open Discord desktop app → Press **Ctrl+Shift+I** (or **Cmd+Option+I** on Mac) **Network** tab → Send any message in Discord → filter requests by `messages` → click any request → **Headers** tab → find the `Authorization` header → copy its value.
42
36
 
43
37
  ### 3. Restart Claude
44
38
 
45
- Restart Claude desktop app. You'll see Discord tools available.
39
+ Restart Claude Code. You'll see Discord tools available.
46
40
 
47
41
  ### Verify it's working
48
42
 
49
43
  ```bash
50
- npx discord-mcp status
44
+ npx @tensakulabs/discord-mcp status
51
45
  # ✅ Connected as: yourname#0
52
46
  ```
53
47
 
54
48
  ## Manual MCP config (if auto-config fails)
55
49
 
56
- Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
50
+ Add to `~/.claude/settings.json`:
57
51
 
58
52
  ```json
59
53
  {
60
54
  "mcpServers": {
61
55
  "discord": {
62
56
  "command": "npx",
63
- "args": ["-y", "discord-mcp"]
57
+ "args": ["-y", "@tensakulabs/discord-mcp"]
64
58
  }
65
59
  }
66
60
  }
@@ -89,7 +83,7 @@ Add to OpenClaw's MCP config:
89
83
  "name": "discord",
90
84
  "transport": "stdio",
91
85
  "command": "npx",
92
- "args": ["-y", "discord-mcp"]
86
+ "args": ["-y", "@tensakulabs/discord-mcp"]
93
87
  }]
94
88
  }
95
89
  }
package/dist/auth.js CHANGED
@@ -1,12 +1,30 @@
1
1
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
- import { homedir } from "os";
2
+ import { homedir, platform } from "os";
3
3
  import { join } from "path";
4
4
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
5
+ import { execSync } from "child_process";
5
6
  const SERVICE = "discord-mcp";
6
7
  const ACCOUNT = "user-token";
7
8
  const CONFIG_DIR = join(homedir(), ".config", "discord-mcp");
8
9
  const TOKEN_FILE = join(CONFIG_DIR, "token.enc");
9
10
  const KEY_FILE = join(CONFIG_DIR, "key.bin");
11
+ // macOS: use built-in `security` CLI — no native module compilation required
12
+ function saveMacOSKeychain(token) {
13
+ // Delete existing entry first to avoid "already exists" error
14
+ try {
15
+ execSync(`security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`);
16
+ }
17
+ catch { /* not found, ok */ }
18
+ execSync(`security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${token}"`);
19
+ }
20
+ function getMacOSKeychain() {
21
+ try {
22
+ return execSync(`security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`).toString().trim() || null;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
10
28
  let _keytar = null;
11
29
  async function getKeytar() {
12
30
  if (_keytar !== undefined)
@@ -20,38 +38,51 @@ async function getKeytar() {
20
38
  return _keytar;
21
39
  }
22
40
  export async function saveToken(token) {
41
+ // macOS: security CLI always works — no native module needed
42
+ if (platform() === "darwin") {
43
+ saveMacOSKeychain(token);
44
+ return;
45
+ }
46
+ // Other platforms: try keytar
23
47
  const keytar = await getKeytar();
24
48
  try {
25
49
  if (!keytar)
26
50
  throw new Error("keytar not available");
27
51
  await keytar.setPassword(SERVICE, ACCOUNT, token);
52
+ return;
28
53
  }
29
- catch {
30
- // Fallback: encrypt to file (for headless/CI environments)
31
- mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
32
- let key;
33
- if (existsSync(KEY_FILE)) {
34
- key = readFileSync(KEY_FILE);
35
- }
36
- else {
37
- key = randomBytes(32);
38
- writeFileSync(KEY_FILE, key, { mode: 0o600 });
39
- }
40
- const iv = randomBytes(16);
41
- const cipher = createCipheriv("aes-256-cbc", key, iv);
42
- const enc = Buffer.concat([cipher.update(token, "utf8"), cipher.final()]);
43
- writeFileSync(TOKEN_FILE, Buffer.concat([iv, enc]), { mode: 0o600 });
54
+ catch { /* fall through */ }
55
+ // Fallback: encrypt to file (for headless/CI environments)
56
+ console.warn("⚠️ Keychain unavailable, using encrypted file fallback.");
57
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
58
+ let key;
59
+ if (existsSync(KEY_FILE)) {
60
+ key = readFileSync(KEY_FILE);
61
+ }
62
+ else {
63
+ key = randomBytes(32);
64
+ writeFileSync(KEY_FILE, key, { mode: 0o600 });
44
65
  }
66
+ const iv = randomBytes(16);
67
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
68
+ const enc = Buffer.concat([cipher.update(token, "utf8"), cipher.final()]);
69
+ writeFileSync(TOKEN_FILE, Buffer.concat([iv, enc]), { mode: 0o600 });
45
70
  }
46
71
  export async function getToken() {
47
- // Try keychain first
72
+ // macOS: try security CLI first
73
+ if (platform() === "darwin") {
74
+ const token = getMacOSKeychain();
75
+ if (token)
76
+ return token;
77
+ }
78
+ // Try keytar
48
79
  const keytar = await getKeytar();
49
80
  const keychainToken = keytar ? await keytar.getPassword(SERVICE, ACCOUNT).catch(() => null) : null;
50
81
  if (keychainToken)
51
82
  return keychainToken;
52
83
  // Fallback: encrypted file
53
84
  if (!existsSync(TOKEN_FILE) || !existsSync(KEY_FILE)) {
54
- throw new Error("No Discord token found. Run: npx discord-mcp setup");
85
+ throw new Error("No Discord token found. Run: npx @tensakulabs/discord-mcp setup");
55
86
  }
56
87
  const key = readFileSync(KEY_FILE);
57
88
  const raw = readFileSync(TOKEN_FILE);
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import { saveToken, getToken } from "./auth.js";
5
5
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
6
6
  import { execSync } from "child_process";
7
7
  import { homedir, platform } from "os";
8
- import { join } from "path";
8
+ import { join, dirname } from "path";
9
9
  const program = new Command();
10
10
  program
11
11
  .name("discord-mcp")
@@ -24,12 +24,11 @@ Step 1: Extract your Discord token
24
24
 
25
25
  1. Open Discord desktop app
26
26
  2. Press Ctrl+Shift+I (or Cmd+Option+I on Mac) to open DevTools
27
- 3. Click the "Console" tab
28
- 4. Paste this one-liner and press Enter:
29
-
30
- window.webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m.find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken()
31
-
32
- 5. Copy the returned string (no quotes needed)
27
+ 3. Click the "Network" tab
28
+ 4. Switch to any server or channel to trigger a request
29
+ 5. Click any request to discord.com/api/...
30
+ 6. Scroll to "Request Headers" → find the "authorization" header
31
+ 7. Copy the value (starts with MT or OD — no quotes needed)
33
32
 
34
33
  `);
35
34
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -42,11 +41,9 @@ Step 1: Extract your Discord token
42
41
  }
43
42
  await saveToken(token);
44
43
  console.log("✅ Token saved securely to OS keychain (or encrypted file fallback).");
45
- // Auto-patch claude_desktop_config.json
44
+ // Auto-patch Claude Code settings.json
46
45
  const configPaths = [
47
- join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json"), // macOS
48
- join(homedir(), ".config", "Claude", "claude_desktop_config.json"), // Linux
49
- join(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json"), // Windows
46
+ join(homedir(), ".claude", "settings.json"), // Claude Code (all platforms)
50
47
  ];
51
48
  const configPath = configPaths.find(existsSync);
52
49
  // Write launchd plist (macOS only) — ISC-D6
@@ -59,6 +56,12 @@ Step 1: Extract your Discord token
59
56
  catch {
60
57
  return "/usr/local/bin/npx";
61
58
  } })();
59
+ const nodeBinDir = (() => { try {
60
+ return dirname(execSync("which node").toString().trim());
61
+ }
62
+ catch {
63
+ return "/usr/local/bin";
64
+ } })();
62
65
  const logDir = join(homedir(), ".config", "discord-mcp");
63
66
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
64
67
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -72,6 +75,11 @@ Step 1: Extract your Discord token
72
75
  <string>@tensakulabs/discord-mcp</string>
73
76
  <string>daemon-start</string>
74
77
  </array>
78
+ <key>EnvironmentVariables</key>
79
+ <dict>
80
+ <key>PATH</key>
81
+ <string>${nodeBinDir}:/usr/local/bin:/usr/bin:/bin</string>
82
+ </dict>
75
83
  <key>RunAtLoad</key>
76
84
  <true/>
77
85
  <key>KeepAlive</key>
@@ -101,13 +109,13 @@ Step 1: Extract your Discord token
101
109
  };
102
110
  writeFileSync(configPath, JSON.stringify(config, null, 2));
103
111
  console.log(`✅ Registered in Claude config: ${configPath}`);
104
- console.log("\n🎉 Done! Restart Claude to start using Discord tools.");
112
+ console.log("\n🎉 Done! Restart Claude Code to start using Discord tools.");
105
113
  }
106
114
  else {
107
115
  console.log(`
108
- ⚠️ Could not find Claude desktop config automatically.
116
+ ⚠️ Could not find Claude Code config automatically.
109
117
 
110
- Add this to your claude_desktop_config.json manually:
118
+ Add this to your ~/.claude/settings.json manually:
111
119
 
112
120
  "mcpServers": {
113
121
  "discord": {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@tensakulabs/discord-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Discord selfbot MCP server — read & send messages via Claude",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "bin": {
7
8
  "discord-mcp": "./dist/cli.js"
@@ -29,6 +30,10 @@
29
30
  "optionalDependencies": {
30
31
  "keytar": "^7.9.0"
31
32
  },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/tensakulabs/discord-mcp"
36
+ },
32
37
  "publishConfig": {
33
38
  "access": "public"
34
39
  },