#
tokens: 6396/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .cursor
│   └── rules
│       ├── bun-file.mdc
│       ├── bun-glob.mdc
│       ├── bun-test.mdc
│       ├── bun-utils.mdc
│       └── mcp.mdc
├── .cursorrules
├── .gitignore
├── bun.lock
├── CLAUDE.md
├── index.ts
├── package.json
├── README.md
├── spec.txt
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.cursorrules:
--------------------------------------------------------------------------------

```
1 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # dependencies (bun install)
 2 | node_modules
 3 | 
 4 | # output
 5 | out
 6 | dist
 7 | *.tgz
 8 | 
 9 | # code coverage
10 | coverage
11 | *.lcov
12 | 
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 | 
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 | 
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 | 
30 | # IntelliJ based IDEs
31 | .idea
32 | 
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Slack Search MCP Server
  2 | 
  3 | A Model Context Protocol (MCP) server that provides tools and resources to access Slack's search functionality. This server allows LLMs to search and retrieve users, channels, messages, and more from a Slack workspace.
  4 | 
  5 | ## Features
  6 | 
  7 | ### Tools
  8 | 
  9 | 1. `get_users` - Get a list of users in the Slack workspace
 10 | 2. `get_channels` - Get a list of channels in the Slack workspace
 11 | 3. `get_channel_messages` - Get messages from a specific channel
 12 | 4. `get_thread_replies` - Get replies in a thread
 13 | 5. `search_messages` - Search for messages in Slack
 14 | 
 15 | ### Resources
 16 | 
 17 | 1. `allusers://` - Get all users in the Slack workspace
 18 | 2. `allchannels://` - Get all channels in the Slack workspace
 19 | 
 20 | ## Requirements
 21 | 
 22 | - [Bun](https://bun.sh/) runtime
 23 | - Slack API token with appropriate permissions
 24 | 
 25 | ## Installation
 26 | 
 27 | 1. Clone the repository
 28 | 2. Install dependencies:
 29 |    ```bash
 30 |    bun install
 31 |    ```
 32 | 
 33 | ## Usage
 34 | 
 35 | 1. Set the Slack API token as an environment variable:
 36 |    ```bash
 37 |    export SLACK_TOKEN=xoxb-your-token-here
 38 |    ```
 39 | 
 40 | 2. Run the server:
 41 |    ```bash
 42 |    bun run index.ts
 43 |    ```
 44 | 
 45 |    Or use the compiled version:
 46 |    ```bash
 47 |    ./dist/slack_search_function_mcp
 48 |    ```
 49 | 
 50 | ## Building
 51 | 
 52 | To build the executable:
 53 | 
 54 | ```bash
 55 | bun run build
 56 | ```
 57 | 
 58 | This will create a compiled executable in the `dist` directory.
 59 | 
 60 | ## MCP Configuration
 61 | 
 62 | To use this server with an MCP-enabled LLM, add it to your MCP configuration:
 63 | 
 64 | ```json
 65 | {
 66 |   "mcpServers": {
 67 |     "slack": {
 68 |       "command": "/path/to/dist/slack_search_function_mcp",
 69 |       "env": {
 70 |         "SLACK_TOKEN": "xoxb-your-token-here"
 71 |       }
 72 |     }
 73 |   }
 74 | }
 75 | ```
 76 | 
 77 | ## Tool Examples
 78 | 
 79 | ### Get Users
 80 | 
 81 | ```json
 82 | {
 83 |   "name": "get_users",
 84 |   "arguments": {
 85 |     "limit": 10
 86 |   }
 87 | }
 88 | ```
 89 | 
 90 | ### Get Channels
 91 | 
 92 | ```json
 93 | {
 94 |   "name": "get_channels",
 95 |   "arguments": {
 96 |     "limit": 10,
 97 |     "exclude_archived": true
 98 |   }
 99 | }
100 | ```
101 | 
102 | ### Get Channel Messages
103 | 
104 | ```json
105 | {
106 |   "name": "get_channel_messages",
107 |   "arguments": {
108 |     "channel": "C01234ABCDE",
109 |     "limit": 10
110 |   }
111 | }
112 | ```
113 | 
114 | ### Get Thread Replies
115 | 
116 | ```json
117 | {
118 |   "name": "get_thread_replies",
119 |   "arguments": {
120 |     "channel": "C01234ABCDE",
121 |     "thread_ts": "1234567890.123456",
122 |     "limit": 10
123 |   }
124 | }
125 | ```
126 | 
127 | ### Search Messages
128 | 
129 | ```json
130 | {
131 |   "name": "search_messages",
132 |   "arguments": {
133 |     "query": "important announcement",
134 |     "sort": "timestamp",
135 |     "sort_dir": "desc",
136 |     "count": 10
137 |   }
138 | }
139 | ```
140 | 
141 | ## Resource Examples
142 | 
143 | ### Get All Users
144 | 
145 | ```
146 | allusers://
147 | ```
148 | 
149 | ### Get All Channels
150 | 
151 | ```
152 | allchannels://
153 | ```
154 | 
155 | ## Error Handling
156 | 
157 | The server includes comprehensive error handling for:
158 | - Invalid or missing Slack API token
159 | - API rate limits
160 | - Network errors
161 | - Invalid parameters
162 | - Authentication failures
163 | 
164 | ## Security
165 | 
166 | - The Slack API token is never logged or exposed in responses
167 | - The token is securely passed via environment variables
168 | 
```

--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------

```markdown
 1 | # CLAUDE.md - Guidelines for Bun MCP Server
 2 | 
 3 | ## Implementation Rules
 4 | 
 5 | - See [mcp.mdc](.cursor/rules/mcp.mdc) at first and learn how to implement a MCP Server.
 6 | - Generate a appropriate package name and overwrite package.json
 7 | - Some examples using `setRequestHandler()` to implement a tool or resource, but it's deprecated. Use `server.tool()` or `server.resource()` instead.
 8 | - You can add a thrid party package if needed to meet the requirements.
 9 | - You can use Bun runtime features. Bun provides many built-in functions. See [bun-file.mdc](.cursor/rules/bun-file.mdc), [bun-test.mdc](.cursor/rules/bun-test.mdc), [bun-glob.mdc](.cursor/rules/bun-glob.mdc), [bun-utils.mdc](.cursor/rules/bun-utils.mdc)
10 | 
11 | ## Build Commands
12 | 
13 | - `bun run build` - Build the MCP server executable
14 | - `bun run show-package-name` - Display the package name for installation
15 | - Install with: `cp dist/$npm_package_name $HOME/bin/`
16 | 
17 | ## Code Style Guidelines
18 | 
19 | ### Imports & Organization
20 | 
21 | - Use named imports from MCP SDK: `import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"`
22 | - Group imports by external packages first, then internal modules
23 | 
24 | ### TypeScript & Types
25 | 
26 | - Use Zod for parameter validation in tools and resources
27 | - Prefer TypeScript strict mode with explicit type annotations
28 | - Use async/await for asynchronous operations
29 | 
30 | ### Naming Conventions
31 | 
32 | - CamelCase for variables and functions
33 | - PascalCase for classes and types
34 | - Use descriptive names for resources, tools and prompts
35 | 
36 | ### MCP Best Practices
37 | 
38 | - Resources should be pure and not have side effects (like GET endpoints)
39 | - Tools should handle specific actions with well-defined parameters (like POST endpoints)
40 | - Write a enough description for tool and each parameters.
41 | - Use ResourceTemplate for parameterized resources
42 | - Properly handle errors in tool implementations and return isError: true
43 | 
44 | ### Error Handling
45 | 
46 | - Use try/catch blocks with specific error types
47 | - Return proper error responses with descriptive messages
48 | - Always close connections and free resources in finally blocks
49 | 
50 | ## References
51 | 
52 | - [Basic Examples](.cursor/rules/basic.mdc)
53 | 
54 | ## Another Examples
55 | 
56 | - [@modelcontextprotocol/server-memory](https://github.com/modelcontextprotocol/servers/blob/main/src/memory/index.ts)
57 | - [@modelcontextprotocol/server-filesystem](https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/index.ts)
58 | - [redis](https://github.com/modelcontextprotocol/servers/blob/main/src/redis/src/index.ts)
59 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "slack_search_function_mcp",
 3 |   "module": "index.ts",
 4 |   "type": "module",
 5 |   "private": true,
 6 |   "scripts": {
 7 |     "build": "mkdir -p dist && bun build --compile --outfile=dist/$npm_package_name index.ts",
 8 |     "show-package-name": "echo $npm_package_name"
 9 |   },
10 |   "devDependencies": {
11 |     "@types/bun": "latest"
12 |   },
13 |   "peerDependencies": {
14 |     "typescript": "^5"
15 |   },
16 |   "dependencies": {
17 |     "@modelcontextprotocol/sdk": "^1.6.0",
18 |     "@slack/web-api": "^7.8.0",
19 |     "zod": "^3.24.2"
20 |   }
21 | }
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     // Enable latest features
 4 |     "lib": ["ESNext", "DOM"],
 5 |     "target": "ESNext",
 6 |     "module": "ESNext",
 7 |     "moduleDetection": "force",
 8 |     "jsx": "react-jsx",
 9 |     "allowJs": true,
10 | 
11 |     // Bundler mode
12 |     "moduleResolution": "bundler",
13 |     "allowImportingTsExtensions": true,
14 |     "verbatimModuleSyntax": true,
15 |     "noEmit": true,
16 | 
17 |     // Best practices
18 |     "strict": true,
19 |     "skipLibCheck": true,
20 |     "noFallthroughCasesInSwitch": true,
21 | 
22 |     // Some stricter flags (disabled by default)
23 |     "noUnusedLocals": false,
24 |     "noUnusedParameters": false,
25 |     "noPropertyAccessFromIndexSignature": false
26 |   }
27 | }
28 | 
```

--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env bun
  2 | import {
  3 |   McpServer,
  4 |   ResourceTemplate,
  5 | } from "@modelcontextprotocol/sdk/server/mcp.js";
  6 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  7 | import { z } from "zod";
  8 | import { WebClient, ErrorCode as SlackErrorCode } from "@slack/web-api";
  9 | import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
 10 | 
 11 | // Get Slack API token from environment variables
 12 | const SLACK_TOKEN = process.env.SLACK_TOKEN;
 13 | 
 14 | if (!SLACK_TOKEN) {
 15 |   console.error("Error: SLACK_TOKEN environment variable is required");
 16 |   process.exit(1);
 17 | }
 18 | 
 19 | // Create Slack Web Client
 20 | const slack = new WebClient(SLACK_TOKEN);
 21 | 
 22 | // Create an MCP server
 23 | const server = new McpServer({
 24 |   name: "slack-search-mcp",
 25 |   version: "1.0.0",
 26 | });
 27 | 
 28 | // Validate Slack token on startup
 29 | async function validateSlackToken() {
 30 |   try {
 31 |     await slack.auth.test();
 32 |     console.error("Successfully connected to Slack API");
 33 |   } catch (error: any) {
 34 |     console.error("Failed to connect to Slack API:", error);
 35 |     process.exit(1);
 36 |   }
 37 | }
 38 | 
 39 | // Common schemas
 40 | const tokenSchema = z.string().describe("Slack API token");
 41 | 
 42 | // Common error handling function
 43 | function handleSlackError(error: any): never {
 44 |   console.error("Slack API error:", error);
 45 |   
 46 |   if (error.code === SlackErrorCode.PlatformError) {
 47 |     throw new McpError(
 48 |       ErrorCode.InternalError,
 49 |       `Slack API error: ${error.data?.error || "Unknown error"}`
 50 |     );
 51 |   } else if (error.code === SlackErrorCode.RequestError) {
 52 |     throw new McpError(
 53 |       ErrorCode.InternalError,
 54 |       "Network error when connecting to Slack API"
 55 |     );
 56 |   } else if (error.code === SlackErrorCode.RateLimitedError) {
 57 |     throw new McpError(
 58 |       ErrorCode.InternalError,
 59 |       "Rate limited by Slack API"
 60 |     );
 61 |   } else if (error.code === SlackErrorCode.HTTPError) {
 62 |     throw new McpError(
 63 |       ErrorCode.InternalError,
 64 |       `HTTP error: ${error.statusCode}`
 65 |     );
 66 |   } else {
 67 |     throw new McpError(
 68 |       ErrorCode.InternalError,
 69 |       `Unexpected error: ${error.message || "Unknown error"}`
 70 |     );
 71 |   }
 72 | }
 73 | 
 74 | // Tool: get_users
 75 | server.tool(
 76 |   "get_users",
 77 |   "Get a list of users in the Slack workspace",
 78 |   {
 79 |     token: tokenSchema.optional(),
 80 |     limit: z.number().min(1).max(1000).optional().describe("Maximum number of users to return"),
 81 |     cursor: z.string().optional().describe("Pagination cursor for fetching next page"),
 82 |   },
 83 |   async ({ token = SLACK_TOKEN, limit = 100, cursor }) => {
 84 |     try {
 85 |       const response = await slack.users.list({
 86 |         token,
 87 |         limit,
 88 |         cursor,
 89 |       });
 90 | 
 91 |       return {
 92 |         content: [
 93 |           {
 94 |             type: "text",
 95 |             text: JSON.stringify({
 96 |               users: response.members,
 97 |               next_cursor: response.response_metadata?.next_cursor,
 98 |               has_more: !!response.response_metadata?.next_cursor,
 99 |             }, null, 2),
100 |           },
101 |         ],
102 |       };
103 |     } catch (error: any) {
104 |       handleSlackError(error);
105 |     }
106 |   }
107 | );
108 | 
109 | // Tool: get_channels
110 | server.tool(
111 |   "get_channels",
112 |   "Get a list of channels in the Slack workspace",
113 |   {
114 |     token: tokenSchema.optional(),
115 |     limit: z.number().min(1).max(1000).optional().describe("Maximum number of channels to return"),
116 |     cursor: z.string().optional().describe("Pagination cursor for fetching next page"),
117 |     exclude_archived: z.boolean().optional().describe("Exclude archived channels"),
118 |     types: z.string().optional().describe("Types of channels to include (public_channel, private_channel, mpim, im)"),
119 |   },
120 |   async ({ token = SLACK_TOKEN, limit = 100, cursor, exclude_archived = true, types = "public_channel,private_channel" }) => {
121 |     try {
122 |       const response = await slack.conversations.list({
123 |         token,
124 |         limit,
125 |         cursor,
126 |         exclude_archived,
127 |         types,
128 |       });
129 | 
130 |       return {
131 |         content: [
132 |           {
133 |             type: "text",
134 |             text: JSON.stringify({
135 |               channels: response.channels,
136 |               next_cursor: response.response_metadata?.next_cursor,
137 |               has_more: !!response.response_metadata?.next_cursor,
138 |             }, null, 2),
139 |           },
140 |         ],
141 |       };
142 |     } catch (error: any) {
143 |       handleSlackError(error);
144 |     }
145 |   }
146 | );
147 | 
148 | // Tool: get_channel_messages
149 | server.tool(
150 |   "get_channel_messages",
151 |   "Get messages from a specific channel",
152 |   {
153 |     token: tokenSchema.optional(),
154 |     channel: z.string().describe("Channel ID"),
155 |     limit: z.number().min(1).max(1000).optional().describe("Maximum number of messages to return"),
156 |     oldest: z.string().optional().describe("Start of time range (Unix timestamp)"),
157 |     latest: z.string().optional().describe("End of time range (Unix timestamp)"),
158 |     inclusive: z.boolean().optional().describe("Include messages with timestamps matching oldest or latest"),
159 |     cursor: z.string().optional().describe("Pagination cursor for fetching next page"),
160 |   },
161 |   async ({ token = SLACK_TOKEN, channel, limit = 100, oldest, latest, inclusive, cursor }) => {
162 |     try {
163 |       // Validate channel ID format
164 |       if (!channel.match(/^[A-Z0-9]+$/i)) {
165 |         throw new McpError(
166 |           ErrorCode.InvalidParams,
167 |           "Invalid channel ID format"
168 |         );
169 |       }
170 | 
171 |       const response = await slack.conversations.history({
172 |         token,
173 |         channel,
174 |         limit,
175 |         oldest,
176 |         latest,
177 |         inclusive,
178 |         cursor,
179 |       });
180 | 
181 |       return {
182 |         content: [
183 |           {
184 |             type: "text",
185 |             text: JSON.stringify({
186 |               messages: response.messages,
187 |               has_more: response.has_more,
188 |               next_cursor: response.response_metadata?.next_cursor,
189 |             }, null, 2),
190 |           },
191 |         ],
192 |       };
193 |     } catch (error: any) {
194 |       handleSlackError(error);
195 |     }
196 |   }
197 | );
198 | 
199 | // Tool: get_thread_replies
200 | server.tool(
201 |   "get_thread_replies",
202 |   "Get replies in a thread",
203 |   {
204 |     token: tokenSchema.optional(),
205 |     channel: z.string().describe("Channel ID"),
206 |     thread_ts: z.string().describe("Timestamp of the parent message"),
207 |     limit: z.number().min(1).max(1000).optional().describe("Maximum number of replies to return"),
208 |     oldest: z.string().optional().describe("Start of time range (Unix timestamp)"),
209 |     latest: z.string().optional().describe("End of time range (Unix timestamp)"),
210 |     inclusive: z.boolean().optional().describe("Include messages with timestamps matching oldest or latest"),
211 |     cursor: z.string().optional().describe("Pagination cursor for fetching next page"),
212 |   },
213 |   async ({ token = SLACK_TOKEN, channel, thread_ts, limit = 100, oldest, latest, inclusive, cursor }) => {
214 |     try {
215 |       // Validate channel ID format
216 |       if (!channel.match(/^[A-Z0-9]+$/i)) {
217 |         throw new McpError(
218 |           ErrorCode.InvalidParams,
219 |           "Invalid channel ID format"
220 |         );
221 |       }
222 | 
223 |       // Validate thread_ts format (Unix timestamp)
224 |       if (!thread_ts.match(/^\d+\.\d+$/)) {
225 |         throw new McpError(
226 |           ErrorCode.InvalidParams,
227 |           "Invalid thread_ts format. Expected Unix timestamp (e.g., 1234567890.123456)"
228 |         );
229 |       }
230 | 
231 |       const response = await slack.conversations.replies({
232 |         token,
233 |         channel,
234 |         ts: thread_ts,
235 |         limit,
236 |         oldest,
237 |         latest,
238 |         inclusive,
239 |         cursor,
240 |       });
241 | 
242 |       return {
243 |         content: [
244 |           {
245 |             type: "text",
246 |             text: JSON.stringify({
247 |               messages: response.messages,
248 |               has_more: response.has_more,
249 |               next_cursor: response.response_metadata?.next_cursor,
250 |             }, null, 2),
251 |           },
252 |         ],
253 |       };
254 |     } catch (error: any) {
255 |       handleSlackError(error);
256 |     }
257 |   }
258 | );
259 | 
260 | // Tool: search_messages
261 | server.tool(
262 |   "search_messages",
263 |   "Search for messages in Slack",
264 |   {
265 |     token: tokenSchema.optional(),
266 |     query: z.string().describe("Search query"),
267 |     sort: z.enum(["score", "timestamp"]).optional().describe("Sort by relevance or timestamp"),
268 |     sort_dir: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
269 |     highlight: z.boolean().optional().describe("Whether to highlight the matches"),
270 |     count: z.number().min(1).max(100).optional().describe("Number of results to return per page"),
271 |     page: z.number().min(1).optional().describe("Page number of results to return"),
272 |   },
273 |   async ({ token = SLACK_TOKEN, query, sort = "score", sort_dir = "desc", highlight = true, count = 20, page = 1 }) => {
274 |     try {
275 |       const response = await slack.search.messages({
276 |         token,
277 |         query,
278 |         sort,
279 |         sort_dir,
280 |         highlight,
281 |         count,
282 |         page,
283 |       });
284 | 
285 |       return {
286 |         content: [
287 |           {
288 |             type: "text",
289 |             text: JSON.stringify({
290 |               messages: response.messages,
291 |               pagination: response.messages?.pagination,
292 |               total: response.messages?.total,
293 |             }, null, 2),
294 |           },
295 |         ],
296 |       };
297 |     } catch (error: any) {
298 |       handleSlackError(error);
299 |     }
300 |   }
301 | );
302 | 
303 | // Resource: all_users
304 | server.resource(
305 |   "all_users",
306 |   new ResourceTemplate("allusers://", { list: undefined }),
307 |   async (uri) => {
308 |     try {
309 |       // Get all users (handle pagination internally)
310 |       const allUsers: any[] = [];
311 |       let cursor;
312 |       let hasMore = true;
313 | 
314 |       while (hasMore) {
315 |         const response = await slack.users.list({
316 |           token: SLACK_TOKEN,
317 |           limit: 1000,
318 |           cursor,
319 |         });
320 | 
321 |         if (response.members) {
322 |           allUsers.push(...response.members);
323 |         }
324 | 
325 |         cursor = response.response_metadata?.next_cursor;
326 |         hasMore = !!cursor;
327 |       }
328 | 
329 |       return {
330 |         contents: [
331 |           {
332 |             uri: uri.href,
333 |             text: JSON.stringify(allUsers, null, 2),
334 |             mimeType: "application/json",
335 |           },
336 |         ],
337 |       };
338 |     } catch (error: any) {
339 |       console.error("Error fetching all users:", error);
340 |       throw new McpError(
341 |         ErrorCode.InternalError,
342 |         `Failed to fetch all users: ${error.message || "Unknown error"}`
343 |       );
344 |     }
345 |   }
346 | );
347 | 
348 | // Resource: all_channels
349 | server.resource(
350 |   "all_channels",
351 |   new ResourceTemplate("allchannels://", { list: undefined }),
352 |   async (uri) => {
353 |     try {
354 |       // Get all channels (handle pagination internally)
355 |       const allChannels: any[] = [];
356 |       let cursor;
357 |       let hasMore = true;
358 | 
359 |       while (hasMore) {
360 |         const response = await slack.conversations.list({
361 |           token: SLACK_TOKEN,
362 |           limit: 1000,
363 |           cursor,
364 |           types: "public_channel,private_channel",
365 |         });
366 | 
367 |         if (response.channels) {
368 |           allChannels.push(...response.channels);
369 |         }
370 | 
371 |         cursor = response.response_metadata?.next_cursor;
372 |         hasMore = !!cursor;
373 |       }
374 | 
375 |       return {
376 |         contents: [
377 |           {
378 |             uri: uri.href,
379 |             text: JSON.stringify(allChannels, null, 2),
380 |             mimeType: "application/json",
381 |           },
382 |         ],
383 |       };
384 |     } catch (error: any) {
385 |       console.error("Error fetching all channels:", error);
386 |       throw new McpError(
387 |         ErrorCode.InternalError,
388 |         `Failed to fetch all channels: ${error.message || "Unknown error"}`
389 |       );
390 |     }
391 |   }
392 | );
393 | 
394 | async function main() {
395 |   try {
396 |     // Validate Slack token before starting the server
397 |     await validateSlackToken();
398 | 
399 |     // Start receiving messages on stdin and sending messages on stdout
400 |     const transport = new StdioServerTransport();
401 |     await server.connect(transport);
402 |     console.error("Slack Search MCP server running on stdio");
403 |   } catch (error: any) {
404 |     console.error("Failed to start MCP server:", error);
405 |     process.exit(1);
406 |   }
407 | }
408 | 
409 | if (import.meta.main) {
410 |   main();
411 | }
412 | 
```