feat(mcps): unify SSE/Stdio transport and fix handshake timeouts
This commit is contained in:
@@ -7,12 +7,17 @@ import {
|
||||
Tool,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import axios from "axios";
|
||||
import https from "https";
|
||||
|
||||
const UMAMI_BASE_URL = process.env.UMAMI_BASE_URL || "https://umami.infra.mintel.me";
|
||||
const UMAMI_USERNAME = process.env.UMAMI_USERNAME;
|
||||
const UMAMI_PASSWORD = process.env.UMAMI_PASSWORD;
|
||||
const UMAMI_API_KEY = process.env.UMAMI_API_KEY; // optional if using API key auth
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
|
||||
if (!UMAMI_USERNAME && !UMAMI_API_KEY) {
|
||||
console.error("Warning: Neither UMAMI_USERNAME/PASSWORD nor UMAMI_API_KEY is set.");
|
||||
}
|
||||
@@ -28,12 +33,13 @@ async function getAuthHeaders(): Promise<Record<string, string>> {
|
||||
const res = await axios.post(`${UMAMI_BASE_URL}/api/auth/login`, {
|
||||
username: UMAMI_USERNAME,
|
||||
password: UMAMI_PASSWORD,
|
||||
});
|
||||
}, { httpsAgent });
|
||||
cachedToken = res.data.token;
|
||||
}
|
||||
return { Authorization: `Bearer ${cachedToken}` };
|
||||
}
|
||||
|
||||
|
||||
// --- Tool Definitions ---
|
||||
const LIST_WEBSITES_TOOL: Tool = {
|
||||
name: "umami_list_websites",
|
||||
@@ -147,7 +153,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const headers = await getAuthHeaders();
|
||||
const api = axios.create({ baseURL: `${UMAMI_BASE_URL}/api`, headers });
|
||||
const api = axios.create({ baseURL: `${UMAMI_BASE_URL}/api`, headers, httpsAgent });
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
@@ -236,24 +242,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
|
||||
// --- Express / SSE Server ---
|
||||
async function run() {
|
||||
const app = express();
|
||||
let transport: SSEServerTransport | null = null;
|
||||
const isStdio = process.argv.includes('--stdio');
|
||||
|
||||
app.get('/sse', async (req, res) => {
|
||||
console.error('New SSE connection established');
|
||||
transport = new SSEServerTransport('/message', res);
|
||||
if (isStdio) {
|
||||
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
});
|
||||
console.error('Umami MCP server is running on stdio');
|
||||
} else {
|
||||
const app = express();
|
||||
let transport: SSEServerTransport | null = null;
|
||||
|
||||
app.post('/message', async (req, res) => {
|
||||
if (!transport) { res.status(400).send('No active SSE connection'); return; }
|
||||
await transport.handlePostMessage(req, res);
|
||||
});
|
||||
app.get('/sse', async (req, res) => {
|
||||
console.error('New SSE connection established');
|
||||
transport = new SSEServerTransport('/message', res);
|
||||
await server.connect(transport);
|
||||
});
|
||||
|
||||
const PORT = process.env.UMAMI_MCP_PORT || 3003;
|
||||
app.listen(PORT, () => {
|
||||
console.error(`Umami MCP server running on http://localhost:${PORT}/sse`);
|
||||
});
|
||||
app.post('/message', async (req, res) => {
|
||||
if (!transport) {
|
||||
res.status(400).send('No active SSE connection');
|
||||
return;
|
||||
}
|
||||
await transport.handlePostMessage(req, res);
|
||||
});
|
||||
|
||||
const PORT = process.env.UMAMI_MCP_PORT || 3003;
|
||||
app.listen(PORT, () => {
|
||||
console.error(`Umami MCP server running on http://localhost:${PORT}/sse`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
|
||||
Reference in New Issue
Block a user