index.ts•27.1 kB
import { FastMCP } from "fastmcp"
import { z } from "zod"
import { initializeRedditClient, getRedditClient } from "./client/reddit-client"
import { formatUserInfo, formatPostInfo, formatSubredditInfo } from "./utils/formatters"
import dotenv from "dotenv"
// Load environment variables
dotenv.config()
// Initialize Reddit client
async function setupRedditClient() {
const clientId = process.env.REDDIT_CLIENT_ID
const clientSecret = process.env.REDDIT_CLIENT_SECRET
const userAgent = process.env.REDDIT_USER_AGENT || "RedditMCPServer/1.1.0"
const username = process.env.REDDIT_USERNAME
const password = process.env.REDDIT_PASSWORD
if (!clientId || !clientSecret) {
console.error(
"[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables.",
)
process.exit(1)
}
try {
const client = initializeRedditClient({
clientId,
clientSecret,
userAgent,
username,
password,
})
console.error("[Setup] Reddit client initialized")
console.error("[Setup] Testing Reddit API connection...")
// Test the connection by attempting authentication
const isConnected = await client.checkAuthentication()
if (!isConnected) {
console.error("[Error] ✗ Failed to connect to Reddit API")
console.error("[Error] Please check your REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET")
process.exit(1)
}
console.error("[Setup] ✓ Reddit API connection successful")
if (username && password) {
console.error(`[Setup] ✓ User authenticated as: ${username}`)
console.error("[Setup] Write operations enabled (posting, replying, editing, deleting)")
} else {
console.error("[Setup] Running in read-only mode (client credentials only)")
console.error("[Setup] For write operations, set REDDIT_USERNAME and REDDIT_PASSWORD")
}
} catch (error) {
console.error("[Error] ✗ Reddit API connection failed:", error instanceof Error ? error.message : error)
console.error("[Error] Please verify your Reddit API credentials")
process.exit(1)
}
}
// Create FastMCP server
const server = new FastMCP({
name: "reddit-mcp-server",
version: "1.1.0",
instructions: `A comprehensive Reddit MCP server that provides tools for interacting with Reddit API.
Available capabilities:
- Fetch Reddit posts, comments, and user information
- Get subreddit details and statistics
- Search Reddit content across posts and subreddits
- Create posts and reply to posts/comments (with authentication)
- Edit your own posts and comments (with authentication)
- Delete your own posts and comments (with authentication)
- Analyze engagement metrics and community insights
For write operations (posting, replying, editing, deleting), ensure REDDIT_USERNAME and REDDIT_PASSWORD are configured.`,
// Optional OAuth configuration for HTTP transport
...(process.env.OAUTH_ENABLED === "true" && {
authenticate: async (request) => {
const authHeader = request.headers.authorization
const expectedToken = process.env.OAUTH_TOKEN
if (!expectedToken) {
// If OAuth is enabled but no token configured, generate one
const token = Array.from({ length: 32 }, () =>
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(Math.random() * 62)),
).join("")
console.log(`[Auth] Generated OAuth token: ${token}`)
throw new Response(
JSON.stringify({
error: "No OAuth token configured",
generatedToken: token,
}),
{
status: 401,
headers: { "Content-Type": "application/json" },
},
)
}
if (!authHeader?.startsWith("Bearer ")) {
throw new Response(null, {
status: 401,
statusText: "Missing or invalid Authorization header",
})
}
const token = authHeader.slice(7)
if (token !== expectedToken) {
throw new Response(null, {
status: 403,
statusText: "Invalid token",
})
}
return { authenticated: true }
},
}),
})
// Test tool
server.addTool({
name: "test_reddit_mcp_server",
description: "Test the Reddit MCP Server connection and configuration",
parameters: z.object({}),
execute: async () => {
const client = getRedditClient()
const hasAuth = client ? "✓" : "✗"
const hasWriteAccess = process.env.REDDIT_USERNAME && process.env.REDDIT_PASSWORD ? "✓" : "✗"
return `Reddit MCP Server Status:
- Server: ✓ Running
- Reddit Client: ${hasAuth} ${client ? "Initialized" : "Not initialized"}
- Write Access: ${hasWriteAccess} ${hasWriteAccess === "✓" ? "Available" : "Read-only mode"}
- Version: 1.1.0
Ready to handle Reddit API requests!`
},
})
// User tools
server.addTool({
name: "get_user_info",
description: "Get detailed information about a Reddit user including karma, account status, and activity analysis",
parameters: z.object({
username: z.string().describe("The Reddit username (without u/ prefix)"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const user = await client.getUser(args.username)
const formattedUser = formatUserInfo(user)
return `# User Information: u/${formattedUser.username}
## Profile Overview
- Username: u/${formattedUser.username}
- Karma:
- Comment Karma: ${formattedUser.karma.commentKarma.toLocaleString()}
- Post Karma: ${formattedUser.karma.postKarma.toLocaleString()}
- Total Karma: ${formattedUser.karma.totalKarma.toLocaleString()}
- Account Status: ${formattedUser.accountStatus.join(", ")}
- Account Created: ${formattedUser.accountCreated}
- Profile URL: ${formattedUser.profileUrl}
## Activity Analysis
- ${formattedUser.activityAnalysis.replace(/\n - /g, "\n- ")}
## Recommendations
- ${formattedUser.recommendations.replace(/\n {2}- /g, "\n- ")}`
},
})
server.addTool({
name: "get_user_posts",
description: "Get recent posts by a Reddit user with sorting and filtering options",
parameters: z.object({
username: z.string().describe("The Reddit username (without u/ prefix)"),
sort: z.enum(["new", "hot", "top"]).default("new").describe("Sort order for posts"),
time_filter: z
.enum(["hour", "day", "week", "month", "year", "all"])
.default("all")
.describe("Time filter for top posts"),
limit: z.number().min(1).max(100).default(10).describe("Number of posts to retrieve"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const posts = await client.getUserPosts(args.username, {
sort: args.sort,
timeFilter: args.time_filter,
limit: args.limit,
})
if (posts.length === 0) {
return `No posts found for u/${args.username} with the specified filters.`
}
const postSummaries = posts
.map((post, index) => {
const flags = [...(post.over18 ? ["**NSFW**"] : []), ...(post.spoiler ? ["**Spoiler**"] : [])]
return `### ${index + 1}. ${post.title} ${flags.join(" ")}
- Subreddit: r/${post.subreddit}
- Score: ${post.score.toLocaleString()} (${(post.upvoteRatio * 100).toFixed(1)}% upvoted)
- Comments: ${post.numComments.toLocaleString()}
- Posted: ${new Date(post.createdUtc * 1000).toLocaleString()}
- Link: https://reddit.com${post.permalink}`
})
.join("\n\n")
return `# Posts by u/${args.username} (${args.sort} - ${args.time_filter})
${postSummaries}`
},
})
server.addTool({
name: "get_user_comments",
description: "Get recent comments by a Reddit user with sorting and filtering options",
parameters: z.object({
username: z.string().describe("The Reddit username (without u/ prefix)"),
sort: z.enum(["new", "hot", "top"]).default("new").describe("Sort order for comments"),
time_filter: z
.enum(["hour", "day", "week", "month", "year", "all"])
.default("all")
.describe("Time filter for top comments"),
limit: z.number().min(1).max(100).default(10).describe("Number of comments to retrieve"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const comments = await client.getUserComments(args.username, {
sort: args.sort,
timeFilter: args.time_filter,
limit: args.limit,
})
if (comments.length === 0) {
return `No comments found for u/${args.username} with the specified filters.`
}
const commentSummaries = comments
.map((comment, index) => {
const truncatedBody = comment.body.length > 300 ? comment.body.substring(0, 300) + "..." : comment.body
const flags = [...(comment.edited ? ["*(edited)*"] : []), ...(comment.isSubmitter ? ["**OP**"] : [])]
return `### ${index + 1}. Comment ${flags.join(" ")}
In r/${comment.subreddit} on "${comment.submissionTitle}"
> ${truncatedBody}
- Score: ${comment.score.toLocaleString()}
- Posted: ${new Date(comment.createdUtc * 1000).toLocaleString()}
- Link: https://reddit.com${comment.permalink}`
})
.join("\n\n")
return `# Comments by u/${args.username} (${args.sort} - ${args.time_filter})
${commentSummaries}`
},
})
// Post tools
server.addTool({
name: "get_reddit_post",
description:
"Get detailed information about a specific Reddit post including content, stats, and engagement analysis",
parameters: z.object({
subreddit: z.string().describe("The subreddit name (without r/ prefix)"),
post_id: z.string().describe("The Reddit post ID"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const post = await client.getPost(args.post_id, args.subreddit)
const formattedPost = formatPostInfo(post)
return `# Post from r/${formattedPost.subreddit}
## Post Details
- Title: ${formattedPost.title}
- Type: ${formattedPost.type}
- Author: u/${formattedPost.author}
## Content
${formattedPost.content}
## Stats
- Score: ${formattedPost.stats.score.toLocaleString()}
- Upvote Ratio: ${(formattedPost.stats.upvoteRatio * 100).toFixed(1)}%
- Comments: ${formattedPost.stats.comments.toLocaleString()}
## Metadata
- Posted: ${formattedPost.metadata.posted}
- Flags: ${formattedPost.metadata.flags.length ? formattedPost.metadata.flags.join(", ") : "None"}
- Flair: ${formattedPost.metadata.flair}
## Links
- Full Post: ${formattedPost.links.fullPost}
- Short Link: ${formattedPost.links.shortLink}
## Engagement Analysis
- ${formattedPost.engagementAnalysis.replace(/\n - /g, "\n- ")}
## Best Time to Engage
${formattedPost.bestTimeToEngage}`
},
})
server.addTool({
name: "get_top_posts",
description: "Get top posts from a subreddit or from the Reddit home feed",
parameters: z.object({
subreddit: z.string().optional().describe("The subreddit name (without r/ prefix). Leave empty for home feed"),
time_filter: z
.enum(["hour", "day", "week", "month", "year", "all"])
.default("week")
.describe("Time period for top posts"),
limit: z.number().min(1).max(100).default(10).describe("Number of posts to retrieve"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const posts = await client.getTopPosts(args.subreddit || "", args.time_filter, args.limit)
if (posts.length === 0) {
const location = args.subreddit ? `r/${args.subreddit}` : "home feed"
return `No posts found in ${location} for the specified time period.`
}
const formattedPosts = posts.map(formatPostInfo)
const postSummaries = formattedPosts
.map(
(post, index) => `### ${index + 1}. ${post.title}
- Author: u/${post.author}
- Score: ${post.stats.score.toLocaleString()} (${(post.stats.upvoteRatio * 100).toFixed(1)}% upvoted)
- Comments: ${post.stats.comments.toLocaleString()}
- Posted: ${post.metadata.posted}
- Link: ${post.links.shortLink}`,
)
.join("\n\n")
const location = args.subreddit ? `r/${args.subreddit}` : "Home Feed"
return `# Top Posts from ${location} (${args.time_filter})
${postSummaries}`
},
})
// Subreddit tools
server.addTool({
name: "get_subreddit_info",
description: "Get detailed information about a subreddit including description, stats, and community analysis",
parameters: z.object({
subreddit_name: z.string().describe("The subreddit name (without r/ prefix)"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const subreddit = await client.getSubredditInfo(args.subreddit_name)
const formattedSubreddit = formatSubredditInfo(subreddit)
return `# Subreddit Information: r/${formattedSubreddit.name}
## Overview
- Name: r/${formattedSubreddit.name}
- Title: ${formattedSubreddit.title}
- Subscribers: ${formattedSubreddit.stats.subscribers.toLocaleString()}
- Active Users: ${
typeof formattedSubreddit.stats.activeUsers === "number"
? formattedSubreddit.stats.activeUsers.toLocaleString()
: formattedSubreddit.stats.activeUsers
}
## Description
${formattedSubreddit.description.short}
## Detailed Description
${formattedSubreddit.description.full}
## Metadata
- Created: ${formattedSubreddit.metadata.created}
- Flags: ${formattedSubreddit.metadata.flags.join(", ")}
## Links
- Subreddit: ${formattedSubreddit.links.subreddit}
- Wiki: ${formattedSubreddit.links.wiki}
## Community Analysis
- ${formattedSubreddit.communityAnalysis.replace(/\n - /g, "\n- ")}
## Engagement Tips
- ${formattedSubreddit.engagementTips.replace(/\n - /g, "\n- ")}`
},
})
server.addTool({
name: "get_trending_subreddits",
description: "Get a list of currently trending subreddits",
parameters: z.object({}),
execute: async () => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
const trendingSubreddits = await client.getTrendingSubreddits()
return `# Trending Subreddits
${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).join("\n")}`
},
})
// Search tools
server.addTool({
name: "search_reddit",
description: "Search Reddit for posts and content across subreddits",
parameters: z.object({
query: z.string().describe("Search query"),
subreddit: z.string().optional().describe("Limit search to specific subreddit (without r/ prefix)"),
sort: z.enum(["relevance", "hot", "top", "new", "comments"]).default("relevance").describe("Sort order"),
time_filter: z.enum(["hour", "day", "week", "month", "year", "all"]).default("all").describe("Time filter"),
limit: z.number().min(1).max(100).default(10).describe("Number of results"),
type: z.enum(["link", "sr", "user"]).default("link").describe("Type of content to search"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
if (!args.query || args.query.trim() === "") {
throw new Error("Search query cannot be empty")
}
const posts = await client.searchReddit(args.query, {
subreddit: args.subreddit,
sort: args.sort,
timeFilter: args.time_filter,
limit: args.limit,
type: args.type,
})
if (posts.length === 0) {
const searchLocation = args.subreddit ? ` in r/${args.subreddit}` : ""
return `No results found for "${args.query}"${searchLocation}.`
}
const searchResults = posts
.map((post, index) => {
const flags = [...(post.over18 ? ["**NSFW**"] : []), ...(post.spoiler ? ["**Spoiler**"] : [])]
return `### ${index + 1}. ${post.title} ${flags.join(" ")}
- Subreddit: r/${post.subreddit}
- Author: u/${post.author}
- Score: ${post.score.toLocaleString()} (${(post.upvoteRatio * 100).toFixed(1)}% upvoted)
- Comments: ${post.numComments.toLocaleString()}
- Posted: ${new Date(post.createdUtc * 1000).toLocaleString()}
- Link: https://reddit.com${post.permalink}`
})
.join("\n\n")
const searchLocation = args.subreddit ? ` in r/${args.subreddit}` : ""
return `# Reddit Search Results for: "${args.query}"${searchLocation}
Sorted by: ${args.sort} | Time: ${args.time_filter} | Type: ${args.type}
${searchResults}`
},
})
// Write tools (require user authentication)
server.addTool({
name: "create_post",
description: "Create a new post in a subreddit (requires REDDIT_USERNAME and REDDIT_PASSWORD)",
parameters: z.object({
subreddit: z.string().describe("The subreddit name (without r/ prefix)"),
title: z.string().describe("The post title"),
content: z.string().describe("The post content (text for self posts, URL for link posts)"),
is_self: z.boolean().default(true).describe("Whether this is a self post (text) or link post"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
// Check if user credentials are configured
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
throw new Error(
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables.",
)
}
const post = await client.createPost(args.subreddit, args.title, args.content, args.is_self)
const formattedPost = formatPostInfo(post)
return `# Post Created Successfully
## Post Details
- Title: ${formattedPost.title}
- Subreddit: r/${formattedPost.subreddit}
- Type: ${formattedPost.type}
- Link: ${formattedPost.links.fullPost}
Your post has been successfully submitted to r/${formattedPost.subreddit}.`
},
})
server.addTool({
name: "reply_to_post",
description: "Post a reply to an existing Reddit post or comment (requires REDDIT_USERNAME and REDDIT_PASSWORD)",
parameters: z.object({
post_id: z.string().describe("The Reddit post ID (thing_id, e.g., t3_xxxxx for posts, t1_xxxxx for comments)"),
content: z.string().describe("The reply content"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
// Check if user credentials are configured
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
throw new Error(
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables.",
)
}
const comment = await client.replyToPost(args.post_id, args.content)
return `# Reply Posted Successfully
## Comment Details
- Posted to: ${args.post_id}
- Author: u/${process.env.REDDIT_USERNAME}
- Comment ID: ${comment.id}
Your reply has been successfully posted.`
},
})
server.addTool({
name: "delete_post",
description:
"Delete your own Reddit post (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
parameters: z.object({
thing_id: z
.string()
.describe(
"The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing.",
),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
// Check if user credentials are configured
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
throw new Error(
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables.",
)
}
await client.deletePost(args.thing_id)
return `# Post Deleted Successfully
The post ${args.thing_id} has been permanently deleted from Reddit.
**Note**: This action cannot be undone. The post content has been removed and cannot be recovered.`
},
})
server.addTool({
name: "delete_comment",
description:
"Delete your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
parameters: z.object({
thing_id: z
.string()
.describe(
"The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing.",
),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
// Check if user credentials are configured
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
throw new Error(
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables.",
)
}
await client.deleteComment(args.thing_id)
return `# Comment Deleted Successfully
The comment ${args.thing_id} has been permanently deleted from Reddit.
**Note**: This action cannot be undone. The comment content has been removed and cannot be recovered.`
},
})
server.addTool({
name: "edit_post",
description:
"Edit your own Reddit post (self-text posts only, requires REDDIT_USERNAME and REDDIT_PASSWORD). You can only edit the text content of self posts, not titles or link posts.",
parameters: z.object({
thing_id: z
.string()
.describe(
"The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing.",
),
new_text: z.string().describe("The new text content for the post. Supports Reddit markdown formatting."),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
// Check if user credentials are configured
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
throw new Error(
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables.",
)
}
await client.editPost(args.thing_id, args.new_text)
return `# Post Edited Successfully
The post ${args.thing_id} has been updated with your new content.
**Note**:
- Only self (text) posts can be edited
- Post titles cannot be edited
- Link posts cannot be edited
- An "edited" marker will appear on your post`
},
})
server.addTool({
name: "edit_comment",
description:
"Edit your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). Update the text content of a comment you previously posted.",
parameters: z.object({
thing_id: z
.string()
.describe(
"The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing.",
),
new_text: z.string().describe("The new text content for the comment. Supports Reddit markdown formatting."),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
// Check if user credentials are configured
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
throw new Error(
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables.",
)
}
await client.editComment(args.thing_id, args.new_text)
return `# Comment Edited Successfully
The comment ${args.thing_id} has been updated with your new content.
**Note**: An "edited" marker will appear on your comment to show it has been modified.`
},
})
// Comment tools
server.addTool({
name: "get_post_comments",
description: "Get comments from a specific Reddit post",
parameters: z.object({
post_id: z.string().describe("The Reddit post ID"),
subreddit: z.string().describe("The subreddit name (without r/ prefix)"),
sort: z.enum(["best", "top", "new", "controversial", "old", "qa"]).default("best").describe("Comment sort order"),
limit: z.number().min(1).max(500).default(100).describe("Maximum number of comments to retrieve"),
}),
execute: async (args) => {
const client = getRedditClient()
if (!client) {
throw new Error("Reddit client not initialized")
}
if (!args.post_id || !args.subreddit) {
throw new Error("post_id and subreddit are required")
}
const data = await client.getPostComments(args.post_id, args.subreddit, {
sort: args.sort,
limit: args.limit,
})
const post = data.post
const comments = data.comments
let response = `# Comments for: ${post.title}
**Post by u/${post.author} in r/${post.subreddit}**
- Score: ${post.score.toLocaleString()} | Comments: ${post.numComments.toLocaleString()}
- Posted: ${new Date(post.createdUtc * 1000).toLocaleString()}
---
`
if (comments.length === 0) {
response += "No comments found for this post."
return response
}
const commentSummaries = comments
.map((comment) => {
const indent = "└─".repeat(Math.min(comment.depth || 0, 3))
const authorBadge = comment.isSubmitter ? " **[OP]**" : ""
const editedBadge = comment.edited ? " *(edited)*" : ""
return `${indent} **u/${comment.author}**${authorBadge}${editedBadge} (${comment.score.toLocaleString()} points)
${comment.body}
---`
})
.join("\n\n")
response += commentSummaries
return response
},
})
// Initialize and start server
async function main() {
try {
await setupRedditClient()
// Default to HTTP on port 3000, unless explicitly using stdio (for npx/CLI)
const useStdio = process.env.TRANSPORT_TYPE === "stdio"
const port = parseInt(process.env.PORT || "3000")
const host = process.env.HOST || "0.0.0.0"
if (useStdio) {
console.error("[Setup] Starting in stdio mode (CLI/npx)")
await server.start({
transportType: "stdio",
})
} else {
console.error(`[Setup] Starting HTTP server on ${host}:${port}`)
await server.start({
transportType: "httpStream",
httpStream: {
port,
host,
endpoint: "/mcp",
},
})
console.error(`[Setup] HTTP server ready at http://${host}:${port}/mcp`)
console.error(`[Setup] SSE endpoint available at http://${host}:${port}/sse`)
}
} catch (error) {
console.error("[Error] Failed to start server:", error)
process.exit(1)
}
}
// Handle graceful shutdown
process.on("SIGINT", async () => {
console.error("[Shutdown] Shutting down Reddit MCP Server...")
process.exit(0)
})
process.on("SIGTERM", async () => {
console.error("[Shutdown] Shutting down Reddit MCP Server...")
process.exit(0)
})
main().catch(console.error)