feat(server): add search messages handler and schema
- Implement searchMessagesHandler to search for messages in a Discord server. - Add SearchMessagesSchema for input validation. - Update toolList to include discord_search_messages tool. - Modify transport and tools to handle new search messages functionality.
This commit is contained in:
parent
f2fc159fd6
commit
470842e396
|
|
@ -131,4 +131,21 @@ export const DeleteWebhookSchema = z.object({
|
|||
reason: z.string().optional()
|
||||
});
|
||||
|
||||
export const ListServersSchema = z.object({});
|
||||
export const ListServersSchema = z.object({});
|
||||
|
||||
export const SearchMessagesSchema = z.object({
|
||||
guildId: z.string().min(1, "guildId is required"),
|
||||
// Optional filters
|
||||
authorId: z.string().optional(),
|
||||
mentions: z.string().optional(),
|
||||
has: z.enum(['link','embed','file','poll','image','video','sound','sticker','snapshot']).optional(),
|
||||
maxId: z.string().optional(),
|
||||
minId: z.string().optional(),
|
||||
channelId: z.string().optional(),
|
||||
pinned: z.boolean().optional(),
|
||||
authorType: z.enum(['user','bot','webhook']).optional(),
|
||||
sortBy: z.enum(['timestamp','relevance']).optional(),
|
||||
sortOrder: z.enum(['desc','asc']).optional(),
|
||||
limit: z.number().min(1).max(100).default(25).optional(),
|
||||
offset: z.number().min(0).default(0).optional()
|
||||
});
|
||||
|
|
@ -30,7 +30,8 @@ import {
|
|||
createCategoryHandler,
|
||||
editCategoryHandler,
|
||||
deleteCategoryHandler,
|
||||
listServersHandler
|
||||
listServersHandler,
|
||||
searchMessagesHandler
|
||||
} from './tools/tools.js';
|
||||
import { MCPTransport } from './transport.js';
|
||||
import { info, error } from './logger.js';
|
||||
|
|
@ -184,6 +185,11 @@ export class DiscordMCPServer {
|
|||
toolResponse = await listServersHandler(args, this.toolContext);
|
||||
return toolResponse;
|
||||
|
||||
case "discord_search_messages":
|
||||
this.logClientState("before discord_search_messages handler");
|
||||
toolResponse = await searchMessagesHandler(args, this.toolContext);
|
||||
return toolResponse;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown tool: ${name}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,5 +301,28 @@ export const toolList = [
|
|||
properties: {},
|
||||
required: []
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "discord_search_messages",
|
||||
description: "Searches for messages in a Discord server",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
guildId: { type: "string", description: "The ID of the Discord server (guild) to search within" },
|
||||
authorId: { type: "string", description: "Filter messages by a specific user ID" },
|
||||
mentions: { type: "string", description: "Filter messages that mention a specific user ID" },
|
||||
has: { type: "string", description: "Filter messages that contain specific content types (e.g., link, embed, file, poll, image, video, sound, sticker, snapshot)" },
|
||||
maxId: { type: "string", description: "Filter messages with IDs less than this value (messages before this ID)" },
|
||||
minId: { type: "string", description: "Filter messages with IDs greater than this value (messages after this ID)" },
|
||||
channelId: { type: "string", description: "Filter messages within a specific channel ID" },
|
||||
pinned: { type: "boolean", description: "Filter messages based on whether they are pinned" },
|
||||
authorType: { type: "string", description: "Filter messages by author type (user, bot, webhook)" },
|
||||
sortBy: { type: "string", description: "Sort results by 'timestamp' or 'relevance'" },
|
||||
sortOrder: { type: "string", description: "Sort order: 'desc' for descending or 'asc' for ascending" },
|
||||
limit: { type: "number", description: "Maximum number of messages to return (default 25, max 100)" },
|
||||
offset: { type: "number", description: "Number of messages to skip (for pagination)" }
|
||||
},
|
||||
required: ["guildId"]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import { handleDiscordError } from "../errorHandler.js";
|
||||
import { SearchMessagesSchema } from "../schemas.js";
|
||||
import { ToolContext, ToolResponse } from "./types.js";
|
||||
|
||||
|
||||
// Search server messages handler
|
||||
export async function searchMessagesHandler(
|
||||
args: unknown,
|
||||
context: ToolContext
|
||||
): Promise<ToolResponse> {
|
||||
const { guildId, authorId, mentions, has, maxId, minId, channelId, pinned, authorType, sortBy, sortOrder, limit, offset } = SearchMessagesSchema.parse(args);
|
||||
try {
|
||||
if (!context.client.isReady()) {
|
||||
return {
|
||||
content: [{ type: "text", text: "Discord client not logged in." }],
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
// Note: Discord.js does not support guild message search natively.
|
||||
// This requires direct API calls or using a library that supports it.
|
||||
// Here we will construct the API request using context.client.rest
|
||||
const params = new URLSearchParams();
|
||||
if (authorId) params.append('author_id', authorId);
|
||||
if (mentions) params.append('mentions', mentions);
|
||||
if (has) params.append('has', has);
|
||||
if (maxId) params.append('max_id', maxId);
|
||||
if (minId) params.append('min_id', minId);
|
||||
if (channelId) params.append('channel_id', channelId);
|
||||
if (typeof pinned === 'boolean') params.append('pinned', String(pinned));
|
||||
if (authorType) params.append('author_type', authorType);
|
||||
if (sortBy) params.append('sort_by', sortBy);
|
||||
if (sortOrder) params.append('sort_order', sortOrder);
|
||||
params.append('limit', String(limit || 25));
|
||||
params.append('offset', String(offset || 0));
|
||||
|
||||
const response = await context.client.rest.get(`/guilds/${guildId}/messages/search?${params.toString()}`);
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
|
||||
};
|
||||
} catch (error) {
|
||||
return handleDiscordError(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,9 @@ import {
|
|||
deleteCategoryHandler,
|
||||
listServersHandler
|
||||
} from './channel.js';
|
||||
import {
|
||||
searchMessagesHandler
|
||||
} from "./server.js";
|
||||
import {
|
||||
addReactionHandler,
|
||||
addMultipleReactionsHandler,
|
||||
|
|
@ -57,7 +60,8 @@ export {
|
|||
createCategoryHandler,
|
||||
editCategoryHandler,
|
||||
deleteCategoryHandler,
|
||||
listServersHandler
|
||||
listServersHandler,
|
||||
searchMessagesHandler
|
||||
};
|
||||
|
||||
// Export common types
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ import {
|
|||
editCategoryHandler,
|
||||
createCategoryHandler,
|
||||
deleteCategoryHandler,
|
||||
listServersHandler
|
||||
listServersHandler,
|
||||
searchMessagesHandler
|
||||
} from './tools/tools.js';
|
||||
import { Client, GatewayIntentBits } from "discord.js";
|
||||
import { info, error } from './logger.js';
|
||||
|
|
@ -215,6 +216,7 @@ export class StreamableHttpTransport implements MCPTransport {
|
|||
case 'discord_edit_category':
|
||||
case 'discord_delete_category':
|
||||
case 'discord_list_servers':
|
||||
case 'discord_search_messages':
|
||||
// Check if client is logged in
|
||||
if (!this.toolContext!.client.isReady()) {
|
||||
error(`Client not ready for method ${method}, client state: ${JSON.stringify({
|
||||
|
|
@ -338,8 +340,9 @@ export class StreamableHttpTransport implements MCPTransport {
|
|||
break;
|
||||
case 'discord_list_servers':
|
||||
result = await listServersHandler(params, this.toolContext!);
|
||||
break;
|
||||
|
||||
case 'discord_search_messages':
|
||||
result = await searchMessagesHandler(params, this.toolContext!);
|
||||
break;break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -507,6 +510,9 @@ export class StreamableHttpTransport implements MCPTransport {
|
|||
case 'discord_list_servers':
|
||||
result = await listServersHandler(toolArgs, this.toolContext!);
|
||||
break;
|
||||
case 'discord_search_messages':
|
||||
result = await searchMessagesHandler(toolArgs, this.toolContext!);
|
||||
break;
|
||||
|
||||
default:
|
||||
return res.status(400).json({
|
||||
|
|
|
|||
Loading…
Reference in New Issue