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
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:
@@ -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`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user