#!/usr/bin/env node
/**
* HTTP Server for Todoist MCP in stateless mode.
*
* This server provides an alternative to the hosted service at ai.todoist.net/mcp.
* Each request creates a fresh transport and MCP server instance — no session
* tracking, timeouts, or cleanup required.
*
* Environment variables:
* - TODOIST_API_KEY: Required. Your Todoist API key.
* - TODOIST_BASE_URL: Optional. Custom Todoist API base URL.
* - PORT: Optional. Server port (default: 3000).
*
* @see https://github.com/Doist/todoist-ai/issues/239
*/
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
import dotenv from 'dotenv'
import express, { type Request, type Response } from 'express'
import { getMcpServer } from './mcp-server.js'
dotenv.config({ quiet: true })
const PORT = Number.parseInt(process.env.PORT || '3000', 10)
function main() {
const baseUrl = process.env.TODOIST_BASE_URL
const todoistApiKey = process.env.TODOIST_API_KEY
if (!todoistApiKey) {
console.error('Error: TODOIST_API_KEY environment variable is required')
process.exit(1)
}
const app = express()
app.use(express.json())
// Health check endpoint
app.get('/health', (_req: Request, res: Response) => {
res.json({ status: 'ok' })
})
// MCP endpoint - POST for requests
app.post('/mcp', async (req: Request, res: Response) => {
try {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
})
const server = getMcpServer({ todoistApiKey, baseUrl })
await server.connect(transport)
await transport.handleRequest(req, res, req.body)
} catch (error) {
console.error('[Error] Request handling failed:', error)
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: 'Internal server error',
},
id: null,
})
}
})
// MCP endpoint - GET returns 405 (needed for MCP client compatibility)
app.get('/mcp', (_req: Request, res: Response) => {
res.status(405).set('Allow', 'POST').send('Method Not Allowed')
})
app.listen(PORT, () => {
console.error(`Todoist MCP HTTP Server started on port ${PORT}`)
console.error(`MCP endpoint: http://localhost:${PORT}/mcp`)
console.error(`Health check: http://localhost:${PORT}/health`)
})
}
main()