Skip to main content
Glama
server.js39.6 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import axios from 'axios'; // Configuration const API_BASE_URL = 'http://10.10.10.117:3000/api'; const REPORTS_ENDPOINT = `${API_BASE_URL}/report`; const VULNERABILITY_ENDPOINT = `${API_BASE_URL}/vulnerability`; // JWT Configuration - read from environment variable let JWT_TOKEN = process.env.REPORTS_JWT_TOKEN; // Create server instance const server = new Server( { name: 'reports-server', version: '0.1.0', }, { capabilities: { tools: {}, }, } ); // Helper function to get bearer token function getBearerToken(providedToken) { // If a token is provided in the request, use it if (providedToken) { return providedToken; } // Otherwise, use the configured JWT token if (JWT_TOKEN) { return JWT_TOKEN; } // If no token is available, throw an error throw new McpError( ErrorCode.InvalidParams, 'No bearer token provided. Either pass bearerToken parameter or set REPORTS_JWT_TOKEN environment variable.' ); } // Update a report async function updateReport(providedToken, reportId, reportData) { try { const bearerToken = getBearerToken(providedToken); // Validate reportId format (should be MongoDB ObjectId) if (!reportId || !reportId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid reportId format. Must be a valid MongoDB ObjectId (24 characters)' ); } // Validate status if provided if (reportData.status !== undefined) { const validStatuses = ['Draft', 'In Progress', 'Submitted', 'Reviewed', 'Closed']; if (!validStatuses.includes(reportData.status)) { throw new McpError( ErrorCode.InvalidParams, `Status must be one of: ${validStatuses.join(', ')}` ); } } const response = await axios.put(`${REPORTS_ENDPOINT}/${reportId}`, reportData, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 15000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Successfully updated report ${reportId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${REPORTS_ENDPOINT}/${reportId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Get a specific report by ID async function getReport(providedToken, reportId) { try { const bearerToken = getBearerToken(providedToken); // Validate reportId format (should be MongoDB ObjectId) if (!reportId || !reportId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid reportId format. Must be a valid MongoDB ObjectId (24 characters)' ); } const response = await axios.get(`${REPORTS_ENDPOINT}/${reportId}`, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Retrieved report ${reportId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${REPORTS_ENDPOINT}/${reportId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Get all reports function async function getAllReports(providedToken) { try { const bearerToken = getBearerToken(providedToken); const response = await axios.get(REPORTS_ENDPOINT, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Retrieved ${response.data?.length || 0} reports`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${REPORTS_ENDPOINT}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Create a new report function async function createReport(providedToken, reportData) { try { const bearerToken = getBearerToken(providedToken); // Build the report payload with default templateId if not provided const payload = { title: reportData.title || "", platform: reportData.platform || "", templateId: reportData.templateId || "67b1dac12c8d23272ad47cbd", testers: reportData.testers || [] }; const response = await axios.post(REPORTS_ENDPOINT, payload, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: 'Report created successfully', }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${REPORTS_ENDPOINT}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Get a specific vulnerability by ID async function getVulnerability(providedToken, vulnerabilityId) { try { const bearerToken = getBearerToken(providedToken); // Validate vulnerabilityId format (should be MongoDB ObjectId) if (!vulnerabilityId || !vulnerabilityId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid vulnerabilityId format. Must be a valid MongoDB ObjectId (24 characters)' ); } const response = await axios.get(`${VULNERABILITY_ENDPOINT}/${vulnerabilityId}`, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Retrieved vulnerability ${vulnerabilityId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${VULNERABILITY_ENDPOINT}/${vulnerabilityId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Helper function to auto-format text content as HTML function formatAsHTML(content, fieldType = 'paragraph') { if (!content || typeof content !== 'string') { return content; } // If already contains HTML tags, return as-is if (content.includes('<') && content.includes('>')) { return content; } // For simple text, wrap in appropriate HTML tags if (fieldType === 'list') { // Split by newlines or common list separators and create bullet list const items = content.split(/\n|;|,|\|/).map(item => item.trim()).filter(item => item); if (items.length > 1) { return '<ul>' + items.map(item => `<li>${item}</li>`).join('') + '</ul>'; } } // Default: wrap in paragraph tags return `<p>${content}</p>`; } // Update a vulnerability async function updateVulnerability(providedToken, vulnerabilityId, vulnerabilityData) { try { const bearerToken = getBearerToken(providedToken); // Validate vulnerabilityId format (should be MongoDB ObjectId) if (!vulnerabilityId || !vulnerabilityId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid vulnerabilityId format. Must be a valid MongoDB ObjectId (24 characters)' ); } // Validate vulnerability data if (vulnerabilityData.cvssScore !== undefined) { if (typeof vulnerabilityData.cvssScore !== 'number' || vulnerabilityData.cvssScore < 0 || vulnerabilityData.cvssScore > 10) { throw new McpError( ErrorCode.InvalidParams, 'CVSS Score must be a number between 0.0 and 10.0' ); } } if (vulnerabilityData.severity !== undefined) { const validSeverities = ['Informational', 'Low', 'Medium', 'High', 'Critical']; if (!validSeverities.includes(vulnerabilityData.severity)) { throw new McpError( ErrorCode.InvalidParams, `Severity must be one of: ${validSeverities.join(', ')}` ); } } if (vulnerabilityData.cvss !== undefined) { if (typeof vulnerabilityData.cvss !== 'string' || !vulnerabilityData.cvss.startsWith('CVSS:3.1/')) { throw new McpError( ErrorCode.InvalidParams, 'CVSS vector must be a valid CVSS 3.1 string starting with "CVSS:3.1/"' ); } } const response = await axios.put(`${VULNERABILITY_ENDPOINT}/${vulnerabilityId}`, vulnerabilityData, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 15000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Successfully updated vulnerability ${vulnerabilityId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${VULNERABILITY_ENDPOINT}/${vulnerabilityId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Delete a vulnerability async function deleteVulnerability(providedToken, vulnerabilityId) { try { const bearerToken = getBearerToken(providedToken); // Validate vulnerabilityId format (should be MongoDB ObjectId) if (!vulnerabilityId || !vulnerabilityId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid vulnerabilityId format. Must be a valid MongoDB ObjectId (24 characters)' ); } const response = await axios.delete(`${VULNERABILITY_ENDPOINT}/${vulnerabilityId}`, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Successfully deleted vulnerability ${vulnerabilityId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${VULNERABILITY_ENDPOINT}/${vulnerabilityId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Get vulnerabilities for a report async function getVulnerabilities(providedToken, reportId) { try { const bearerToken = getBearerToken(providedToken); // Validate reportId format (should be MongoDB ObjectId) if (!reportId || !reportId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid reportId format. Must be a valid MongoDB ObjectId (24 characters)' ); } const response = await axios.get(`${VULNERABILITY_ENDPOINT}/report/${reportId}`, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 10000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Retrieved vulnerabilities for report ${reportId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${VULNERABILITY_ENDPOINT}/report/${reportId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // Create vulnerabilities for a report async function createVulnerabilities(providedToken, reportId, vulnerabilities) { try { const bearerToken = getBearerToken(providedToken); // Validate reportId format (should be MongoDB ObjectId) if (!reportId || !reportId.match(/^[0-9a-fA-F]{24}$/)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid reportId format. Must be a valid MongoDB ObjectId (24 characters)' ); } // Ensure vulnerabilities is an array if (!Array.isArray(vulnerabilities)) { vulnerabilities = [vulnerabilities]; } // Validate and format each vulnerability object for (const vuln of vulnerabilities) { if (!vuln.title || typeof vuln.title !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'Each vulnerability must have a title (string)' ); } if (!vuln.description || typeof vuln.description !== 'string') { throw new McpError( ErrorCode.InvalidParams, 'Each vulnerability must have a description (HTML string)' ); } // Auto-format content fields as HTML vuln.description = formatAsHTML(vuln.description); if (vuln.details) vuln.details = formatAsHTML(vuln.details); if (vuln.impact) vuln.impact = formatAsHTML(vuln.impact, 'list'); if (vuln.remediation) vuln.remediation = formatAsHTML(vuln.remediation, 'list'); // Validate CVSS fields if provided if (vuln.cvssScore !== undefined) { if (typeof vuln.cvssScore !== 'number' || vuln.cvssScore < 0 || vuln.cvssScore > 10) { throw new McpError( ErrorCode.InvalidParams, 'CVSS Score must be a number between 0.0 and 10.0' ); } } if (vuln.severity !== undefined) { const validSeverities = ['Informational', 'Low', 'Medium', 'High', 'Critical']; if (!validSeverities.includes(vuln.severity)) { throw new McpError( ErrorCode.InvalidParams, `Severity must be one of: ${validSeverities.join(', ')}` ); } } if (vuln.cvss !== undefined) { if (typeof vuln.cvss !== 'string' || !vuln.cvss.startsWith('CVSS:3.1/')) { throw new McpError( ErrorCode.InvalidParams, 'CVSS vector must be a valid CVSS 3.1 string starting with "CVSS:3.1/"' ); } } } const response = await axios.post(`${VULNERABILITY_ENDPOINT}/${reportId}`, vulnerabilities, { headers: { 'Authorization': `Bearer ${bearerToken}`, 'Content-Type': 'application/json', }, timeout: 15000, }); return { content: [ { type: 'text', text: JSON.stringify({ success: true, status: response.status, data: response.data, timestamp: new Date().toISOString(), message: `Successfully created ${vulnerabilities.length} vulnerability(ies) for report ${reportId}`, }, null, 2), }, ], }; } catch (error) { if (error instanceof McpError) { throw error; } if (error.response) { return { content: [ { type: 'text', text: JSON.stringify({ success: false, status: error.response.status, error: error.response.data || error.message, timestamp: new Date().toISOString(), }, null, 2), }, ], }; } else if (error.request) { throw new McpError( ErrorCode.InternalError, `Network error: Unable to reach the API at ${VULNERABILITY_ENDPOINT}/${reportId}` ); } else { throw new McpError( ErrorCode.InternalError, `Request setup error: ${error.message}` ); } } } // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'update_report', description: 'Update a report. HTML fields (goal, scope, summary description/keyFindings, recommendations) use minimal HTML formatting: only <p> tags for paragraphs and <ul><li> for simple bullet lists. NO nesting, NO numbered lists, NO code blocks, NO headers.', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, reportId: { type: 'string', description: 'The ID of the report to update (24-character MongoDB ObjectId)', }, title: { type: 'string', description: 'Report title (optional, max 100 characters)', }, platform: { type: 'string', description: 'Platform name (optional)', }, goal: { type: 'string', description: 'HTML-formatted goal/objective using only <p> and <ul><li> tags (optional)', }, scope: { type: 'string', description: 'HTML-formatted scope using only <p> and <ul><li> tags (optional)', }, summaryDescription: { type: 'string', description: 'HTML-formatted summary description using only <p> and <ul><li> tags (optional)', }, summaryKeyFindings: { type: 'string', description: 'HTML-formatted key findings using only <p> and <ul><li> tags (optional)', }, recommendations: { type: 'string', description: 'HTML-formatted recommendations using only <p> and <ul><li> tags (optional)', }, status: { type: 'string', enum: ['Draft', 'In Progress', 'Submitted', 'Reviewed', 'Closed'], description: 'Report status (optional)', }, }, required: ['reportId'], }, }, { name: 'get_report', description: 'Retrieve a specific report by ID', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, reportId: { type: 'string', description: 'The ID of the report to retrieve (24-character MongoDB ObjectId)', }, }, required: ['reportId'], }, }, { name: 'get_all_reports', description: 'Retrieve all reports from the API', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, }, required: [], }, }, { name: 'create_report', description: 'Create a new report', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, title: { type: 'string', description: 'The title/name of the report', }, platform: { type: 'string', description: 'The platform for the report (e.g., iOS, Android, Web)', }, templateId: { type: 'string', description: 'Template ID for the report (defaults to 67b1dac12c8d23272ad47cbd if not provided)', }, testers: { type: 'array', items: { type: 'string' }, description: 'Array of tester IDs (optional, defaults to empty array)', }, }, required: ['title'], }, }, { name: 'get_vulnerability', description: 'Retrieve a specific vulnerability by ID', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, vulnerabilityId: { type: 'string', description: 'The ID of the vulnerability to retrieve (24-character MongoDB ObjectId)', }, }, required: ['vulnerabilityId'], }, }, { name: 'update_vulnerability', description: 'Update a vulnerability. Use minimal HTML formatting: only <p> tags for paragraphs and <ul><li> for simple bullet lists. NO nesting, NO numbered lists, NO code blocks, NO headers.', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, vulnerabilityId: { type: 'string', description: 'The ID of the vulnerability to update (24-character MongoDB ObjectId)', }, title: { type: 'string', description: 'The title of the vulnerability (optional)', }, description: { type: 'string', description: 'Simple HTML description using only <p> tags (optional)', }, details: { type: 'string', description: 'Simple HTML details using only <p> and <ul><li> tags (optional)', }, impact: { type: 'string', description: 'Simple HTML impact using only <p> and <ul><li> tags (optional)', }, remediation: { type: 'string', description: 'Simple HTML remediation using only <p> and <ul><li> tags (optional)', }, cvss: { type: 'string', description: 'CVSS 3.1 vector string (optional)', }, cvssScore: { type: 'number', minimum: 0, maximum: 10, description: 'CVSS 3.1 score (0.0 to 10.0, optional)', }, severity: { type: 'string', enum: ['Informational', 'Low', 'Medium', 'High', 'Critical'], description: 'Vulnerability severity level (optional)', }, taskId: { type: 'string', description: 'Task ID associated with the vulnerability (optional)', }, }, required: ['vulnerabilityId'], }, }, { name: 'delete_vulnerability', description: 'Delete a vulnerability by ID', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, vulnerabilityId: { type: 'string', description: 'The ID of the vulnerability to delete (24-character MongoDB ObjectId)', }, }, required: ['vulnerabilityId'], }, }, { name: 'get_vulnerabilities', description: 'Retrieve all vulnerabilities for a specific report', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, reportId: { type: 'string', description: 'The ID of the report to get vulnerabilities from (24-character MongoDB ObjectId)', }, }, required: ['reportId'], }, }, { name: 'create_vulnerabilities', description: 'Create one or more vulnerabilities for a specific report. Use minimal HTML formatting: only <p> tags for paragraphs and <ul><li> for simple bullet lists. NO nesting, NO numbered lists, NO code blocks, NO headers.', inputSchema: { type: 'object', properties: { bearerToken: { type: 'string', description: 'Bearer token for authentication (optional if REPORTS_JWT_TOKEN env var is set)', }, reportId: { type: 'string', description: 'The ID of the report to add vulnerabilities to (24-character MongoDB ObjectId)', }, vulnerabilities: { type: 'array', items: { type: 'object', properties: { title: { type: 'string', description: 'The title of the vulnerability', }, description: { type: 'string', description: 'Simple HTML description using only <p> tags. Keep it concise and minimal.', }, details: { type: 'string', description: 'Simple HTML details using only <p> and <ul><li> tags. No nesting or complex formatting.', }, impact: { type: 'string', description: 'Simple HTML impact using only <p> and <ul><li> tags. List impacts as simple bullet points.', }, remediation: { type: 'string', description: 'Simple HTML remediation using only <p> and <ul><li> tags. List fixes as simple bullet points.', }, cvss: { type: 'string', description: 'CVSS 3.1 vector string (e.g., "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N")', }, cvssScore: { type: 'number', minimum: 0, maximum: 10, description: 'CVSS 3.1 score (0.0 to 10.0)', }, severity: { type: 'string', enum: ['Informational', 'Low', 'Medium', 'High', 'Critical'], description: 'Vulnerability severity level based on CVSS score', }, }, required: ['title', 'description'], }, description: 'Array of vulnerability objects to create. Format content with minimal HTML: <p> for text, <ul><li> for lists only.', }, }, required: ['reportId', 'vulnerabilities'], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get_report': if (!args.reportId) { throw new McpError( ErrorCode.InvalidParams, 'Report ID is required' ); } return await getReport(args.bearerToken, args.reportId); case 'update_report': if (!args.reportId) { throw new McpError( ErrorCode.InvalidParams, 'Report ID is required' ); } // Build update data object from provided fields with auto-HTML formatting for HTML fields const reportUpdateData = {}; if (args.title !== undefined) reportUpdateData.title = args.title; if (args.platform !== undefined) reportUpdateData.platform = args.platform; if (args.goal !== undefined) reportUpdateData.goal = formatAsHTML(args.goal); if (args.scope !== undefined) reportUpdateData.scope = formatAsHTML(args.scope); if (args.recommendations !== undefined) reportUpdateData.recommendations = formatAsHTML(args.recommendations, 'list'); if (args.status !== undefined) reportUpdateData.status = args.status; // Handle summary object fields if (args.summaryDescription !== undefined || args.summaryKeyFindings !== undefined) { reportUpdateData.summary = {}; if (args.summaryDescription !== undefined) { reportUpdateData.summary.description = formatAsHTML(args.summaryDescription); } if (args.summaryKeyFindings !== undefined) { reportUpdateData.summary.keyFindings = formatAsHTML(args.summaryKeyFindings, 'list'); } } if (Object.keys(reportUpdateData).length === 0) { throw new McpError( ErrorCode.InvalidParams, 'At least one field must be provided to update' ); } return await updateReport(args.bearerToken, args.reportId, reportUpdateData); case 'get_all_reports': return await getAllReports(args.bearerToken); case 'create_report': if (!args.title) { throw new McpError( ErrorCode.InvalidParams, 'Report title is required' ); } return await createReport(args.bearerToken, { title: args.title, platform: args.platform, templateId: args.templateId, testers: args.testers, }); case 'get_vulnerabilities': if (!args.reportId) { throw new McpError( ErrorCode.InvalidParams, 'Report ID is required' ); } return await getVulnerabilities(args.bearerToken, args.reportId); case 'get_vulnerability': if (!args.vulnerabilityId) { throw new McpError( ErrorCode.InvalidParams, 'Vulnerability ID is required' ); } return await getVulnerability(args.bearerToken, args.vulnerabilityId); case 'update_vulnerability': if (!args.vulnerabilityId) { throw new McpError( ErrorCode.InvalidParams, 'Vulnerability ID is required' ); } // Build update data object from provided fields with auto-HTML formatting const updateData = {}; if (args.title !== undefined) updateData.title = args.title; if (args.description !== undefined) updateData.description = formatAsHTML(args.description); if (args.details !== undefined) updateData.details = formatAsHTML(args.details); if (args.impact !== undefined) updateData.impact = formatAsHTML(args.impact, 'list'); if (args.remediation !== undefined) updateData.remediation = formatAsHTML(args.remediation, 'list'); if (args.cvss !== undefined) updateData.cvss = args.cvss; if (args.cvssScore !== undefined) updateData.cvssScore = args.cvssScore; if (args.severity !== undefined) updateData.severity = args.severity; if (args.taskId !== undefined) updateData.taskId = args.taskId; if (Object.keys(updateData).length === 0) { throw new McpError( ErrorCode.InvalidParams, 'At least one field must be provided to update' ); } return await updateVulnerability(args.bearerToken, args.vulnerabilityId, updateData); case 'delete_vulnerability': if (!args.vulnerabilityId) { throw new McpError( ErrorCode.InvalidParams, 'Vulnerability ID is required' ); } return await deleteVulnerability(args.bearerToken, args.vulnerabilityId); case 'create_vulnerabilities': if (!args.reportId) { throw new McpError( ErrorCode.InvalidParams, 'Report ID is required' ); } if (!args.vulnerabilities || !Array.isArray(args.vulnerabilities) || args.vulnerabilities.length === 0) { throw new McpError( ErrorCode.InvalidParams, 'At least one vulnerability is required' ); } return await createVulnerabilities(args.bearerToken, args.reportId, args.vulnerabilities); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error.message}` ); } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('Reports MCP server running on stdio'); // Log JWT configuration status if (JWT_TOKEN) { console.error('JWT token configured via environment variable'); } else { console.error('No JWT token configured - bearerToken parameter required for all requests'); } } // Handle process events process.on('SIGINT', () => { console.error('Received SIGINT, shutting down gracefully...'); process.exit(0); }); process.on('SIGTERM', () => { console.error('Received SIGTERM, shutting down gracefully...'); process.exit(0); }); // Run the server main().catch((error) => { console.error('Fatal error in main():', error); process.exit(1); });

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/izzy0101010101/mcp-reports-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server