fix(next-config): move serverActions to experimental for Next.js 15+ compatibility
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧹 Lint (push) Failing after 18s
Monorepo Pipeline / 🧪 Test (push) Failing after 10s
Monorepo Pipeline / 🏗️ Build (push) Failing after 10s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped

This commit is contained in:
2026-04-13 12:30:31 +02:00
parent 8ec2ecb454
commit 0b929d00a1
17 changed files with 343 additions and 307 deletions

View File

@@ -24,7 +24,25 @@ const httpsAgent = new https.Agent({
const glitchtipClient = axios.create({
baseURL: `${GLITCHTIP_BASE_URL}/api/0`,
headers: { Authorization: `Bearer ${GLITCHTIP_API_KEY}` },
httpsAgent
httpsAgent,
timeout: 10000 // Add timeout to prevent hangs
});
// Logging middleware for GlitchTip client
glitchtipClient.interceptors.request.use((config) => {
console.error(`GlitchTip Request: ${config.method?.toUpperCase()} ${config.url}`);
return config;
}, (error) => {
console.error(`GlitchTip Request Error: ${error.message}`);
return Promise.reject(error);
});
glitchtipClient.interceptors.response.use((response) => {
return response;
}, (error) => {
const msg = error.response?.data?.detail || error.response?.data?.message || error.message;
console.error(`GlitchTip API Error (${error.config?.url}): ${msg}`);
return Promise.reject(error);
});
const LIST_PROJECTS_TOOL: Tool = {
@@ -88,48 +106,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "glitchtip_list_projects") {
try {
try {
if (request.params.name === "glitchtip_list_projects") {
const res = await glitchtipClient.get('/projects/');
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
} catch (e: any) {
return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] };
}
}
if (request.params.name === "glitchtip_list_issues") {
const { organization_slug, project_slug, query, limit = 20 } = request.params.arguments as any;
try {
if (request.params.name === "glitchtip_list_issues") {
const { organization_slug, project_slug, query, limit = 20 } = request.params.arguments as any;
const res = await glitchtipClient.get(`/projects/${organization_slug}/${project_slug}/issues/`, {
params: { query, limit }
});
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
} catch (e: any) {
return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] };
}
}
if (request.params.name === "glitchtip_get_issue_details") {
const { issue_id } = request.params.arguments as any;
try {
if (request.params.name === "glitchtip_get_issue_details") {
const { issue_id } = request.params.arguments as any;
const res = await glitchtipClient.get(`/issues/${issue_id}/`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
} catch (e: any) {
return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] };
}
}
if (request.params.name === "glitchtip_update_issue") {
const { issue_id, status } = request.params.arguments as any;
try {
if (request.params.name === "glitchtip_update_issue") {
const { issue_id, status } = request.params.arguments as any;
const res = await glitchtipClient.put(`/issues/${issue_id}/`, { status });
return { content: [{ type: "text", text: `Issue ${issue_id} status updated to ${status}.` }] };
} catch (e: any) {
return { isError: true, content: [{ type: "text", text: `Error: ${e.message}` }] };
}
}
throw new Error(`Unknown tool: ${request.params.name}`);
throw new Error(`Unknown tool: ${request.params.name}`);
} catch (e: any) {
const msg = e.response?.data?.detail || e.response?.data?.message || e.message;
return { isError: true, content: [{ type: "text", text: `GlitchTip Service Error: ${msg}` }] };
}
});
async function run() {
@@ -144,28 +151,51 @@ async function run() {
const app = express();
const transports = new Map<string, SSEServerTransport>();
// Manual CORS middleware
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-mcp-protocol-version');
if (req.method === 'OPTIONS') {
res.status(204).end();
return;
}
next();
});
app.use((req, _res, next) => {
console.error(`${req.method} ${req.url}`);
next();
});
app.get('/sse', async (req, res) => {
const sessionId = crypto.randomUUID();
const transport = new SSEServerTransport('/message', res as any);
const sessionId = transport.sessionId;
console.error(`New SSE connection: ${sessionId}`);
const transport = new SSEServerTransport(`/message/${sessionId}`, res);
transports.set(sessionId, transport);
const heartbeatInterval = setInterval(() => {
res.write(": heartbeat\n\n");
}, 15000);
req.on('close', () => {
console.error(`SSE connection closed: ${sessionId}`);
clearInterval(heartbeatInterval);
transports.delete(sessionId);
});
await server.connect(transport);
await new Promise((resolve) => {
req.on('close', resolve);
});
});
app.post('/message/:sessionId', async (req: Request, res: Response) => {
const sessionId = req.params.sessionId;
const transport = transports.get(sessionId as string);
app.post('/message', express.json(), async (req, res) => {
const sessionId = req.query.sessionId as string;
const transport = transports.get(sessionId);
console.error(`POST /message/${sessionId} - Active transports: ${transports.size}`);
if (!transport) {
console.error(`No transport found for session: ${sessionId}`);
@@ -175,9 +205,9 @@ async function run() {
await transport.handlePostMessage(req, res);
});
const PORT = process.env.GLITCHTIP_MCP_PORT || 3005;
app.listen(PORT, () => {
console.error(`GlitchTip MCP server running on http://localhost:${PORT}/sse`);
const PORT = Number(process.env.GLITCHTIP_MCP_PORT) || 3005;
app.listen(PORT, '0.0.0.0', () => {
console.error(`GlitchTip MCP server running on http://0.0.0.0:${PORT}/sse`);
});
}
}