discord-ops 0.11.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # discord-ops
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e70b6ac: Auto-embed for send_message and health endpoint version field
8
+ - **Auto-embed for `send_message`**: Messages are now automatically wrapped in a polished embed (color bar, description, timestamp) via the new `simple` template. Set `raw: true` to send plain text. Every message looks professional by default.
9
+ - **`simple` template**: New minimal utility template — just a branded embed with optional title, color, author, and footer. Used automatically by `send_message`, also available via `send_template`.
10
+ - **Health endpoint `version` field**: `GET /health` now includes the `discord-ops` package version for deployment verification.
11
+
12
+ - e70b6ac: Live channel name resolution — fuzzy.ts was dead code, now it works
13
+ - **Channel fuzzy resolution**: The `channel` param now resolves in four layers — exact alias match, fuzzy alias match (e.g. `"build"` hits `"builds"`), then a live Discord API lookup that finds channels by their actual Discord name (e.g. `channel: "general"` now works even with no configured alias).
14
+ - **Fuzzy alias matching**: Configured channel aliases are now fuzzy-matched before falling back to Discord, so near-misses on alias names resolve correctly.
15
+ - **`list_templates` fix**: Internal `simple` template (used for auto-embed) no longer appears in `list_templates` output — count now correctly shows 23.
16
+
17
+ - e70b6ac: New send_embed tool with server-side OG metadata fetching
18
+
3
19
  ## 0.11.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -8,7 +8,7 @@ Agency-grade Discord MCP server with multi-guild project routing.
8
8
 
9
9
  ## Features
10
10
 
11
- - **45 MCP tools** — messaging, channels, moderation, roles, webhooks, audit log, threads, guilds, invites, permissions, search, 23 templates, project introspection
11
+ - **46 MCP tools** — messaging, channels, moderation, roles, webhooks, audit log, threads, guilds, invites, permissions, search, 23 templates, project introspection
12
12
  - **Multi-guild project routing** — `send_message({ project: "my-app", channel: "builds" })` instead of raw channel IDs
13
13
  - **Notification routing** — map notification types (ci_build, deploy, error) to channels per project
14
14
  - **Multi-bot support** — manage multiple Discord bots from a single MCP server with per-project tokens
@@ -46,54 +46,76 @@ discord-ops serve --port 3000
46
46
 
47
47
  ## Claude Code Integration
48
48
 
49
- Add to your `.mcp.json`:
49
+ Add to your project's `.mcp.json`. Use `npx` without a version pin so every session automatically uses the latest published release:
50
50
 
51
51
  ```json
52
52
  {
53
53
  "mcpServers": {
54
- "discord": {
54
+ "discord-ops": {
55
55
  "command": "npx",
56
56
  "args": ["-y", "discord-ops"],
57
57
  "env": {
58
- "DISCORD_TOKEN": "your-bot-token"
58
+ "DISCORD_TOKEN": "${DISCORD_TOKEN}"
59
59
  }
60
60
  }
61
61
  }
62
62
  }
63
63
  ```
64
64
 
65
- ### Multi-org setup (per-project tokens, no default)
65
+ The `${VAR}` syntax is Claude Code's native env var interpolation — it reads the value from your shell environment at startup. Export your bot token in `~/.zshrc` (or `.bashrc`) and it will be available to all projects without hardcoding it in any file.
66
66
 
