HubSpot MCP Server
by SheffieldP
Verified
const express = require('express');
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware to parse JSON bodies
app.use(express.json());
// Serve static files from public directory
app.use(express.static('public'));
// CORS headers
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,POST');
res.setHeader('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-HubSpot-Access-Token');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
next();
});
// Handle MCP protocol messages
const handleMcpRequest = async (req, res) => {
const body = req.body;
console.log('Received MCP request:', JSON.stringify(body));
// Extract access token
const accessToken = req.headers['x-hubspot-access-token'] || body.accessToken || body.hubspotAccessToken;
if (!accessToken) {
return res.status(400).json({
error: 'Missing access token',
message: 'HubSpot access token must be provided in either the X-HubSpot-Access-Token header or in the request body'
});
}
// Basic MCP initialization response
if (body.mcp === true || body.action === 'initialize') {
return res.status(200).json({
mcp: true,
version: '1.0.0',
name: 'HubSpot MCP Server',
status: 'ready'
});
}
try {
// Get the path to the MCP server script
const scriptPath = path.join(process.cwd(), 'src', 'mcp_server_hubspot', 'server.py');
console.log('Script path:', scriptPath);
console.log('Script exists:', fs.existsSync(scriptPath));
// Spawn a Python process to run the MCP server
const pythonProcess = spawn('python', [scriptPath, accessToken]);
let responseData = '';
let errorData = '';
// Send the input to the Python process
pythonProcess.stdin.write(JSON.stringify(body) + '\n');
pythonProcess.stdin.end();
// Listen for data from the Python process
pythonProcess.stdout.on('data', (data) => {
console.log('Python output:', data.toString());
responseData += data.toString();
});
// Listen for errors
pythonProcess.stderr.on('data', (data) => {
console.error('Python error:', data.toString());
errorData += data.toString();
});
// Wait for the process to exit
const exitCode = await new Promise((resolve) => {
pythonProcess.on('close', (code) => {
console.log('Python process exited with code:', code);
resolve(code);
});
});
if (exitCode !== 0 || errorData) {
console.error('Error output:', errorData);
if (exitCode === 127) { // Command not found
return res.status(500).json({
error: 'Python execution failed',
details: 'Python command not found. Please make sure Python is installed.',
errorOutput: errorData
});
}
return res.status(500).json({
error: 'Internal server error',
details: errorData || `Process exited with code ${exitCode}`
});
}
// Try to parse the response as JSON
try {
const jsonResponse = JSON.parse(responseData);
return res.status(200).json(jsonResponse);
} catch (e) {
// If we can't parse as JSON, just return the raw output
console.log('Could not parse Python output as JSON:', e.message);
return res.status(200).send(responseData || 'No output from Python script');
}
} catch (error) {
console.error('Error processing request:', error);
return res.status(500).json({
error: 'Internal server error',
details: error.message
});
}
};
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
// Test endpoint
app.get('/ping', (req, res) => {
res.status(200).send('pong');
});
// Debug endpoint
app.get('/debug', async (req, res) => {
const pythonInfo = {};
try {
const pythonVersionCmd = spawn('python', ['--version']);
let pythonVersion = '';
pythonVersionCmd.stdout.on('data', (data) => {
pythonVersion += data.toString();
});
await new Promise((resolve) => {
pythonVersionCmd.on('close', (code) => {
pythonInfo.versionExitCode = code;
pythonInfo.version = pythonVersion.trim();
resolve();
});
});
} catch (e) {
pythonInfo.error = e.message;
}
res.status(200).json({
nodeVersion: process.version,
cwd: process.cwd(),
pythonInfo,
serverPath: path.join(process.cwd(), 'src', 'mcp_server_hubspot', 'server.py'),
serverExists: fs.existsSync(path.join(process.cwd(), 'src', 'mcp_server_hubspot', 'server.py')),
env: process.env.NODE_ENV || 'development'
});
});
// Debug Python environment endpoint
app.get('/debug-python', async (req, res) => {
try {
// Get the path to the debug script
const scriptPath = path.join(process.cwd(), 'src', 'mcp_server_hubspot', 'debug.py');
console.log('Debug script path:', scriptPath);
console.log('Debug script exists:', fs.existsSync(scriptPath));
// Spawn a Python process to run the debug script
const pythonProcess = spawn('python', [scriptPath]);
let responseData = '';
let errorData = '';
// Listen for data from the Python process
pythonProcess.stdout.on('data', (data) => {
console.log('Python debug output:', data.toString());
responseData += data.toString();
});
// Listen for errors
pythonProcess.stderr.on('data', (data) => {
console.error('Python debug error:', data.toString());
errorData += data.toString();
});
// Wait for the process to exit
const exitCode = await new Promise((resolve) => {
pythonProcess.on('close', (code) => {
console.log('Python debug process exited with code:', code);
resolve(code);
});
});
res.status(200).json({
pythonOutput: responseData,
pythonError: errorData,
exitCode
});
} catch (error) {
console.error('Error in debug-python endpoint:', error);
res.status(500).json({
error: 'Internal server error',
details: error.message
});
}
});
// Echo endpoint for debugging
app.post('/echo', (req, res) => {
const accessToken = req.headers['x-hubspot-access-token'] || req.body.accessToken || req.body.hubspotAccessToken;
res.status(200).json({
receivedBody: req.body,
receivedToken: accessToken ? '[PRESENT]' : '[MISSING]'
});
});
// Main MCP endpoint
app.post('/', handleMcpRequest);
// Start the server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});