YouTube MCP Integration
by spolepaka
Verified
import express, { Request, Response } from 'express';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { z } from 'zod';
import path from 'path';
import { fileURLToPath } from 'url';
// Get directory name in ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Initialize Express app
const app = express();
app.use(express.json());
// Debug middleware to log all requests
app.use((req: Request, res: Response, next: () => void) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
next();
});
// Add CORS middleware
app.use((req: Request, res: Response, next: () => void) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// Handle OPTIONS requests
app.options('*', (req: Request, res: Response) => {
res.status(200).end();
});
// Serve the index.html file
app.get('/', (req: Request, res: Response) => {
res.sendFile(path.join(__dirname, '..', 'public', 'simple-test.html'));
});
// Create a simple MCP server
const server = new McpServer({
name: "simple-mcp-test",
version: "1.0.0",
description: "Simple MCP test server"
});
// Define a simple echo tool
server.tool(
"echo",
{ message: z.string() },
async ({ message }) => {
console.log(`Echo tool called with message: ${message}`);
return {
content: [{ type: "text", text: `You said: ${message}` }]
};
}
);
// Map to store active transports
const activeTransports = new Map<string, SSEServerTransport>();
// Session endpoint
app.get("/session", (req: Request, res: Response) => {
try {
const sessionId = Math.random().toString(36).substring(2, 15);
console.log(`New session created: ${sessionId}`);
res.json({ sessionId });
} catch (error) {
console.error('Error creating session:', error);
res.status(500).json({ error: 'Failed to create session' });
}
});
// SSE endpoint
app.get("/sse", async (req: Request, res: Response) => {
// Use a provided session ID or create a default one
const sessionId = (req.query.sessionId as string) || 'default-session';
try {
console.log(`Creating SSE transport for session ${sessionId}`);
// If there's an existing transport for this session ID, remove it
if (activeTransports.has(sessionId)) {
console.log(`Replacing existing transport for session ${sessionId}`);
activeTransports.delete(sessionId);
}
// Create a transport
const transport = new SSEServerTransport("/messages", res);
// Store the transport
activeTransports.set(sessionId, transport);
console.log(`Active sessions: ${Array.from(activeTransports.keys()).join(', ')}`);
// Connect the server to the transport
await server.connect(transport);
// Handle client disconnect
req.on('close', () => {
console.log(`Client disconnected: ${sessionId}`);
activeTransports.delete(sessionId);
});
} catch (error) {
console.error(`Error with SSE connection for session ${sessionId}:`, error);
res.end();
activeTransports.delete(sessionId);
}
});
// Message endpoint with additional format handling
app.post("/messages", async (req: Request, res: Response) => {
// Use a provided session ID or use the default one
const sessionId = (req.query.sessionId as string) || 'default-session';
console.log(`Message received for session ${sessionId}`);
console.log(`Active sessions: ${Array.from(activeTransports.keys()).join(', ')}`);
console.log('Request body:', JSON.stringify(req.body, null, 2)); // Log the full request
const transport = activeTransports.get(sessionId);
if (!transport) {
console.log(`Session ${sessionId} not found in active transports`);
return res.status(404).json({
error: { code: -32000, message: "No active connection found. Please connect to the SSE endpoint first." }
});
}
try {
console.log(`Handling message for session ${sessionId}`);
// Handle the special case for call_tool method
const body = req.body;
if (body.method === 'call_tool' && body.jsonrpc === '2.0' && body.params) {
const { name, arguments: args } = body.params;
if (name === 'echo') {
console.log(`Custom handling for echo tool with message: ${args.message}`);
// Directly return the result without going through transport
return res.json({
jsonrpc: '2.0',
result: {
content: [{ type: "text", text: `You said: ${args.message}` }]
},
id: body.id
});
}
}
// If it's not a special case, proceed with normal handling
await transport.handlePostMessage(req, res);
console.log(`Successfully handled message for session ${sessionId}`);
} catch (error) {
// More detailed error logging
console.error('Error handling message:', error);
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
return res.status(500).json({
error: {
code: -32603,
message: error instanceof Error ? error.message : "Failed to process message",
data: error instanceof Error ? error.stack : undefined
}
});
}
});
// Create a simplified endpoint for direct tool calls
app.post("/simple-call", async (req: Request, res: Response) => {
// Make sessionId optional
const sessionId = (req.query.sessionId as string) || 'default-session';
const { name, arguments: args } = req.body;
console.log(`Simple call, tool: ${name}`);
console.log('Arguments:', JSON.stringify(args, null, 2));
if (!name || !args) {
return res.status(400).json({ error: "Missing name or arguments" });
}
// Find the tool implementation directly
if (name === "echo") {
const message = args.message;
console.log(`Executing echo with message: ${message}`);
// Return a successful response in the expected format
return res.json({
result: {
content: [{ type: "text", text: `You said: ${message}` }]
}
});
} else {
return res.status(404).json({ error: "Tool not found" });
}
});
// Add a simple test endpoint
app.get("/test", (req: Request, res: Response) => {
res.json({ status: "ok", message: "Server is working!" });
});
// Handle GET requests to /messages (needed for MCP SDK initialization)
app.get("/messages", (req: Request, res: Response) => {
console.log('GET request to /messages endpoint received');
// Just return a 200 OK status to acknowledge the endpoint exists
res.status(200).json({ status: "ok", message: "Messages endpoint available for POST requests" });
});
// Start the server
const PORT = 3002;
app.listen(PORT, () => {
console.log('--------------------------------------------------');
console.log(`Simple MCP Test Server running on http://localhost:${PORT}`);
console.log('--------------------------------------------------');
console.log('MCP Endpoints:');
console.log(` - Test Page: http://localhost:${PORT}/`);
console.log(` - SSE: http://localhost:${PORT}/sse`);
console.log(` - Messages: http://localhost:${PORT}/messages`);
console.log(' (Session ID is optional for both SSE and Messages endpoints)');
console.log('--------------------------------------------------');
console.log('Direct API Endpoints (No MCP):');
console.log(` - Test: http://localhost:${PORT}/test`);
console.log(` - Simple Call: http://localhost:${PORT}/simple-call`);
console.log('--------------------------------------------------');
});