diff --git a/slack-mcp/server/lib/types.ts b/slack-mcp/server/lib/types.ts index 177e684a..4b9c4a23 100644 --- a/slack-mcp/server/lib/types.ts +++ b/slack-mcp/server/lib/types.ts @@ -141,6 +141,7 @@ export interface SlackChannel { is_member: boolean; created: number; creator: string; + user?: string; topic?: { value: string; creator: string; diff --git a/slack-mcp/server/slack/handlers/eventHandler.ts b/slack-mcp/server/slack/handlers/eventHandler.ts index 4e81d292..0996d695 100644 --- a/slack-mcp/server/slack/handlers/eventHandler.ts +++ b/slack-mcp/server/slack/handlers/eventHandler.ts @@ -17,6 +17,7 @@ import { addReaction, removeReaction, deleteMessage, + getUserInfo, } from "../../lib/slack-client.ts"; import type { SlackEvent, @@ -845,8 +846,29 @@ async function handleDirectMessage( await removeReaction(channel, ts, "eyes"); } + // Resolve sender name so the LLM knows who it's talking to + let senderText = text; + try { + const userInfo = await getUserInfo(user); + const senderName = userInfo + ? userInfo.profile?.display_name || userInfo.real_name || userInfo.name + : null; + if (senderName) { + senderText = `[Mensagem de ${senderName}]\n${text}`; + console.log(`[EventHandler] DM sender resolved: ${senderName}`); + } + } catch (err) { + console.warn(`[EventHandler] Failed to resolve DM sender name:`, err); + } + console.log(`[EventHandler] Building LLM messages for DM...`); - const messages = await buildLLMMessages(channel, text, ts, undefined, media); + const messages = await buildLLMMessages( + channel, + senderText, + ts, + undefined, + media, + ); console.log(`[EventHandler] LLM messages built: ${messages.length} messages`); messages.forEach((msg, i) => { console.log( diff --git a/slack-mcp/server/tools/channels.ts b/slack-mcp/server/tools/channels.ts index 098f8607..beb49c8d 100644 --- a/slack-mcp/server/tools/channels.ts +++ b/slack-mcp/server/tools/channels.ts @@ -14,6 +14,7 @@ import { getChannelMembers, openDM, inviteToChannel, + getUserInfo, } from "../lib/slack-client.ts"; /** @@ -98,6 +99,80 @@ export const createListChannelsTool = (_env: Env) => }, }); +/** + * List all direct message conversations + */ +export const createListDMsTool = (_env: Env) => + createTool({ + id: "SLACK_LIST_DMS", + description: + "List all direct message conversations the bot has. Returns channel IDs that can be used with SLACK_GET_CHANNEL_HISTORY to read DM messages.", + annotations: { readOnlyHint: true }, + inputSchema: z + .object({ + limit: z + .number() + .optional() + .default(100) + .describe("Maximum number of DMs to return"), + }) + .strict(), + outputSchema: z + .object({ + success: z.boolean(), + dms: z.array( + z.object({ + channel_id: z.string(), + user_id: z.string(), + user_name: z.string(), + }), + ), + error: z.string().optional(), + }) + .strict(), + execute: async ({ context }: { context: unknown }) => { + const input = context as { limit?: number }; + + try { + const channels = await listChannels({ + types: "im", + limit: input.limit, + skipCache: true, + }); + + const imChannels = channels.filter((c) => c.is_im && c.user); + + const dms = await Promise.all( + imChannels.map(async (c) => { + const userInfo = await getUserInfo(c.user!); + const userName = userInfo + ? userInfo.profile?.display_name || + userInfo.real_name || + userInfo.name + : "(unknown user)"; + + return { + channel_id: c.id, + user_id: c.user!, + user_name: userName, + }; + }), + ); + + return { + success: true, + dms, + }; + } catch (error) { + return { + success: false, + dms: [], + error: error instanceof Error ? error.message : String(error), + }; + } + }, + }); + /** * Get channel info */ @@ -356,6 +431,7 @@ export const createInviteToChannelTool = (_env: Env) => */ export const channelTools = [ createListChannelsTool, + createListDMsTool, createGetChannelInfoTool, createJoinChannelTool, createGetChannelMembersTool, diff --git a/slack-mcp/tsconfig.json b/slack-mcp/tsconfig.json index cf5c6b05..ae4ab5c0 100644 --- a/slack-mcp/tsconfig.json +++ b/slack-mcp/tsconfig.json @@ -21,13 +21,6 @@ "noUnusedParameters": false, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, - /* Path Aliases */ - "baseUrl": ".", - "paths": { - "server/*": [ - "./server/*" - ] - }, /* Types */ "types": [ "@types/node"