67
- When each project uses its own bot token, you don't need `DISCORD_TOKEN` at all:
67
+ ### Multi-org setup (per-project tokens)
68
+
69
+ When each project has its own bot, pass all token env vars and let `~/.discord-ops.json` handle which project uses which:
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "discord-ops": {
75
+ "command": "npx",
76
+ "args": ["-y", "discord-ops"],
77
+ "env": {
78
+ "ORG_A_DISCORD_TOKEN": "${ORG_A_DISCORD_TOKEN}",
79
+ "ORG_B_DISCORD_TOKEN": "${ORG_B_DISCORD_TOKEN}"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ Each project in `~/.discord-ops.json` declares `"token_env": "ORG_A_DISCORD_TOKEN"` and discord-ops routes automatically. No default `DISCORD_TOKEN` needed when all projects have `token_env` set.
87
+
88
+ ### Single-org shorthand
89
+
90
+ If all your projects share one bot, just pass that token:
68
91
 
69
92
  ```json
70
93
  {
71
94
  "mcpServers": {
72
- "discord": {
95
+ "discord-ops": {
73
96
  "command": "npx",
74
97
  "args": ["-y", "discord-ops"],
75
98
  "env": {
76
- "PROJECT_A_TOKEN": "bot-token-for-project-a",
77
- "PROJECT_B_TOKEN": "bot-token-for-project-b"
99
+ "DISCORD_TOKEN": "${DISCORD_TOKEN}"
78
100
  }
79
101
  }
80
102
  }
81
103
  }
82
104
  ```
83
105
 
84
- ### Custom default token env var
106
+ ### Custom token env var name
85
107
 
86
- If another tool already claims `DISCORD_TOKEN`, use `DISCORD_OPS_TOKEN_ENV` to point at a different env var:
108
+ If another tool already claims `DISCORD_TOKEN`, use `DISCORD_OPS_TOKEN_ENV` to point at a different name:
87
109
 
88
110
  ```json
89
111
  {
90
112
  "mcpServers": {
91
- "discord": {
113
+ "discord-ops": {
92
114
  "command": "npx",
93
115
  "args": ["-y", "discord-ops"],
94
116
  "env": {
95
- "DISCORD_OPS_TOKEN_ENV": "MY_DISCORD_BOT_TOKEN",
96
- "MY_DISCORD_BOT_TOKEN": "your-bot-token"
117
+ "DISCORD_OPS_TOKEN_ENV": "MY_BOT_TOKEN",
118
+ "MY_BOT_TOKEN": "${MY_BOT_TOKEN}"
97
119
  }
98
120
  }
99
121
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAmEjD,eAAO,MAAM,QAAQ,EAAE,cAAc,EAiEpC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoEjD,eAAO,MAAM,QAAQ,EAAE,cAAc,EAkEpC,CAAC"}
@@ -8,6 +8,7 @@ import { pinMessage } from "./messaging/pin.js";
8
8
  import { unpinMessage } from "./messaging/unpin.js";
9
9
  import { searchMessages } from "./messaging/search.js";
10
10
  import { sendTemplate } from "./messaging/send-template.js";
11
+ import { sendEmbed } from "./messaging/send-embed.js";
11
12
  import { listTemplatesCmd } from "./messaging/list-templates.js";
12
13
  // Channels
13
14
  import { listChannels } from "./channels/list-channels.js";
@@ -64,6 +65,7 @@ export const allTools = [
64
65
  unpinMessage,
65
66
  searchMessages,
66
67
  sendTemplate,
68
+ sendEmbed,
67
69
  listTemplatesCmd,
68
70
  // Channels
69
71
  listChannels,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ToolDefinition } from "../types.js";
2
+ export declare const sendEmbed: ToolDefinition;
3
+ //# sourceMappingURL=send-embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-embed.d.ts","sourceRoot":"","sources":["../../../src/tools/messaging/send-embed.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAoBlD,eAAO,MAAM,SAAS,EAAE,cA0CvB,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ import { toolResultJson } from "../types.js";
3
+ import { snowflakeId } from "../schema.js";
4
+ import { resolveTarget } from "../../routing/resolver.js";
5
+ import { fetchOgMetadata } from "../../utils/og-fetch.js";
6
+ const inputSchema = z.object({
7
+ url: z.string().url().describe("URL to unfurl as a rich embed"),
8
+ channel_id: snowflakeId.optional().describe("Direct channel ID"),
9
+ guild_id: snowflakeId.optional().describe("Direct guild ID"),
10
+ project: z.string().optional().describe("Project name for routing"),
11
+ channel: z.string().optional().describe("Channel alias within project"),
12
+ notification_type: z.string().optional().describe("Notification type for auto-routing"),
13
+ title: z.string().max(256).optional().describe("Override OG title"),
14
+ description: z.string().max(4096).optional().describe("Override OG description"),
15
+ image_url: z.string().url().optional().describe("Override OG image URL"),
16
+ color: z.number().int().min(0).max(0xffffff).optional().describe("Embed color as integer"),
17
+ footer: z.string().max(2048).optional().describe("Footer text"),
18
+ });
19
+ export const sendEmbed = {
20
+ name: "send_embed",
21
+ description: "Send a rich Discord embed by fetching Open Graph metadata from a URL. Automatically extracts title, description, and image from the page. Supports overrides for all OG fields plus color and footer.",
22
+ category: "messaging",
23
+ inputSchema,
24
+ handle: async (input, ctx) => {
25
+ const target = await resolveTarget(input, ctx.config, ctx.discord);
26
+ if ("error" in target) {
27
+ return { content: [{ type: "text", text: target.error }], isError: true };
28
+ }
29
+ const og = await fetchOgMetadata(input.url);
30
+ const embed = {
31
+ url: input.url,
32
+ };
33
+ const title = input.title ?? og.title;
34
+ if (title)
35
+ embed.title = title;
36
+ const description = input.description ?? og.description;
37
+ if (description)
38
+ embed.description = description;
39
+ if (input.color !== undefined)
40
+ embed.color = input.color;
41
+ const imageUrl = input.image_url ?? og.image;
42
+ if (imageUrl)
43
+ embed.image = { url: imageUrl };
44
+ if (input.footer)
45
+ embed.footer = { text: input.footer };
46
+ const channel = await ctx.discord.getChannel(target.channelId, target.token);
47
+ const message = await channel.send({ embeds: [embed] });
48
+ return toolResultJson({
49
+ id: message.id,
50
+ channel_id: message.channelId,
51
+ url: input.url,
52
+ og_fetched: Object.keys(og).length > 0,
53
+ ...(target.project ? { project: target.project } : {}),
54
+ });
55
+ },
56
+ };
57
+ //# sourceMappingURL=send-embed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-embed.js","sourceRoot":"","sources":["../../../src/tools/messaging/send-embed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface OgMetadata {
2
+ title?: string;
3
+ description?: string;
4
+ image?: string;
5
+ siteName?: string;
6
+ }
7
+ export declare function fetchOgMetadata(url: string): Promise<OgMetadata>;
8
+ //# sourceMappingURL=og-fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"og-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/og-fetch.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgDtE"}
@@ -0,0 +1,42 @@
1
+ import { logger } from "./logger.js";
2
+ export async function fetchOgMetadata(url) {
3
+ try {
4
+ const response = await fetch(url, {
5
+ headers: { "User-Agent": "discord-ops/1.0 (OG metadata fetcher)" },
6
+ signal: AbortSignal.timeout(5000),
7
+ });
8
+ const contentType = response.headers.get("content-type") ?? "";
9
+ if (!contentType.includes("text/html")) {
10
+ return {};
11
+ }
12
+ const text = (await response.text()).slice(0, 50000);
13
+ const result = {};
14
+ const properties = ["title", "description", "image", "site_name"];
15
+ const ogKeys = {
16
+ "og:title": "title",
17
+ "og:description": "description",
18
+ "og:image": "image",
19
+ "og:site_name": "siteName",
20
+ };
21
+ for (const [prop, key] of Object.entries(ogKeys)) {
22
+ // property="og:X" content="Y"
23
+ const fwdMatch = text.match(new RegExp(`property=["']${prop}["'][^>]*content=["']([^"']+)["']`, "i"));
24
+ // content="Y" property="og:X"
25
+ const revMatch = text.match(new RegExp(`content=["']([^"']+)["'][^>]*property=["']${prop}["']`, "i"));
26
+ const value = fwdMatch?.[1] ?? revMatch?.[1];
27
+ if (value) {
28
+ result[key] = value;
29
+ }
30
+ }
31
+ void properties; // suppress unused warning
32
+ return result;
33
+ }
34
+ catch (err) {
35
+ logger.warn("Failed to fetch OG metadata", {
36
+ url,
37
+ error: err instanceof Error ? err.message : String(err),
38
+ });
39
+ return {};
40
+ }
41
+ }
42
+ //# sourceMappingURL=og-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"og-fetch.js","sourceRoot":"","sources":["../../src/utils/og-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AASrC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,OAAO,EAAE,EAAE,YAAY,EAAE,uCAAuC,EAAE;YAClE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/D,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACrD,MAAM,MAAM,GAAe,EAAE,CAAC;QAE9B,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;QAC3E,MAAM,MAAM,GAAqC;YAC/C,UAAU,EAAE,OAAO;YACnB,gBAAgB,EAAE,aAAa;YAC/B,UAAU,EAAE,OAAO;YACnB,cAAc,EAAE,UAAU;SAC3B,CAAC;QAEF,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACjD,8BAA8B;YAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CACzB,IAAI,MAAM,CAAC,gBAAgB,IAAI,mCAAmC,EAAE,GAAG,CAAC,CACzE,CAAC;YACF,8BAA8B;YAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CACzB,IAAI,MAAM,CAAC,6CAA6C,IAAI,MAAM,EAAE,GAAG,CAAC,CACzE,CAAC;YACF,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7C,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACtB,CAAC;QACH,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,0BAA0B;QAE3C,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;YACzC,GAAG;YACH,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;QACH,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discord-ops",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Agency-grade Discord MCP server with multi-guild project routing",
5
5
  "type": "module",
6
6
  "license": "MIT",