import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { getDiscordClient } from '../utils/discord-client.js';
import { withErrorHandling } from '../utils/error-handler.js';
export function registerEmojiTools(server: McpServer): void {
// List emojis
server.tool(
'list_emojis',
'List all custom emojis in a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
},
async ({ guildId }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const emojis = await guild.emojis.fetch();
return emojis.map((emoji) => ({
id: emoji.id,
name: emoji.name,
animated: emoji.animated,
available: emoji.available,
managed: emoji.managed,
requiresColons: emoji.requiresColons,
roles: emoji.roles.cache.map((r) => ({ id: r.id, name: r.name })),
author: emoji.author ? { id: emoji.author.id, username: emoji.author.username } : null,
identifier: emoji.identifier,
url: emoji.url,
}));
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// Create emoji
server.tool(
'create_emoji',
'Create a custom emoji in a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
name: z.string().describe('Name for the emoji'),
imageUrl: z.string().describe('URL of the image to use (must be < 256KB)'),
roles: z.array(z.string()).optional().describe('Role IDs that can use this emoji'),
reason: z.string().optional().describe('Reason for creating the emoji'),
},
async ({ guildId, name, imageUrl, roles, reason }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const emoji = await guild.emojis.create({
attachment: imageUrl,
name,
roles,
reason,
});
return {
id: emoji.id,
name: emoji.name,
animated: emoji.animated,
identifier: emoji.identifier,
url: emoji.url,
message: 'Emoji created successfully',
};
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// Delete emoji
server.tool(
'delete_emoji',
'Delete a custom emoji from a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
emojiId: z.string().describe('The ID of the emoji to delete'),
reason: z.string().optional().describe('Reason for deleting the emoji'),
},
async ({ guildId, emojiId, reason }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const emoji = await guild.emojis.fetch(emojiId);
const emojiName = emoji.name;
await emoji.delete(reason);
return { emojiId, emojiName, message: 'Emoji deleted successfully' };
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// Modify emoji
server.tool(
'modify_emoji',
'Modify a custom emoji in a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
emojiId: z.string().describe('The ID of the emoji to modify'),
name: z.string().optional().describe('New name for the emoji'),
roles: z.array(z.string()).optional().describe('Role IDs that can use this emoji'),
reason: z.string().optional().describe('Reason for modifying the emoji'),
},
async ({ guildId, emojiId, name, roles, reason }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const emoji = await guild.emojis.fetch(emojiId);
const editData: { name?: string; roles?: string[]; reason?: string } = {};
if (name) editData.name = name;
if (roles) editData.roles = roles;
if (reason) editData.reason = reason;
const updated = await emoji.edit(editData);
return {
id: updated.id,
name: updated.name,
roles: updated.roles.cache.map((r) => ({ id: r.id, name: r.name })),
message: 'Emoji updated successfully',
};
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// List stickers
server.tool(
'list_stickers',
'List all custom stickers in a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
},
async ({ guildId }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const stickers = await guild.stickers.fetch();
return stickers.map((sticker) => ({
id: sticker.id,
name: sticker.name,
description: sticker.description,
tags: sticker.tags,
format: sticker.format,
available: sticker.available,
guildId: sticker.guildId,
user: sticker.user ? { id: sticker.user.id, username: sticker.user.username } : null,
url: sticker.url,
}));
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// Create sticker
server.tool(
'create_sticker',
'Create a custom sticker in a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
name: z.string().describe('Name for the sticker (2-30 characters)'),
description: z.string().describe('Description of the sticker'),
tags: z.string().describe('Autocomplete/suggestion tags for the sticker'),
imageUrl: z.string().describe('URL of the image (PNG, APNG, GIF, or Lottie JSON)'),
reason: z.string().optional().describe('Reason for creating the sticker'),
},
async ({ guildId, name, description, tags, imageUrl, reason }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const sticker = await guild.stickers.create({
file: imageUrl,
name,
description,
tags,
reason,
});
return {
id: sticker.id,
name: sticker.name,
description: sticker.description,
tags: sticker.tags,
url: sticker.url,
message: 'Sticker created successfully',
};
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// Delete sticker
server.tool(
'delete_sticker',
'Delete a custom sticker from a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
stickerId: z.string().describe('The ID of the sticker to delete'),
reason: z.string().optional().describe('Reason for deleting the sticker'),
},
async ({ guildId, stickerId, reason }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const sticker = await guild.stickers.fetch(stickerId);
const stickerName = sticker.name;
await sticker.delete(reason);
return { stickerId, stickerName, message: 'Sticker deleted successfully' };
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
// Modify sticker
server.tool(
'modify_sticker',
'Modify a custom sticker in a server',
{
guildId: z.string().describe('The ID of the server (guild)'),
stickerId: z.string().describe('The ID of the sticker to modify'),
name: z.string().optional().describe('New name for the sticker'),
description: z.string().optional().describe('New description'),
tags: z.string().optional().describe('New tags'),
reason: z.string().optional().describe('Reason for modifying'),
},
async ({ guildId, stickerId, name, description, tags, reason }) => {
const result = await withErrorHandling(async () => {
const client = await getDiscordClient();
const guild = await client.guilds.fetch(guildId);
const sticker = await guild.stickers.fetch(stickerId);
const editData: { name?: string; description?: string; tags?: string; reason?: string } = {};
if (name) editData.name = name;
if (description) editData.description = description;
if (tags) editData.tags = tags;
if (reason) editData.reason = reason;
const updated = await sticker.edit(editData);
return {
id: updated.id,
name: updated.name,
description: updated.description,
tags: updated.tags,
message: 'Sticker updated successfully',
};
});
if (!result.success) {
return { content: [{ type: 'text', text: result.error }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }] };
}
);
}