resources.ts•4.72 kB
/**
* Google Cloud Monitoring resources for MCP
*/
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { getProjectId } from '../../utils/auth.js';
import { GcpMcpError } from '../../utils/error.js';
import { formatTimeSeriesData, getMonitoringClient, TimeSeriesData } from './types.js';
/**
* Registers Google Cloud Monitoring resources with the MCP server
*
* @param server The MCP server instance
*/
export function registerMonitoringResources(server: McpServer): void {
// Register a resource for recent metrics
server.resource(
'recent-metrics',
new ResourceTemplate('gcp-monitoring://{projectId}/recent', { list: undefined }),
async (uri, { projectId }, context) => {
try {
const actualProjectId = projectId || await getProjectId();
const client = getMonitoringClient();
// Default filter from environment variable or use a common metric
const defaultFilter = process.env.MONITORING_FILTER || 'metric.type="compute.googleapis.com/instance/cpu/utilization"';
// Create time range for the last hour
const now = new Date();
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
const [timeSeries] = await client.listTimeSeries({
name: `projects/${actualProjectId}`,
filter: defaultFilter,
interval: {
startTime: {
seconds: Math.floor(oneHourAgo.getTime() / 1000),
nanos: 0
},
endTime: {
seconds: Math.floor(now.getTime() / 1000),
nanos: 0
}
}
});
if (!timeSeries || timeSeries.length === 0) {
return {
contents: [{
uri: uri.href,
text: `# Recent Metrics for Project: ${actualProjectId}\n\nNo metrics found matching filter: ${defaultFilter}`
}]
};
}
const formattedData = formatTimeSeriesData(timeSeries as unknown as TimeSeriesData[]);
return {
contents: [{
uri: uri.href,
text: `# Recent Metrics for Project: ${actualProjectId}\n\nFilter: ${defaultFilter}\nTime Range: ${oneHourAgo.toISOString()} to ${now.toISOString()}\n\n${formattedData}`
}]
};
} catch (error: any) {
throw new GcpMcpError(
`Failed to retrieve recent metrics: ${error.message}`,
error.code || 'UNKNOWN',
error.statusCode || 500
);
}
}
);
// Register a resource for metrics with a custom filter
server.resource(
'filtered-metrics',
new ResourceTemplate('gcp-monitoring://{projectId}/filter/{filter}', { list: undefined }),
async (uri, { projectId, filter }, context) => {
try {
const actualProjectId = projectId || await getProjectId();
const client = getMonitoringClient();
if (!filter) {
throw new GcpMcpError('Metric filter is required', 'INVALID_ARGUMENT', 400);
}
const decodedFilter = Array.isArray(filter) ? decodeURIComponent(filter[0]) : decodeURIComponent(filter);
// Create time range for the last hour
const now = new Date();
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
const [timeSeries] = await client.listTimeSeries({
name: `projects/${actualProjectId}`,
filter: decodedFilter,
interval: {
startTime: {
seconds: Math.floor(oneHourAgo.getTime() / 1000),
nanos: 0
},
endTime: {
seconds: Math.floor(now.getTime() / 1000),
nanos: 0
}
}
});
if (!timeSeries || timeSeries.length === 0) {
return {
contents: [{
uri: uri.href,
text: `# Filtered Metrics for Project: ${actualProjectId}\n\nNo metrics found matching filter: ${decodedFilter}`
}]
};
}
const formattedData = formatTimeSeriesData(timeSeries as unknown as TimeSeriesData[]);
return {
contents: [{
uri: uri.href,
text: `# Filtered Metrics for Project: ${actualProjectId}\n\nFilter: ${decodedFilter}\nTime Range: ${oneHourAgo.toISOString()} to ${now.toISOString()}\n\n${formattedData}`
}]
};
} catch (error: any) {
throw new GcpMcpError(
`Failed to retrieve filtered metrics: ${error.message}`,
error.code || 'UNKNOWN',
error.statusCode || 500
);
}
}
);
}