import { z } from "zod";
import pino from "pino";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { InstallStore } from "./installStore.js";
import { getSlackClientForTeam } from "./slackClient.js";
const log = pino({ level: process.env.LOG_LEVEL ?? "info" });
export function registerTools(server: McpServer, store: InstallStore) {
server.registerTool(
"slack_post_message",
{
description: "Post a message to a Slack channel in the selected workspace.",
inputSchema: {
team_id: z.string().describe("Slack workspace/team ID (T...)"),
channel_id: z.string().describe("Channel/DM ID (C... or D...)"),
text: z.string().min(1).max(4000).describe("Message text to post"),
thread_ts: z.string().optional().describe("Thread timestamp to reply to (optional)")
}
},
async ({ team_id, channel_id, text, thread_ts }) => {
try {
const slack = await getSlackClientForTeam(store, team_id);
const r = await slack.chat.postMessage({
channel: channel_id,
text,
thread_ts
});
if (!r.ok) {
log.error({ team_id, channel_id, error: r.error }, "Failed to post Slack message");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: false, error: r.error ?? "unknown_error" }, null, 2)
}],
isError: true
};
}
log.info({ team_id, channel_id, ts: r.ts }, "Posted Slack message");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: r.ok, ts: r.ts, channel: r.channel }, null, 2)
}]
};
} catch (err: unknown) {
const error = err as Error;
log.error({ err: error, team_id, channel_id }, "Error posting Slack message");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: false, error: error.message }, null, 2)
}],
isError: true
};
}
}
);
server.registerTool(
"slack_get_channel_history",
{
description: "Read messages from a Slack channel in the selected workspace.",
inputSchema: {
team_id: z.string().describe("Slack workspace/team ID (T...)"),
channel_id: z.string().describe("Channel/DM ID (C... or D...)"),
limit: z.number().int().min(1).max(200).optional().describe("Number of messages to retrieve (default: 50)")
}
},
async ({ team_id, channel_id, limit }) => {
try {
const slack = await getSlackClientForTeam(store, team_id);
const r = await slack.conversations.history({
channel: channel_id,
limit: limit ?? 50
});
if (!r.ok) {
log.error({ team_id, channel_id, error: r.error }, "Failed to get channel history");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: false, error: r.error ?? "unknown_error" }, null, 2)
}],
isError: true
};
}
log.info({ team_id, channel_id, messageCount: r.messages?.length ?? 0 }, "Retrieved channel history");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: true, messages: r.messages ?? [] }, null, 2)
}]
};
} catch (err: unknown) {
const error = err as Error;
log.error({ err: error, team_id, channel_id }, "Error getting channel history");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: false, error: error.message }, null, 2)
}],
isError: true
};
}
}
);
server.registerTool(
"slack_list_channels",
{
description: "List all channels in the Slack workspace.",
inputSchema: {
team_id: z.string().describe("Slack workspace/team ID (T...)"),
types: z.string().optional().describe("Channel types: public_channel,private_channel,mpim,im (default: public_channel)"),
limit: z.number().int().min(1).max(1000).optional().describe("Number of channels to retrieve (default: 200)")
}
},
async ({ team_id, types, limit }) => {
try {
const slack = await getSlackClientForTeam(store, team_id);
const r = await slack.conversations.list({
types: types ?? "public_channel",
limit: limit ?? 200
});
if (!r.ok) {
log.error({ team_id, error: r.error }, "Failed to list channels");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: false, error: r.error ?? "unknown_error" }, null, 2)
}],
isError: true
};
}
log.info({ team_id, channelCount: r.channels?.length ?? 0 }, "Listed channels");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: true, channels: r.channels ?? [] }, null, 2)
}]
};
} catch (err: unknown) {
const error = err as Error;
log.error({ err: error, team_id }, "Error listing channels");
return {
content: [{
type: "text",
text: JSON.stringify({ ok: false, error: error.message }, null, 2)
}],
isError: true
};
}
}
);
}