feat: add Discord category management (create/edit/delete)
- Added handlers, schemas, and MCP tool wiring for creating, editing, and deleting Discord categories (GuildCategory). - Fixed all .create/.edit argument errors for both category and text channel operations. - Registered new tools in toolList and MCP server.
This commit is contained in:
parent
c827761ccb
commit
6f80c47b89
|
@ -4,6 +4,13 @@ npm-debug.log
|
||||||
yarn-debug.log
|
yarn-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Internal AI memory/log
|
||||||
|
knowledge.md
|
||||||
|
|
||||||
# Environment Variables
|
# Environment Variables
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
|
|
@ -32,7 +32,28 @@ export const ReplyToForumSchema = z.object({
|
||||||
export const CreateTextChannelSchema = z.object({
|
export const CreateTextChannelSchema = z.object({
|
||||||
guildId: z.string(),
|
guildId: z.string(),
|
||||||
channelName: z.string(),
|
channelName: z.string(),
|
||||||
topic: z.string().optional()
|
topic: z.string().optional(),
|
||||||
|
reason: z.string().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Category schemas
|
||||||
|
export const CreateCategorySchema = z.object({
|
||||||
|
guildId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
position: z.number().optional(),
|
||||||
|
reason: z.string().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const EditCategorySchema = z.object({
|
||||||
|
categoryId: z.string(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
position: z.number().optional(),
|
||||||
|
reason: z.string().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DeleteCategorySchema = z.object({
|
||||||
|
categoryId: z.string(),
|
||||||
|
reason: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DeleteChannelSchema = z.object({
|
export const DeleteChannelSchema = z.object({
|
||||||
|
|
|
@ -26,7 +26,10 @@ import {
|
||||||
createWebhookHandler,
|
createWebhookHandler,
|
||||||
sendWebhookMessageHandler,
|
sendWebhookMessageHandler,
|
||||||
editWebhookHandler,
|
editWebhookHandler,
|
||||||
deleteWebhookHandler
|
deleteWebhookHandler,
|
||||||
|
createCategoryHandler,
|
||||||
|
editCategoryHandler,
|
||||||
|
deleteCategoryHandler
|
||||||
} from './tools/tools.js';
|
} from './tools/tools.js';
|
||||||
import { MCPTransport } from './transport.js';
|
import { MCPTransport } from './transport.js';
|
||||||
import { info, error } from './logger.js';
|
import { info, error } from './logger.js';
|
||||||
|
@ -71,6 +74,15 @@ export class DiscordMCPServer {
|
||||||
try {
|
try {
|
||||||
let toolResponse;
|
let toolResponse;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
case "discord_create_category":
|
||||||
|
toolResponse = await createCategoryHandler(args, this.toolContext);
|
||||||
|
return toolResponse;
|
||||||
|
case "discord_edit_category":
|
||||||
|
toolResponse = await editCategoryHandler(args, this.toolContext);
|
||||||
|
return toolResponse;
|
||||||
|
case "discord_delete_category":
|
||||||
|
toolResponse = await deleteCategoryHandler(args, this.toolContext);
|
||||||
|
return toolResponse;
|
||||||
case "discord_login":
|
case "discord_login":
|
||||||
toolResponse = await loginHandler(args, this.toolContext);
|
toolResponse = await loginHandler(args, this.toolContext);
|
||||||
// Check the client state after login
|
// Check the client state after login
|
||||||
|
|
|
@ -1,4 +1,44 @@
|
||||||
export const toolList = [
|
export const toolList = [
|
||||||
|
{
|
||||||
|
name: "discord_create_category",
|
||||||
|
description: "Creates a new category in a Discord server.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
guildId: { type: "string" },
|
||||||
|
name: { type: "string" },
|
||||||
|
position: { type: "number" },
|
||||||
|
reason: { type: "string" }
|
||||||
|
},
|
||||||
|
required: ["guildId", "name"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "discord_edit_category",
|
||||||
|
description: "Edits an existing Discord category (name and position).",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
categoryId: { type: "string" },
|
||||||
|
name: { type: "string" },
|
||||||
|
position: { type: "number" },
|
||||||
|
reason: { type: "string" }
|
||||||
|
},
|
||||||
|
required: ["categoryId"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "discord_delete_category",
|
||||||
|
description: "Deletes a Discord category by ID.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
categoryId: { type: "string" },
|
||||||
|
reason: { type: "string" }
|
||||||
|
},
|
||||||
|
required: ["categoryId"]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "discord_login",
|
name: "discord_login",
|
||||||
description: "Logs in to Discord using the configured token",
|
description: "Logs in to Discord using the configured token",
|
||||||
|
|
|
@ -5,16 +5,113 @@ import {
|
||||||
CreateTextChannelSchema,
|
CreateTextChannelSchema,
|
||||||
DeleteChannelSchema,
|
DeleteChannelSchema,
|
||||||
ReadMessagesSchema,
|
ReadMessagesSchema,
|
||||||
GetServerInfoSchema
|
GetServerInfoSchema,
|
||||||
|
CreateCategorySchema,
|
||||||
|
EditCategorySchema,
|
||||||
|
DeleteCategorySchema
|
||||||
} from "../schemas.js";
|
} from "../schemas.js";
|
||||||
import { handleDiscordError } from "../errorHandler.js";
|
import { handleDiscordError } from "../errorHandler.js";
|
||||||
|
|
||||||
|
// Category creation handler
|
||||||
|
export async function createCategoryHandler(
|
||||||
|
args: unknown,
|
||||||
|
context: ToolContext
|
||||||
|
): Promise<ToolResponse> {
|
||||||
|
const { guildId, name, position, reason } = CreateCategorySchema.parse(args);
|
||||||
|
try {
|
||||||
|
if (!context.client.isReady()) {
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||||
|
isError: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const guild = await context.client.guilds.fetch(guildId);
|
||||||
|
if (!guild) {
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Cannot find guild with ID: ${guildId}` }],
|
||||||
|
isError: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const options: any = { name, type: ChannelType.GuildCategory };
|
||||||
|
if (typeof position === "number") options.position = position;
|
||||||
|
if (reason) options.reason = reason;
|
||||||
|
const category = await guild.channels.create(options);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Successfully created category "${name}" with ID: ${category.id}` }]
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return handleDiscordError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category edit handler
|
||||||
|
export async function editCategoryHandler(
|
||||||
|
args: unknown,
|
||||||
|
context: ToolContext
|
||||||
|
): Promise<ToolResponse> {
|
||||||
|
const { categoryId, name, position, reason } = EditCategorySchema.parse(args);
|
||||||
|
try {
|
||||||
|
if (!context.client.isReady()) {
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||||
|
isError: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const category = await context.client.channels.fetch(categoryId);
|
||||||
|
if (!category || category.type !== ChannelType.GuildCategory) {
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Cannot find category with ID: ${categoryId}` }],
|
||||||
|
isError: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const update: any = {};
|
||||||
|
if (name) update.name = name;
|
||||||
|
if (typeof position === "number") update.position = position;
|
||||||
|
if (reason) update.reason = reason;
|
||||||
|
await category.edit(update);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Successfully edited category with ID: ${categoryId}` }]
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return handleDiscordError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category deletion handler
|
||||||
|
export async function deleteCategoryHandler(
|
||||||
|
args: unknown,
|
||||||
|
context: ToolContext
|
||||||
|
): Promise<ToolResponse> {
|
||||||
|
const { categoryId, reason } = DeleteCategorySchema.parse(args);
|
||||||
|
try {
|
||||||
|
if (!context.client.isReady()) {
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: "Discord client not logged in. Please use discord_login tool first." }],
|
||||||
|
isError: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const category = await context.client.channels.fetch(categoryId);
|
||||||
|
if (!category || category.type !== ChannelType.GuildCategory) {
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Cannot find category with ID: ${categoryId}` }],
|
||||||
|
isError: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
await category.delete(reason || "Category deleted via API");
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Successfully deleted category with ID: ${categoryId}` }]
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return handleDiscordError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Text channel creation handler
|
// Text channel creation handler
|
||||||
export async function createTextChannelHandler(
|
export async function createTextChannelHandler(
|
||||||
args: unknown,
|
args: unknown,
|
||||||
context: ToolContext
|
context: ToolContext
|
||||||
): Promise<ToolResponse> {
|
): Promise<ToolResponse> {
|
||||||
const { guildId, channelName, topic } = CreateTextChannelSchema.parse(args);
|
const { guildId, channelName, topic, reason } = CreateTextChannelSchema.parse(args);
|
||||||
try {
|
try {
|
||||||
if (!context.client.isReady()) {
|
if (!context.client.isReady()) {
|
||||||
return {
|
return {
|
||||||
|
@ -32,11 +129,13 @@ export async function createTextChannelHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the text channel
|
// Create the text channel
|
||||||
const channel = await guild.channels.create({
|
const channelOptions: any = {
|
||||||
name: channelName,
|
name: channelName,
|
||||||
type: ChannelType.GuildText,
|
type: ChannelType.GuildText
|
||||||
topic: topic
|
};
|
||||||
});
|
if (topic) channelOptions.topic = topic;
|
||||||
|
if (reason) channelOptions.reason = reason;
|
||||||
|
const channel = await guild.channels.create(channelOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
|
|
|
@ -14,7 +14,10 @@ import {
|
||||||
createTextChannelHandler,
|
createTextChannelHandler,
|
||||||
deleteChannelHandler,
|
deleteChannelHandler,
|
||||||
readMessagesHandler,
|
readMessagesHandler,
|
||||||
getServerInfoHandler
|
getServerInfoHandler,
|
||||||
|
createCategoryHandler,
|
||||||
|
editCategoryHandler,
|
||||||
|
deleteCategoryHandler
|
||||||
} from './channel.js';
|
} from './channel.js';
|
||||||
import {
|
import {
|
||||||
addReactionHandler,
|
addReactionHandler,
|
||||||
|
@ -49,7 +52,10 @@ export {
|
||||||
createWebhookHandler,
|
createWebhookHandler,
|
||||||
sendWebhookMessageHandler,
|
sendWebhookMessageHandler,
|
||||||
editWebhookHandler,
|
editWebhookHandler,
|
||||||
deleteWebhookHandler
|
deleteWebhookHandler,
|
||||||
|
createCategoryHandler,
|
||||||
|
editCategoryHandler,
|
||||||
|
deleteCategoryHandler
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export common types
|
// Export common types
|
||||||
|
|
Loading…
Reference in New Issue