mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-01-16 23:00:51 +00:00
feat: Enhance MCP tool generation with public status page tools
- Refactored ToolGenerator.ts to include generation of public status page tools. - Added PublicStatusPageTools.ts with functions to create tools for querying public status pages. - Implemented tools for getting overview, incidents, scheduled maintenance, announcements, and resolving status page domains. - Updated logging and error handling for public status page tool execution.
This commit is contained in:
parent
2cf23c203e
commit
eecf87bd0f
4 changed files with 1510 additions and 849 deletions
|
|
@ -4,317 +4,373 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
CallToolRequest,
|
||||
ErrorCode,
|
||||
ListToolsRequestSchema,
|
||||
McpError,
|
||||
CallToolRequestSchema,
|
||||
CallToolRequest,
|
||||
ErrorCode,
|
||||
ListToolsRequestSchema,
|
||||
McpError,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { McpToolInfo, OneUptimeToolCallArgs, JSONSchema } from "../Types/McpTypes";
|
||||
import {
|
||||
McpToolInfo,
|
||||
OneUptimeToolCallArgs,
|
||||
JSONSchema,
|
||||
} from "../Types/McpTypes";
|
||||
import OneUptimeOperation from "../Types/OneUptimeOperation";
|
||||
import OneUptimeApiService from "../Services/OneUptimeApiService";
|
||||
import SessionManager from "../Server/SessionManager";
|
||||
import { LIST_PREVIEW_LIMIT } from "../Config/ServerConfig";
|
||||
import { isHelperTool, handleHelperTool } from "../Tools/HelperTools";
|
||||
import {
|
||||
isPublicStatusPageTool,
|
||||
handlePublicStatusPageTool,
|
||||
} from "../Tools/PublicStatusPageTools";
|
||||
import logger from "Common/Server/Utils/Logger";
|
||||
|
||||
/**
|
||||
* Register tool handlers on the MCP server
|
||||
*/
|
||||
export function registerToolHandlers(mcpServer: McpServer, tools: McpToolInfo[]): void {
|
||||
// Register list tools handler
|
||||
mcpServer.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return handleListTools(tools);
|
||||
});
|
||||
export function registerToolHandlers(
|
||||
mcpServer: McpServer,
|
||||
tools: McpToolInfo[],
|
||||
): void {
|
||||
// Register list tools handler
|
||||
mcpServer.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return handleListTools(tools);
|
||||
});
|
||||
|
||||
// Register call tool handler
|
||||
mcpServer.server.setRequestHandler(
|
||||
CallToolRequestSchema,
|
||||
async (request: CallToolRequest) => {
|
||||
return handleCallTool(request, tools);
|
||||
}
|
||||
);
|
||||
// Register call tool handler
|
||||
mcpServer.server.setRequestHandler(
|
||||
CallToolRequestSchema,
|
||||
async (request: CallToolRequest) => {
|
||||
return handleCallTool(request, tools);
|
||||
},
|
||||
);
|
||||
|
||||
logger.info(`Registered handlers for ${tools.length} tools`);
|
||||
logger.info(`Registered handlers for ${tools.length} tools`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle list tools request
|
||||
*/
|
||||
function handleListTools(tools: McpToolInfo[]): {
|
||||
tools: Array<{ name: string; description: string; inputSchema: JSONSchema }>;
|
||||
tools: Array<{ name: string; description: string; inputSchema: JSONSchema }>;
|
||||
} {
|
||||
const mcpTools: Array<{
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: JSONSchema;
|
||||
}> = tools.map((tool: McpToolInfo) => ({
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
}));
|
||||
const mcpTools: Array<{
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: JSONSchema;
|
||||
}> = tools.map((tool: McpToolInfo) => {
|
||||
return {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema,
|
||||
};
|
||||
});
|
||||
|
||||
logger.info(`Listing ${mcpTools.length} available tools`);
|
||||
return { tools: mcpTools };
|
||||
logger.info(`Listing ${mcpTools.length} available tools`);
|
||||
return { tools: mcpTools };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle tool call request
|
||||
*/
|
||||
async function handleCallTool(
|
||||
request: CallToolRequest,
|
||||
tools: McpToolInfo[]
|
||||
request: CallToolRequest,
|
||||
tools: McpToolInfo[],
|
||||
): Promise<{ content: Array<{ type: string; text: string }> }> {
|
||||
const { name, arguments: args } = request.params;
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
try {
|
||||
// Check if this is a helper tool (doesn't require API key)
|
||||
if (isHelperTool(name)) {
|
||||
logger.info(`Executing helper tool: ${name}`);
|
||||
const responseText: string = handleHelperTool(
|
||||
name,
|
||||
(args || {}) as Record<string, unknown>,
|
||||
tools.filter((t: McpToolInfo) => !isHelperTool(t.name))
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: responseText,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Find the tool by name
|
||||
const tool: McpToolInfo | undefined = tools.find(
|
||||
(t: McpToolInfo) => t.name === name
|
||||
);
|
||||
|
||||
if (!tool) {
|
||||
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}. Use 'oneuptime_help' to see available tools.`);
|
||||
}
|
||||
|
||||
logger.info(`Executing tool: ${name} for model: ${tool.modelName}`);
|
||||
|
||||
// Validate API key is available for this session
|
||||
const apiKey: string = SessionManager.getCurrentApiKey();
|
||||
if (!apiKey) {
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidRequest,
|
||||
"API key is required. Please provide x-api-key header in your request. Use 'oneuptime_help' to learn more."
|
||||
);
|
||||
}
|
||||
|
||||
// Execute the OneUptime operation with the session's API key
|
||||
const result: unknown = await OneUptimeApiService.executeOperation(
|
||||
tool.tableName,
|
||||
tool.operation,
|
||||
tool.modelType,
|
||||
tool.apiPath || "",
|
||||
args as OneUptimeToolCallArgs,
|
||||
apiKey
|
||||
);
|
||||
|
||||
// Format the response
|
||||
const responseText: string = formatToolResponse(
|
||||
tool,
|
||||
result,
|
||||
args as OneUptimeToolCallArgs
|
||||
);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: responseText,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`Error executing tool ${name}: ${error}`);
|
||||
|
||||
if (error instanceof McpError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Failed to execute ${name}: ${error}. Use 'oneuptime_help' for guidance.`
|
||||
);
|
||||
try {
|
||||
// Check if this is a helper tool (doesn't require API key)
|
||||
if (isHelperTool(name)) {
|
||||
logger.info(`Executing helper tool: ${name}`);
|
||||
const responseText: string = handleHelperTool(
|
||||
name,
|
||||
(args || {}) as Record<string, unknown>,
|
||||
tools.filter((t: McpToolInfo) => {
|
||||
return !isHelperTool(t.name) && !isPublicStatusPageTool(t.name);
|
||||
}),
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: responseText,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Check if this is a public status page tool (doesn't require API key)
|
||||
if (isPublicStatusPageTool(name)) {
|
||||
logger.info(`Executing public status page tool: ${name}`);
|
||||
const responseText: string = await handlePublicStatusPageTool(
|
||||
name,
|
||||
(args || {}) as Record<string, unknown>,
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: responseText,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Find the tool by name
|
||||
const tool: McpToolInfo | undefined = tools.find((t: McpToolInfo) => {
|
||||
return t.name === name;
|
||||
});
|
||||
|
||||
if (!tool) {
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
`Unknown tool: ${name}. Use 'oneuptime_help' to see available tools.`,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`Executing tool: ${name} for model: ${tool.modelName}`);
|
||||
|
||||
// Validate API key is available for this session
|
||||
const apiKey: string = SessionManager.getCurrentApiKey();
|
||||
if (!apiKey) {
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidRequest,
|
||||
"API key is required. Please provide x-api-key header in your request. Use 'oneuptime_help' to learn more.",
|
||||
);
|
||||
}
|
||||
|
||||
// Execute the OneUptime operation with the session's API key
|
||||
const result: unknown = await OneUptimeApiService.executeOperation(
|
||||
tool.tableName,
|
||||
tool.operation,
|
||||
tool.modelType,
|
||||
tool.apiPath || "",
|
||||
args as OneUptimeToolCallArgs,
|
||||
apiKey,
|
||||
);
|
||||
|
||||
// Format the response
|
||||
const responseText: string = formatToolResponse(
|
||||
tool,
|
||||
result,
|
||||
args as OneUptimeToolCallArgs,
|
||||
);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: responseText,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`Error executing tool ${name}: ${error}`);
|
||||
|
||||
if (error instanceof McpError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Failed to execute ${name}: ${error}. Use 'oneuptime_help' for guidance.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format tool response based on operation type
|
||||
*/
|
||||
export function formatToolResponse(
|
||||
tool: McpToolInfo,
|
||||
result: unknown,
|
||||
args: OneUptimeToolCallArgs
|
||||
tool: McpToolInfo,
|
||||
result: unknown,
|
||||
args: OneUptimeToolCallArgs,
|
||||
): string {
|
||||
const operation: OneUptimeOperation = tool.operation;
|
||||
const modelName: string = tool.singularName;
|
||||
const pluralName: string = tool.pluralName;
|
||||
const operation: OneUptimeOperation = tool.operation;
|
||||
const modelName: string = tool.singularName;
|
||||
const pluralName: string = tool.pluralName;
|
||||
|
||||
switch (operation) {
|
||||
case OneUptimeOperation.Create:
|
||||
return formatCreateResponse(modelName, result);
|
||||
switch (operation) {
|
||||
case OneUptimeOperation.Create:
|
||||
return formatCreateResponse(modelName, result);
|
||||
|
||||
case OneUptimeOperation.Read:
|
||||
return formatReadResponse(modelName, result, args.id);
|
||||
case OneUptimeOperation.Read:
|
||||
return formatReadResponse(modelName, result, args.id);
|
||||
|
||||
case OneUptimeOperation.List:
|
||||
return formatListResponse(modelName, pluralName, result);
|
||||
case OneUptimeOperation.List:
|
||||
return formatListResponse(modelName, pluralName, result);
|
||||
|
||||
case OneUptimeOperation.Update:
|
||||
return formatUpdateResponse(modelName, result, args.id);
|
||||
case OneUptimeOperation.Update:
|
||||
return formatUpdateResponse(modelName, result, args.id);
|
||||
|
||||
case OneUptimeOperation.Delete:
|
||||
return formatDeleteResponse(modelName, args.id);
|
||||
case OneUptimeOperation.Delete:
|
||||
return formatDeleteResponse(modelName, args.id);
|
||||
|
||||
case OneUptimeOperation.Count:
|
||||
return formatCountResponse(pluralName, result);
|
||||
case OneUptimeOperation.Count:
|
||||
return formatCountResponse(pluralName, result);
|
||||
|
||||
default:
|
||||
return `Operation ${operation} completed successfully: ${JSON.stringify(result, null, 2)}`;
|
||||
}
|
||||
default:
|
||||
return `Operation ${operation} completed successfully: ${JSON.stringify(result, null, 2)}`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatCreateResponse(modelName: string, result: unknown): string {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "create",
|
||||
resourceType: modelName,
|
||||
message: `Successfully created ${modelName}`,
|
||||
data: result,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "create",
|
||||
resourceType: modelName,
|
||||
message: `Successfully created ${modelName}`,
|
||||
data: result,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function formatReadResponse(
|
||||
modelName: string,
|
||||
result: unknown,
|
||||
id: string | undefined
|
||||
modelName: string,
|
||||
result: unknown,
|
||||
id: string | undefined,
|
||||
): string {
|
||||
if (result) {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "read",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
data: result,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
if (result) {
|
||||
const response: Record<string, unknown> = {
|
||||
success: false,
|
||||
operation: "read",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
error: `${modelName} not found with ID: ${id}`,
|
||||
suggestion: `Use list_${modelName.toLowerCase().replace(/\s+/g, "_")}s to find valid IDs`,
|
||||
success: true,
|
||||
operation: "read",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
data: result,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
const response: Record<string, unknown> = {
|
||||
success: false,
|
||||
operation: "read",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
error: `${modelName} not found with ID: ${id}`,
|
||||
suggestion: `Use list_${modelName.toLowerCase().replace(/\s+/g, "_")}s to find valid IDs`,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function formatListResponse(
|
||||
modelName: string,
|
||||
pluralName: string,
|
||||
result: unknown
|
||||
modelName: string,
|
||||
pluralName: string,
|
||||
result: unknown,
|
||||
): string {
|
||||
const items: Array<unknown> = Array.isArray(result)
|
||||
? result
|
||||
: (result as { data?: Array<unknown> })?.data || [];
|
||||
const count: number = items.length;
|
||||
const items: Array<unknown> = Array.isArray(result)
|
||||
? result
|
||||
: (result as { data?: Array<unknown> })?.data || [];
|
||||
const count: number = items.length;
|
||||
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "list",
|
||||
resourceType: pluralName,
|
||||
totalReturned: count,
|
||||
hasMore: count >= LIST_PREVIEW_LIMIT,
|
||||
message: count === 0
|
||||
? `No ${pluralName} found matching the criteria`
|
||||
: `Found ${count} ${count === 1 ? modelName : pluralName}`,
|
||||
data: items.slice(0, LIST_PREVIEW_LIMIT),
|
||||
};
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "list",
|
||||
resourceType: pluralName,
|
||||
totalReturned: count,
|
||||
hasMore: count >= LIST_PREVIEW_LIMIT,
|
||||
message:
|
||||
count === 0
|
||||
? `No ${pluralName} found matching the criteria`
|
||||
: `Found ${count} ${count === 1 ? modelName : pluralName}`,
|
||||
data: items.slice(0, LIST_PREVIEW_LIMIT),
|
||||
};
|
||||
|
||||
if (count > LIST_PREVIEW_LIMIT) {
|
||||
response["note"] = `Showing first ${LIST_PREVIEW_LIMIT} results. Use 'skip' parameter to paginate.`;
|
||||
}
|
||||
if (count > LIST_PREVIEW_LIMIT) {
|
||||
response["note"] =
|
||||
`Showing first ${LIST_PREVIEW_LIMIT} results. Use 'skip' parameter to paginate.`;
|
||||
}
|
||||
|
||||
return JSON.stringify(response, null, 2);
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function formatUpdateResponse(
|
||||
modelName: string,
|
||||
result: unknown,
|
||||
id: string | undefined
|
||||
modelName: string,
|
||||
result: unknown,
|
||||
id: string | undefined,
|
||||
): string {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "update",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
message: `Successfully updated ${modelName}`,
|
||||
data: result,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "update",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
message: `Successfully updated ${modelName}`,
|
||||
data: result,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function formatDeleteResponse(modelName: string, id: string | undefined): string {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "delete",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
message: `Successfully deleted ${modelName} (ID: ${id})`,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
function formatDeleteResponse(
|
||||
modelName: string,
|
||||
id: string | undefined,
|
||||
): string {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "delete",
|
||||
resourceType: modelName,
|
||||
resourceId: id,
|
||||
message: `Successfully deleted ${modelName} (ID: ${id})`,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function formatCountResponse(pluralName: string, result: unknown): string {
|
||||
let totalCount: number = 0;
|
||||
let totalCount: number = 0;
|
||||
|
||||
if (result !== null && result !== undefined) {
|
||||
if (typeof result === "number") {
|
||||
totalCount = result;
|
||||
} else if (typeof result === "object") {
|
||||
const resultObj: Record<string, unknown> = result as Record<string, unknown>;
|
||||
if (result !== null && result !== undefined) {
|
||||
if (typeof result === "number") {
|
||||
totalCount = result;
|
||||
} else if (typeof result === "object") {
|
||||
const resultObj: Record<string, unknown> = result as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
// Handle { count: number } format
|
||||
if ("count" in resultObj) {
|
||||
const countValue: unknown = resultObj["count"];
|
||||
if (typeof countValue === "number") {
|
||||
totalCount = countValue;
|
||||
} else if (typeof countValue === "object" && countValue !== null) {
|
||||
// Handle PositiveNumber or other objects with value/toNumber
|
||||
const countObj: Record<string, unknown> = countValue as Record<string, unknown>;
|
||||
if (typeof countObj["value"] === "number") {
|
||||
totalCount = countObj["value"];
|
||||
} else if (typeof (countObj as { toNumber?: () => number }).toNumber === "function") {
|
||||
totalCount = (countObj as { toNumber: () => number }).toNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle { data: { count: number } } format
|
||||
else if ("data" in resultObj && typeof resultObj["data"] === "object" && resultObj["data"] !== null) {
|
||||
const dataObj: Record<string, unknown> = resultObj["data"] as Record<string, unknown>;
|
||||
if ("count" in dataObj && typeof dataObj["count"] === "number") {
|
||||
totalCount = dataObj["count"];
|
||||
}
|
||||
}
|
||||
// Handle { count: number } format
|
||||
if ("count" in resultObj) {
|
||||
const countValue: unknown = resultObj["count"];
|
||||
if (typeof countValue === "number") {
|
||||
totalCount = countValue;
|
||||
} else if (typeof countValue === "object" && countValue !== null) {
|
||||
// Handle PositiveNumber or other objects with value/toNumber
|
||||
const countObj: Record<string, unknown> = countValue as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
if (typeof countObj["value"] === "number") {
|
||||
totalCount = countObj["value"];
|
||||
} else if (
|
||||
typeof (countObj as { toNumber?: () => number }).toNumber ===
|
||||
"function"
|
||||
) {
|
||||
totalCount = (countObj as { toNumber: () => number }).toNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle { data: { count: number } } format
|
||||
else if (
|
||||
"data" in resultObj &&
|
||||
typeof resultObj["data"] === "object" &&
|
||||
resultObj["data"] !== null
|
||||
) {
|
||||
const dataObj: Record<string, unknown> = resultObj["data"] as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
if ("count" in dataObj && typeof dataObj["count"] === "number") {
|
||||
totalCount = dataObj["count"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "count",
|
||||
resourceType: pluralName,
|
||||
count: totalCount,
|
||||
message: `Total count of ${pluralName}: ${totalCount}`,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
operation: "count",
|
||||
resourceType: pluralName,
|
||||
count: totalCount,
|
||||
message: `Total count of ${pluralName}: ${totalCount}`,
|
||||
};
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,69 +8,82 @@ import OneUptimeOperation from "../Types/OneUptimeOperation";
|
|||
import ModelType from "../Types/ModelType";
|
||||
|
||||
export interface ResourceInfo {
|
||||
name: string;
|
||||
singularName: string;
|
||||
pluralName: string;
|
||||
description: string;
|
||||
operations: string[];
|
||||
name: string;
|
||||
singularName: string;
|
||||
pluralName: string;
|
||||
description: string;
|
||||
operations: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate helper tools for MCP
|
||||
*/
|
||||
export function generateHelperTools(resourceTools: McpToolInfo[]): McpToolInfo[] {
|
||||
// Extract unique resources from tools
|
||||
const resources: Map<string, ResourceInfo> = new Map();
|
||||
export function generateHelperTools(
|
||||
resourceTools: McpToolInfo[],
|
||||
): McpToolInfo[] {
|
||||
// Extract unique resources from tools
|
||||
const resources: Map<string, ResourceInfo> = new Map();
|
||||
|
||||
for (const tool of resourceTools) {
|
||||
if (!resources.has(tool.tableName)) {
|
||||
resources.set(tool.tableName, {
|
||||
name: tool.tableName,
|
||||
singularName: tool.singularName,
|
||||
pluralName: tool.pluralName,
|
||||
description: getResourceDescription(tool.singularName),
|
||||
operations: [],
|
||||
});
|
||||
}
|
||||
const resource: ResourceInfo | undefined = resources.get(tool.tableName);
|
||||
if (resource) {
|
||||
resource.operations.push(tool.operation);
|
||||
}
|
||||
for (const tool of resourceTools) {
|
||||
if (!resources.has(tool.tableName)) {
|
||||
resources.set(tool.tableName, {
|
||||
name: tool.tableName,
|
||||
singularName: tool.singularName,
|
||||
pluralName: tool.pluralName,
|
||||
description: getResourceDescription(tool.singularName),
|
||||
operations: [],
|
||||
});
|
||||
}
|
||||
const resource: ResourceInfo | undefined = resources.get(tool.tableName);
|
||||
if (resource) {
|
||||
resource.operations.push(tool.operation);
|
||||
}
|
||||
}
|
||||
|
||||
const resourceList: ResourceInfo[] = Array.from(resources.values());
|
||||
const resourceList: ResourceInfo[] = Array.from(resources.values());
|
||||
|
||||
return [
|
||||
createHelpTool(resourceList),
|
||||
createResourceInfoTool(resourceList),
|
||||
];
|
||||
return [createHelpTool(resourceList), createResourceInfoTool(resourceList)];
|
||||
}
|
||||
|
||||
function getResourceDescription(singularName: string): string {
|
||||
const descriptions: Record<string, string> = {
|
||||
"Incident": "Represents service disruptions or issues affecting your systems. Track incident lifecycle from creation to resolution.",
|
||||
"Monitor": "Defines what to monitor (websites, APIs, servers) and how to check their health and availability.",
|
||||
"Alert": "Notifications triggered when monitors detect issues. Configures who gets notified and how.",
|
||||
"Project": "Top-level container for all your monitoring resources. Organizes monitors, incidents, and team members.",
|
||||
"Status Page": "Public-facing page showing the status of your services to your customers.",
|
||||
"Scheduled Maintenance": "Planned downtime events that inform users about expected service interruptions.",
|
||||
"Team": "Groups of users with shared access to project resources.",
|
||||
"On-Call Duty Policy": "Defines escalation rules and schedules for incident response.",
|
||||
"Incident State": "Represents the lifecycle states of incidents (e.g., Created, Acknowledged, Resolved).",
|
||||
"Monitor Status": "Represents the health states of monitors (e.g., Operational, Degraded, Offline).",
|
||||
};
|
||||
const descriptions: Record<string, string> = {
|
||||
Incident:
|
||||
"Represents service disruptions or issues affecting your systems. Track incident lifecycle from creation to resolution.",
|
||||
Monitor:
|
||||
"Defines what to monitor (websites, APIs, servers) and how to check their health and availability.",
|
||||
Alert:
|
||||
"Notifications triggered when monitors detect issues. Configures who gets notified and how.",
|
||||
Project:
|
||||
"Top-level container for all your monitoring resources. Organizes monitors, incidents, and team members.",
|
||||
"Status Page":
|
||||
"Public-facing page showing the status of your services to your customers.",
|
||||
"Scheduled Maintenance":
|
||||
"Planned downtime events that inform users about expected service interruptions.",
|
||||
Team: "Groups of users with shared access to project resources.",
|
||||
"On-Call Duty Policy":
|
||||
"Defines escalation rules and schedules for incident response.",
|
||||
"Incident State":
|
||||
"Represents the lifecycle states of incidents (e.g., Created, Acknowledged, Resolved).",
|
||||
"Monitor Status":
|
||||
"Represents the health states of monitors (e.g., Operational, Degraded, Offline).",
|
||||
};
|
||||
|
||||
return descriptions[singularName] || `Manages ${singularName} resources in OneUptime.`;
|
||||
return (
|
||||
descriptions[singularName] ||
|
||||
`Manages ${singularName} resources in OneUptime.`
|
||||
);
|
||||
}
|
||||
|
||||
function createHelpTool(resources: ResourceInfo[]): McpToolInfo {
|
||||
const resourceSummary: string = resources
|
||||
.map((r: ResourceInfo) => `- ${r.pluralName}: ${r.description}`)
|
||||
.join("\n");
|
||||
const resourceSummary: string = resources
|
||||
.map((r: ResourceInfo) => {
|
||||
return `- ${r.pluralName}: ${r.description}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return {
|
||||
name: "oneuptime_help",
|
||||
description: `Get help and guidance for using the OneUptime MCP server. Returns information about available resources and common operations. Use this tool first to understand what you can do with OneUptime.
|
||||
return {
|
||||
name: "oneuptime_help",
|
||||
description: `Get help and guidance for using the OneUptime MCP server. Returns information about available resources and common operations. Use this tool first to understand what you can do with OneUptime.
|
||||
|
||||
AVAILABLE RESOURCES:
|
||||
${resourceSummary}
|
||||
|
|
@ -80,234 +93,300 @@ COMMON WORKFLOWS:
|
|||
2. Create incident: Use create_incident with title and severity
|
||||
3. Check monitor status: Use list_monitors to see all monitors and their status
|
||||
4. Count resources: Use count_* tools to get totals without fetching all data`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
topic: {
|
||||
type: "string",
|
||||
description: "Optional topic to get help on: 'resources', 'incidents', 'monitors', 'alerts', 'workflows', or 'examples'",
|
||||
enum: ["resources", "incidents", "monitors", "alerts", "workflows", "examples"],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
topic: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional topic to get help on: 'resources', 'incidents', 'monitors', 'alerts', 'workflows', or 'examples'",
|
||||
enum: [
|
||||
"resources",
|
||||
"incidents",
|
||||
"monitors",
|
||||
"alerts",
|
||||
"workflows",
|
||||
"examples",
|
||||
],
|
||||
},
|
||||
modelName: "Help",
|
||||
operation: OneUptimeOperation.Read,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Help",
|
||||
pluralName: "Help",
|
||||
tableName: "Help",
|
||||
apiPath: "",
|
||||
};
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName: "Help",
|
||||
operation: OneUptimeOperation.Read,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Help",
|
||||
pluralName: "Help",
|
||||
tableName: "Help",
|
||||
apiPath: "",
|
||||
};
|
||||
}
|
||||
|
||||
function createResourceInfoTool(_resources: ResourceInfo[]): McpToolInfo {
|
||||
return {
|
||||
name: "oneuptime_list_resources",
|
||||
description: "List all available OneUptime resources and their supported operations. Use this to discover what resources you can manage through the MCP server.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName: "ResourceInfo",
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Resource",
|
||||
pluralName: "Resources",
|
||||
tableName: "ResourceInfo",
|
||||
apiPath: "",
|
||||
};
|
||||
return {
|
||||
name: "oneuptime_list_resources",
|
||||
description:
|
||||
"List all available OneUptime resources and their supported operations. Use this to discover what resources you can manage through the MCP server.",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName: "ResourceInfo",
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Resource",
|
||||
pluralName: "Resources",
|
||||
tableName: "ResourceInfo",
|
||||
apiPath: "",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle helper tool execution
|
||||
*/
|
||||
export function handleHelperTool(
|
||||
toolName: string,
|
||||
args: Record<string, unknown>,
|
||||
resourceTools: McpToolInfo[]
|
||||
toolName: string,
|
||||
args: Record<string, unknown>,
|
||||
resourceTools: McpToolInfo[],
|
||||
): string {
|
||||
// Extract unique resources from tools
|
||||
const resources: Map<string, ResourceInfo> = new Map();
|
||||
// Extract unique resources from tools
|
||||
const resources: Map<string, ResourceInfo> = new Map();
|
||||
|
||||
for (const tool of resourceTools) {
|
||||
if (!resources.has(tool.tableName)) {
|
||||
resources.set(tool.tableName, {
|
||||
name: tool.tableName,
|
||||
singularName: tool.singularName,
|
||||
pluralName: tool.pluralName,
|
||||
description: getResourceDescription(tool.singularName),
|
||||
operations: [],
|
||||
});
|
||||
}
|
||||
const resource: ResourceInfo | undefined = resources.get(tool.tableName);
|
||||
if (resource) {
|
||||
resource.operations.push(tool.operation);
|
||||
}
|
||||
for (const tool of resourceTools) {
|
||||
if (!resources.has(tool.tableName)) {
|
||||
resources.set(tool.tableName, {
|
||||
name: tool.tableName,
|
||||
singularName: tool.singularName,
|
||||
pluralName: tool.pluralName,
|
||||
description: getResourceDescription(tool.singularName),
|
||||
operations: [],
|
||||
});
|
||||
}
|
||||
|
||||
const resourceList: ResourceInfo[] = Array.from(resources.values());
|
||||
|
||||
if (toolName === "oneuptime_help") {
|
||||
return handleHelpTool(args, resourceList);
|
||||
} else if (toolName === "oneuptime_list_resources") {
|
||||
return handleListResourcesTool(resourceList);
|
||||
const resource: ResourceInfo | undefined = resources.get(tool.tableName);
|
||||
if (resource) {
|
||||
resource.operations.push(tool.operation);
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify({ error: "Unknown helper tool" });
|
||||
const resourceList: ResourceInfo[] = Array.from(resources.values());
|
||||
|
||||
if (toolName === "oneuptime_help") {
|
||||
return handleHelpTool(args, resourceList);
|
||||
} else if (toolName === "oneuptime_list_resources") {
|
||||
return handleListResourcesTool(resourceList);
|
||||
}
|
||||
|
||||
return JSON.stringify({ error: "Unknown helper tool" });
|
||||
}
|
||||
|
||||
function handleHelpTool(args: Record<string, unknown>, resourceList: ResourceInfo[]): string {
|
||||
const topic: string = (args["topic"] as string) || "general";
|
||||
function handleHelpTool(
|
||||
args: Record<string, unknown>,
|
||||
resourceList: ResourceInfo[],
|
||||
): string {
|
||||
const topic: string = (args["topic"] as string) || "general";
|
||||
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
topic,
|
||||
data: {} as Record<string, unknown>,
|
||||
};
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
topic,
|
||||
data: {} as Record<string, unknown>,
|
||||
};
|
||||
|
||||
switch (topic) {
|
||||
case "resources":
|
||||
(response["data"] as Record<string, unknown>)["resources"] = resourceList.map((r: ResourceInfo) => ({
|
||||
name: r.name,
|
||||
singularName: r.singularName,
|
||||
pluralName: r.pluralName,
|
||||
description: r.description,
|
||||
availableOperations: r.operations,
|
||||
}));
|
||||
(response["data"] as Record<string, unknown>)["hint"] = "Use the specific tool for each operation. For example: list_incidents, create_incident, get_incident, update_incident, delete_incident, count_incidents";
|
||||
break;
|
||||
|
||||
case "incidents":
|
||||
(response["data"] as Record<string, unknown>)["description"] = "Incidents represent service disruptions or issues. They have states (Created, Acknowledged, Resolved) and severities.";
|
||||
(response["data"] as Record<string, unknown>)["commonOperations"] = [
|
||||
{ tool: "list_incidents", description: "List all incidents, optionally filtered by state or severity" },
|
||||
{ tool: "create_incident", description: "Create a new incident when an issue is detected" },
|
||||
{ tool: "update_incident", description: "Update incident state, severity, or add notes" },
|
||||
{ tool: "count_incidents", description: "Get count of incidents by state" },
|
||||
];
|
||||
(response["data"] as Record<string, unknown>)["example"] = {
|
||||
createIncident: {
|
||||
title: "Database connection failure",
|
||||
description: "Production database is not responding to queries",
|
||||
incidentSeverityId: "<severity-uuid>",
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
case "monitors":
|
||||
(response["data"] as Record<string, unknown>)["description"] = "Monitors check the health and availability of your services (websites, APIs, servers).";
|
||||
(response["data"] as Record<string, unknown>)["commonOperations"] = [
|
||||
{ tool: "list_monitors", description: "List all monitors and their current status" },
|
||||
{ tool: "create_monitor", description: "Create a new monitor to watch a service" },
|
||||
{ tool: "update_monitor", description: "Update monitor configuration or enable/disable" },
|
||||
{ tool: "count_monitors", description: "Get total number of monitors" },
|
||||
];
|
||||
break;
|
||||
|
||||
case "alerts":
|
||||
(response["data"] as Record<string, unknown>)["description"] = "Alerts are notifications sent when monitors detect issues.";
|
||||
(response["data"] as Record<string, unknown>)["commonOperations"] = [
|
||||
{ tool: "list_alerts", description: "List all alerts and their status" },
|
||||
{ tool: "count_alerts", description: "Get count of alerts" },
|
||||
];
|
||||
break;
|
||||
|
||||
case "workflows":
|
||||
(response["data"] as Record<string, unknown>)["workflows"] = [
|
||||
{
|
||||
name: "Check system status",
|
||||
steps: [
|
||||
"1. Use count_incidents to see if there are active incidents",
|
||||
"2. Use list_monitors with query to find any monitors with issues",
|
||||
"3. Use list_incidents to get details of any active incidents",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Create and manage incident",
|
||||
steps: [
|
||||
"1. Use list_incident_states to get available states",
|
||||
"2. Use create_incident with title, description, and severity",
|
||||
"3. Use update_incident to change state as incident progresses",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Incident summary report",
|
||||
steps: [
|
||||
"1. Use count_incidents to get total count",
|
||||
"2. Use list_incidents with sort by createdAt descending",
|
||||
"3. Group and summarize the results",
|
||||
],
|
||||
},
|
||||
];
|
||||
break;
|
||||
|
||||
case "examples":
|
||||
(response["data"] as Record<string, unknown>)["examples"] = {
|
||||
listRecentIncidents: {
|
||||
tool: "list_incidents",
|
||||
args: { limit: 10, sort: { createdAt: -1 } },
|
||||
},
|
||||
countActiveIncidents: {
|
||||
tool: "count_incidents",
|
||||
args: { query: {} },
|
||||
},
|
||||
getSpecificIncident: {
|
||||
tool: "get_incident",
|
||||
args: { id: "<incident-uuid>" },
|
||||
},
|
||||
updateIncidentTitle: {
|
||||
tool: "update_incident",
|
||||
args: { id: "<incident-uuid>", title: "Updated title" },
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
(response["data"] as Record<string, unknown>)["welcome"] = "Welcome to OneUptime MCP Server!";
|
||||
(response["data"] as Record<string, unknown>)["description"] = "OneUptime is an open-source monitoring platform. This MCP server lets you manage incidents, monitors, alerts, and more.";
|
||||
(response["data"] as Record<string, unknown>)["availableTopics"] = ["resources", "incidents", "monitors", "alerts", "workflows", "examples"];
|
||||
(response["data"] as Record<string, unknown>)["quickStart"] = [
|
||||
"1. Use 'oneuptime_list_resources' to see all available resources",
|
||||
"2. Use 'list_*' tools to browse existing data",
|
||||
"3. Use 'count_*' tools to get quick summaries",
|
||||
"4. Use 'create_*' tools to add new items",
|
||||
];
|
||||
(response["data"] as Record<string, unknown>)["resourceCount"] = resourceList.length;
|
||||
break;
|
||||
}
|
||||
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function handleListResourcesTool(resources: ResourceInfo[]): string {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
totalResources: resources.length,
|
||||
resources: resources.map((r: ResourceInfo) => ({
|
||||
switch (topic) {
|
||||
case "resources":
|
||||
(response["data"] as Record<string, unknown>)["resources"] =
|
||||
resourceList.map((r: ResourceInfo) => {
|
||||
return {
|
||||
name: r.name,
|
||||
singularName: r.singularName,
|
||||
pluralName: r.pluralName,
|
||||
description: r.description,
|
||||
operations: r.operations,
|
||||
tools: {
|
||||
create: `create_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
get: `get_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
list: `list_${r.pluralName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
update: `update_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
delete: `delete_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
count: `count_${r.pluralName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
},
|
||||
})),
|
||||
};
|
||||
availableOperations: r.operations,
|
||||
};
|
||||
});
|
||||
(response["data"] as Record<string, unknown>)["hint"] =
|
||||
"Use the specific tool for each operation. For example: list_incidents, create_incident, get_incident, update_incident, delete_incident, count_incidents";
|
||||
break;
|
||||
|
||||
return JSON.stringify(response, null, 2);
|
||||
case "incidents":
|
||||
(response["data"] as Record<string, unknown>)["description"] =
|
||||
"Incidents represent service disruptions or issues. They have states (Created, Acknowledged, Resolved) and severities.";
|
||||
(response["data"] as Record<string, unknown>)["commonOperations"] = [
|
||||
{
|
||||
tool: "list_incidents",
|
||||
description:
|
||||
"List all incidents, optionally filtered by state or severity",
|
||||
},
|
||||
{
|
||||
tool: "create_incident",
|
||||
description: "Create a new incident when an issue is detected",
|
||||
},
|
||||
{
|
||||
tool: "update_incident",
|
||||
description: "Update incident state, severity, or add notes",
|
||||
},
|
||||
{
|
||||
tool: "count_incidents",
|
||||
description: "Get count of incidents by state",
|
||||
},
|
||||
];
|
||||
(response["data"] as Record<string, unknown>)["example"] = {
|
||||
createIncident: {
|
||||
title: "Database connection failure",
|
||||
description: "Production database is not responding to queries",
|
||||
incidentSeverityId: "<severity-uuid>",
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
case "monitors":
|
||||
(response["data"] as Record<string, unknown>)["description"] =
|
||||
"Monitors check the health and availability of your services (websites, APIs, servers).";
|
||||
(response["data"] as Record<string, unknown>)["commonOperations"] = [
|
||||
{
|
||||
tool: "list_monitors",
|
||||
description: "List all monitors and their current status",
|
||||
},
|
||||
{
|
||||
tool: "create_monitor",
|
||||
description: "Create a new monitor to watch a service",
|
||||
},
|
||||
{
|
||||
tool: "update_monitor",
|
||||
description: "Update monitor configuration or enable/disable",
|
||||
},
|
||||
{ tool: "count_monitors", description: "Get total number of monitors" },
|
||||
];
|
||||
break;
|
||||
|
||||
case "alerts":
|
||||
(response["data"] as Record<string, unknown>)["description"] =
|
||||
"Alerts are notifications sent when monitors detect issues.";
|
||||
(response["data"] as Record<string, unknown>)["commonOperations"] = [
|
||||
{
|
||||
tool: "list_alerts",
|
||||
description: "List all alerts and their status",
|
||||
},
|
||||
{ tool: "count_alerts", description: "Get count of alerts" },
|
||||
];
|
||||
break;
|
||||
|
||||
case "workflows":
|
||||
(response["data"] as Record<string, unknown>)["workflows"] = [
|
||||
{
|
||||
name: "Check system status",
|
||||
steps: [
|
||||
"1. Use count_incidents to see if there are active incidents",
|
||||
"2. Use list_monitors with query to find any monitors with issues",
|
||||
"3. Use list_incidents to get details of any active incidents",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Create and manage incident",
|
||||
steps: [
|
||||
"1. Use list_incident_states to get available states",
|
||||
"2. Use create_incident with title, description, and severity",
|
||||
"3. Use update_incident to change state as incident progresses",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Incident summary report",
|
||||
steps: [
|
||||
"1. Use count_incidents to get total count",
|
||||
"2. Use list_incidents with sort by createdAt descending",
|
||||
"3. Group and summarize the results",
|
||||
],
|
||||
},
|
||||
];
|
||||
break;
|
||||
|
||||
case "examples":
|
||||
(response["data"] as Record<string, unknown>)["examples"] = {
|
||||
listRecentIncidents: {
|
||||
tool: "list_incidents",
|
||||
args: { limit: 10, sort: { createdAt: -1 } },
|
||||
},
|
||||
countActiveIncidents: {
|
||||
tool: "count_incidents",
|
||||
args: { query: {} },
|
||||
},
|
||||
getSpecificIncident: {
|
||||
tool: "get_incident",
|
||||
args: { id: "<incident-uuid>" },
|
||||
},
|
||||
updateIncidentTitle: {
|
||||
tool: "update_incident",
|
||||
args: { id: "<incident-uuid>", title: "Updated title" },
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
(response["data"] as Record<string, unknown>)["welcome"] =
|
||||
"Welcome to OneUptime MCP Server!";
|
||||
(response["data"] as Record<string, unknown>)["description"] =
|
||||
"OneUptime is an open-source monitoring platform. This MCP server lets you manage incidents, monitors, alerts, and more.";
|
||||
(response["data"] as Record<string, unknown>)["availableTopics"] = [
|
||||
"resources",
|
||||
"incidents",
|
||||
"monitors",
|
||||
"alerts",
|
||||
"workflows",
|
||||
"examples",
|
||||
];
|
||||
(response["data"] as Record<string, unknown>)["quickStart"] = [
|
||||
"1. Use 'oneuptime_list_resources' to see all available resources",
|
||||
"2. Use 'list_*' tools to browse existing data",
|
||||
"3. Use 'count_*' tools to get quick summaries",
|
||||
"4. Use 'create_*' tools to add new items",
|
||||
];
|
||||
(response["data"] as Record<string, unknown>)["resourceCount"] =
|
||||
resourceList.length;
|
||||
break;
|
||||
}
|
||||
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
function handleListResourcesTool(resources: ResourceInfo[]): string {
|
||||
const response: Record<string, unknown> = {
|
||||
success: true,
|
||||
totalResources: resources.length,
|
||||
resources: resources.map((r: ResourceInfo) => {
|
||||
return {
|
||||
name: r.name,
|
||||
singularName: r.singularName,
|
||||
pluralName: r.pluralName,
|
||||
description: r.description,
|
||||
operations: r.operations,
|
||||
tools: {
|
||||
create: `create_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
get: `get_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
list: `list_${r.pluralName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
update: `update_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
delete: `delete_${r.singularName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
count: `count_${r.pluralName.toLowerCase().replace(/\s+/g, "_")}`,
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
return JSON.stringify(response, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tool is a helper tool
|
||||
* Check if a tool is a helper tool (doesn't require API key)
|
||||
*/
|
||||
export function isHelperTool(toolName: string): boolean {
|
||||
return toolName === "oneuptime_help" || toolName === "oneuptime_list_resources";
|
||||
return (
|
||||
toolName === "oneuptime_help" || toolName === "oneuptime_list_resources"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tool doesn't require API key (helper or public status page tool)
|
||||
*/
|
||||
export function isPublicTool(toolName: string): boolean {
|
||||
// Import check is done in ToolHandler to avoid circular dependencies
|
||||
return isHelperTool(toolName);
|
||||
}
|
||||
|
|
|
|||
472
MCP/Tools/PublicStatusPageTools.ts
Normal file
472
MCP/Tools/PublicStatusPageTools.ts
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
/**
|
||||
* Public Status Page Tools
|
||||
* Provides tools for querying public status pages without authentication
|
||||
* These tools can be used with either a status page ID or domain name
|
||||
*/
|
||||
|
||||
import { McpToolInfo, JSONSchema } from "../Types/McpTypes";
|
||||
import OneUptimeOperation from "../Types/OneUptimeOperation";
|
||||
import ModelType from "../Types/ModelType";
|
||||
import MCPLogger from "../Utils/MCPLogger";
|
||||
import API from "Common/Utils/API";
|
||||
import URL from "Common/Types/API/URL";
|
||||
import Route from "Common/Types/API/Route";
|
||||
import Headers from "Common/Types/API/Headers";
|
||||
import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import HTTPErrorResponse from "Common/Types/API/HTTPErrorResponse";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import { getApiUrl } from "../Config/ServerConfig";
|
||||
|
||||
// Common input schema for status page identifier
|
||||
const statusPageIdentifierSchema: JSONSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
statusPageIdOrDomain: {
|
||||
type: "string",
|
||||
description:
|
||||
"The status page ID (UUID) or domain name (e.g., 'status.company.com'). Use domain for public status pages with custom domains.",
|
||||
},
|
||||
},
|
||||
required: ["statusPageIdOrDomain"],
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate public status page tools
|
||||
*/
|
||||
export function generatePublicStatusPageTools(): McpToolInfo[] {
|
||||
return [
|
||||
createGetOverviewTool(),
|
||||
createGetIncidentsTool(),
|
||||
createGetScheduledMaintenanceTool(),
|
||||
createGetAnnouncementsTool(),
|
||||
createResolveStatusPageTool(),
|
||||
];
|
||||
}
|
||||
|
||||
function createGetOverviewTool(): McpToolInfo {
|
||||
return {
|
||||
name: "get_public_status_page_overview",
|
||||
description: `Get the complete overview of a public status page including current status, resources, active incidents, scheduled maintenance, and announcements.
|
||||
|
||||
This tool does NOT require an API key and works with public status pages.
|
||||
|
||||
USAGE:
|
||||
- By domain: statusPageIdOrDomain = "status.company.com"
|
||||
- By ID: statusPageIdOrDomain = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
RETURNS:
|
||||
- Status page metadata (name, description, branding)
|
||||
- Resources and their current status
|
||||
- Active incidents
|
||||
- Upcoming scheduled maintenance
|
||||
- Active announcements
|
||||
- Monitor status history`,
|
||||
inputSchema: statusPageIdentifierSchema,
|
||||
modelName: "StatusPageOverview",
|
||||
operation: OneUptimeOperation.Read,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Status Page Overview",
|
||||
pluralName: "Status Page Overviews",
|
||||
tableName: "StatusPageOverview",
|
||||
apiPath: "/status-page",
|
||||
};
|
||||
}
|
||||
|
||||
function createGetIncidentsTool(): McpToolInfo {
|
||||
return {
|
||||
name: "get_public_status_page_incidents",
|
||||
description: `Get incidents from a public status page.
|
||||
|
||||
This tool does NOT require an API key and works with public status pages.
|
||||
|
||||
USAGE:
|
||||
- By domain: statusPageIdOrDomain = "status.company.com"
|
||||
- By ID: statusPageIdOrDomain = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
RETURNS:
|
||||
- List of incidents (active and recent history)
|
||||
- Incident details (title, description, severity)
|
||||
- Incident timeline and state changes
|
||||
- Public notes/updates for each incident`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
statusPageIdOrDomain: {
|
||||
type: "string",
|
||||
description:
|
||||
"The status page ID (UUID) or domain name (e.g., 'status.company.com')",
|
||||
},
|
||||
incidentId: {
|
||||
type: "string",
|
||||
description: "Optional: Specific incident ID to fetch details for",
|
||||
},
|
||||
},
|
||||
required: ["statusPageIdOrDomain"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName: "StatusPageIncidents",
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Status Page Incident",
|
||||
pluralName: "Status Page Incidents",
|
||||
tableName: "StatusPageIncidents",
|
||||
apiPath: "/status-page",
|
||||
};
|
||||
}
|
||||
|
||||
function createGetScheduledMaintenanceTool(): McpToolInfo {
|
||||
return {
|
||||
name: "get_public_status_page_scheduled_maintenance",
|
||||
description: `Get scheduled maintenance events from a public status page.
|
||||
|
||||
This tool does NOT require an API key and works with public status pages.
|
||||
|
||||
USAGE:
|
||||
- By domain: statusPageIdOrDomain = "status.company.com"
|
||||
- By ID: statusPageIdOrDomain = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
RETURNS:
|
||||
- List of scheduled maintenance events (upcoming and ongoing)
|
||||
- Maintenance details (title, description, scheduled times)
|
||||
- Maintenance timeline and state changes
|
||||
- Public notes/updates for each maintenance event`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
statusPageIdOrDomain: {
|
||||
type: "string",
|
||||
description:
|
||||
"The status page ID (UUID) or domain name (e.g., 'status.company.com')",
|
||||
},
|
||||
scheduledMaintenanceId: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional: Specific scheduled maintenance ID to fetch details for",
|
||||
},
|
||||
},
|
||||
required: ["statusPageIdOrDomain"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName: "StatusPageScheduledMaintenance",
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Status Page Scheduled Maintenance",
|
||||
pluralName: "Status Page Scheduled Maintenances",
|
||||
tableName: "StatusPageScheduledMaintenance",
|
||||
apiPath: "/status-page",
|
||||
};
|
||||
}
|
||||
|
||||
function createGetAnnouncementsTool(): McpToolInfo {
|
||||
return {
|
||||
name: "get_public_status_page_announcements",
|
||||
description: `Get announcements from a public status page.
|
||||
|
||||
This tool does NOT require an API key and works with public status pages.
|
||||
|
||||
USAGE:
|
||||
- By domain: statusPageIdOrDomain = "status.company.com"
|
||||
- By ID: statusPageIdOrDomain = "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
RETURNS:
|
||||
- List of active announcements
|
||||
- Announcement details (title, description, dates)`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
statusPageIdOrDomain: {
|
||||
type: "string",
|
||||
description:
|
||||
"The status page ID (UUID) or domain name (e.g., 'status.company.com')",
|
||||
},
|
||||
announcementId: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional: Specific announcement ID to fetch details for",
|
||||
},
|
||||
},
|
||||
required: ["statusPageIdOrDomain"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName: "StatusPageAnnouncements",
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Status Page Announcement",
|
||||
pluralName: "Status Page Announcements",
|
||||
tableName: "StatusPageAnnouncements",
|
||||
apiPath: "/status-page",
|
||||
};
|
||||
}
|
||||
|
||||
function createResolveStatusPageTool(): McpToolInfo {
|
||||
return {
|
||||
name: "resolve_status_page_domain",
|
||||
description: `Resolve a status page domain to get the status page ID and basic information.
|
||||
|
||||
This tool does NOT require an API key.
|
||||
|
||||
USAGE:
|
||||
- statusPageIdOrDomain = "status.company.com"
|
||||
|
||||
RETURNS:
|
||||
- Status page ID
|
||||
- Page title
|
||||
- Page description`,
|
||||
inputSchema: statusPageIdentifierSchema,
|
||||
modelName: "StatusPageResolve",
|
||||
operation: OneUptimeOperation.Read,
|
||||
modelType: ModelType.Database,
|
||||
singularName: "Status Page",
|
||||
pluralName: "Status Pages",
|
||||
tableName: "StatusPageResolve",
|
||||
apiPath: "/status-page",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tool is a public status page tool
|
||||
*/
|
||||
export function isPublicStatusPageTool(toolName: string): boolean {
|
||||
return (
|
||||
toolName === "get_public_status_page_overview" ||
|
||||
toolName === "get_public_status_page_incidents" ||
|
||||
toolName === "get_public_status_page_scheduled_maintenance" ||
|
||||
toolName === "get_public_status_page_announcements" ||
|
||||
toolName === "resolve_status_page_domain"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle public status page tool execution
|
||||
*/
|
||||
export async function handlePublicStatusPageTool(
|
||||
toolName: string,
|
||||
args: Record<string, unknown>,
|
||||
): Promise<string> {
|
||||
const statusPageIdOrDomain: string = args["statusPageIdOrDomain"] as string;
|
||||
|
||||
if (!statusPageIdOrDomain) {
|
||||
return JSON.stringify({
|
||||
success: false,
|
||||
error: "statusPageIdOrDomain is required",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
switch (toolName) {
|
||||
case "get_public_status_page_overview":
|
||||
return await getStatusPageOverview(statusPageIdOrDomain);
|
||||
|
||||
case "get_public_status_page_incidents":
|
||||
return await getStatusPageIncidents(
|
||||
statusPageIdOrDomain,
|
||||
args["incidentId"] as string | undefined,
|
||||
);
|
||||
|
||||
case "get_public_status_page_scheduled_maintenance":
|
||||
return await getStatusPageScheduledMaintenance(
|
||||
statusPageIdOrDomain,
|
||||
args["scheduledMaintenanceId"] as string | undefined,
|
||||
);
|
||||
|
||||
case "get_public_status_page_announcements":
|
||||
return await getStatusPageAnnouncements(
|
||||
statusPageIdOrDomain,
|
||||
args["announcementId"] as string | undefined,
|
||||
);
|
||||
|
||||
case "resolve_status_page_domain":
|
||||
return await resolveStatusPage(statusPageIdOrDomain);
|
||||
|
||||
default:
|
||||
return JSON.stringify({
|
||||
success: false,
|
||||
error: `Unknown public status page tool: ${toolName}`,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
MCPLogger.error(
|
||||
`Error executing public status page tool ${toolName}: ${error}`,
|
||||
);
|
||||
return JSON.stringify({
|
||||
success: false,
|
||||
error: `Failed to execute ${toolName}: ${error}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status page overview
|
||||
* The backend now accepts both statusPageId and domain directly
|
||||
*/
|
||||
async function getStatusPageOverview(
|
||||
statusPageIdOrDomain: string,
|
||||
): Promise<string> {
|
||||
const response: JSONObject = await makeStatusPageApiRequest(
|
||||
"POST",
|
||||
`/api/status-page/overview/${statusPageIdOrDomain}`,
|
||||
);
|
||||
|
||||
return JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
operation: "get_overview",
|
||||
statusPageIdOrDomain,
|
||||
data: response,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status page incidents
|
||||
* The backend now accepts both statusPageId and domain directly
|
||||
*/
|
||||
async function getStatusPageIncidents(
|
||||
statusPageIdOrDomain: string,
|
||||
incidentId?: string,
|
||||
): Promise<string> {
|
||||
let route: string = `/api/status-page/incidents/${statusPageIdOrDomain}`;
|
||||
if (incidentId) {
|
||||
route = `/api/status-page/incidents/${statusPageIdOrDomain}/${incidentId}`;
|
||||
}
|
||||
|
||||
const response: JSONObject = await makeStatusPageApiRequest("POST", route);
|
||||
|
||||
return JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
operation: "get_incidents",
|
||||
statusPageIdOrDomain,
|
||||
incidentId: incidentId || null,
|
||||
data: response,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status page scheduled maintenance events
|
||||
* The backend now accepts both statusPageId and domain directly
|
||||
*/
|
||||
async function getStatusPageScheduledMaintenance(
|
||||
statusPageIdOrDomain: string,
|
||||
scheduledMaintenanceId?: string,
|
||||
): Promise<string> {
|
||||
let route: string = `/api/status-page/scheduled-maintenance-events/${statusPageIdOrDomain}`;
|
||||
if (scheduledMaintenanceId) {
|
||||
route = `/api/status-page/scheduled-maintenance-events/${statusPageIdOrDomain}/${scheduledMaintenanceId}`;
|
||||
}
|
||||
|
||||
const response: JSONObject = await makeStatusPageApiRequest("POST", route);
|
||||
|
||||
return JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
operation: "get_scheduled_maintenance",
|
||||
statusPageIdOrDomain,
|
||||
scheduledMaintenanceId: scheduledMaintenanceId || null,
|
||||
data: response,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status page announcements
|
||||
* The backend now accepts both statusPageId and domain directly
|
||||
*/
|
||||
async function getStatusPageAnnouncements(
|
||||
statusPageIdOrDomain: string,
|
||||
announcementId?: string,
|
||||
): Promise<string> {
|
||||
let route: string = `/api/status-page/announcements/${statusPageIdOrDomain}`;
|
||||
if (announcementId) {
|
||||
route = `/api/status-page/announcements/${statusPageIdOrDomain}/${announcementId}`;
|
||||
}
|
||||
|
||||
const response: JSONObject = await makeStatusPageApiRequest("POST", route);
|
||||
|
||||
return JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
operation: "get_announcements",
|
||||
statusPageIdOrDomain,
|
||||
announcementId: announcementId || null,
|
||||
data: response,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve status page and get basic info
|
||||
*/
|
||||
async function resolveStatusPage(
|
||||
statusPageIdOrDomain: string,
|
||||
): Promise<string> {
|
||||
const seoData: JSONObject = await makeStatusPageApiRequest(
|
||||
"GET",
|
||||
`/api/status-page/seo/${statusPageIdOrDomain}`,
|
||||
);
|
||||
|
||||
return JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
operation: "resolve_status_page",
|
||||
statusPageIdOrDomain,
|
||||
data: {
|
||||
statusPageId: seoData["_id"],
|
||||
title: seoData["title"],
|
||||
description: seoData["description"],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to the StatusPage API
|
||||
*/
|
||||
async function makeStatusPageApiRequest(
|
||||
method: "GET" | "POST",
|
||||
path: string,
|
||||
data?: JSONObject,
|
||||
): Promise<JSONObject> {
|
||||
const apiUrl: string = getApiUrl();
|
||||
const url: URL = URL.fromString(apiUrl);
|
||||
const route: Route = new Route(path);
|
||||
const fullUrl: URL = new URL(url.protocol, url.hostname, route);
|
||||
|
||||
const headers: Headers = {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
};
|
||||
|
||||
MCPLogger.info(`Making ${method} request to ${fullUrl.toString()}`);
|
||||
|
||||
let response: HTTPResponse<JSONObject> | HTTPErrorResponse;
|
||||
|
||||
if (method === "GET") {
|
||||
response = await API.get({ url: fullUrl, headers });
|
||||
} else {
|
||||
response = await API.post({ url: fullUrl, headers, data: data || {} });
|
||||
}
|
||||
|
||||
if (response instanceof HTTPErrorResponse) {
|
||||
MCPLogger.error(
|
||||
`API request failed: ${response.statusCode} - ${response.message}`,
|
||||
);
|
||||
throw new Error(
|
||||
`API request failed: ${response.statusCode} - ${response.message}`,
|
||||
);
|
||||
}
|
||||
|
||||
return response.data as JSONObject;
|
||||
}
|
||||
|
|
@ -9,479 +9,533 @@ import DatabaseBaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/Da
|
|||
import AnalyticsBaseModel from "Common/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
||||
import { ModelSchema, ModelSchemaType } from "Common/Utils/Schema/ModelSchema";
|
||||
import {
|
||||
AnalyticsModelSchema,
|
||||
AnalyticsModelSchemaType,
|
||||
AnalyticsModelSchema,
|
||||
AnalyticsModelSchemaType,
|
||||
} from "Common/Utils/Schema/AnalyticsModelSchema";
|
||||
import { McpToolInfo, ModelToolsResult } from "../Types/McpTypes";
|
||||
import OneUptimeOperation from "../Types/OneUptimeOperation";
|
||||
import ModelType from "../Types/ModelType";
|
||||
import { zodToJsonSchema, sanitizeToolName, ZodToJsonSchemaResult } from "./SchemaConverter";
|
||||
import {
|
||||
zodToJsonSchema,
|
||||
sanitizeToolName,
|
||||
ZodToJsonSchemaResult,
|
||||
} from "./SchemaConverter";
|
||||
import { generateHelperTools } from "./HelperTools";
|
||||
import { generatePublicStatusPageTools } from "./PublicStatusPageTools";
|
||||
import MCPLogger from "../Utils/MCPLogger";
|
||||
|
||||
/**
|
||||
* Generate all MCP tools for all OneUptime models
|
||||
*/
|
||||
export function generateAllTools(): McpToolInfo[] {
|
||||
const allTools: McpToolInfo[] = [];
|
||||
const allTools: McpToolInfo[] = [];
|
||||
|
||||
// Generate tools for Database Models
|
||||
const databaseTools: McpToolInfo[] = generateDatabaseModelTools();
|
||||
allTools.push(...databaseTools);
|
||||
// Generate tools for Database Models
|
||||
const databaseTools: McpToolInfo[] = generateDatabaseModelTools();
|
||||
allTools.push(...databaseTools);
|
||||
|
||||
// Generate tools for Analytics Models
|
||||
const analyticsTools: McpToolInfo[] = generateAnalyticsModelTools();
|
||||
allTools.push(...analyticsTools);
|
||||
// Generate tools for Analytics Models
|
||||
const analyticsTools: McpToolInfo[] = generateAnalyticsModelTools();
|
||||
allTools.push(...analyticsTools);
|
||||
|
||||
// Generate helper tools for discovery and guidance
|
||||
const helperTools: McpToolInfo[] = generateHelperTools(allTools);
|
||||
allTools.push(...helperTools);
|
||||
// Generate helper tools for discovery and guidance
|
||||
const helperTools: McpToolInfo[] = generateHelperTools(allTools);
|
||||
allTools.push(...helperTools);
|
||||
|
||||
MCPLogger.info(`Generated ${allTools.length} MCP tools for OneUptime models (including ${helperTools.length} helper tools)`);
|
||||
return allTools;
|
||||
// Generate public status page tools (no API key required)
|
||||
const publicStatusPageTools: McpToolInfo[] = generatePublicStatusPageTools();
|
||||
allTools.push(...publicStatusPageTools);
|
||||
|
||||
MCPLogger.info(
|
||||
`Generated ${allTools.length} MCP tools for OneUptime models (including ${helperTools.length} helper tools and ${publicStatusPageTools.length} public status page tools)`,
|
||||
);
|
||||
return allTools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tools for all database models
|
||||
*/
|
||||
function generateDatabaseModelTools(): McpToolInfo[] {
|
||||
const tools: McpToolInfo[] = [];
|
||||
const tools: McpToolInfo[] = [];
|
||||
|
||||
for (const ModelClass of DatabaseModels) {
|
||||
try {
|
||||
const model: DatabaseBaseModel = new ModelClass();
|
||||
const result: ModelToolsResult = generateToolsForDatabaseModel(model, ModelClass);
|
||||
tools.push(...result.tools);
|
||||
} catch (error) {
|
||||
MCPLogger.error(
|
||||
`Error generating tools for database model ${ModelClass.name}: ${error}`
|
||||
);
|
||||
}
|
||||
for (const ModelClass of DatabaseModels) {
|
||||
try {
|
||||
const model: DatabaseBaseModel = new ModelClass();
|
||||
const result: ModelToolsResult = generateToolsForDatabaseModel(
|
||||
model,
|
||||
ModelClass,
|
||||
);
|
||||
tools.push(...result.tools);
|
||||
} catch (error) {
|
||||
MCPLogger.error(
|
||||
`Error generating tools for database model ${ModelClass.name}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return tools;
|
||||
return tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tools for all analytics models
|
||||
*/
|
||||
function generateAnalyticsModelTools(): McpToolInfo[] {
|
||||
const tools: McpToolInfo[] = [];
|
||||
const tools: McpToolInfo[] = [];
|
||||
|
||||
for (const ModelClass of AnalyticsModels) {
|
||||
try {
|
||||
const model: AnalyticsBaseModel = new ModelClass();
|
||||
const result: ModelToolsResult = generateToolsForAnalyticsModel(model, ModelClass);
|
||||
tools.push(...result.tools);
|
||||
} catch (error) {
|
||||
MCPLogger.error(
|
||||
`Error generating tools for analytics model ${ModelClass.name}: ${error}`
|
||||
);
|
||||
}
|
||||
for (const ModelClass of AnalyticsModels) {
|
||||
try {
|
||||
const model: AnalyticsBaseModel = new ModelClass();
|
||||
const result: ModelToolsResult = generateToolsForAnalyticsModel(
|
||||
model,
|
||||
ModelClass,
|
||||
);
|
||||
tools.push(...result.tools);
|
||||
} catch (error) {
|
||||
MCPLogger.error(
|
||||
`Error generating tools for analytics model ${ModelClass.name}: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return tools;
|
||||
return tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate MCP tools for a specific database model
|
||||
*/
|
||||
export function generateToolsForDatabaseModel(
|
||||
model: DatabaseBaseModel,
|
||||
ModelClass: { new (): DatabaseBaseModel }
|
||||
model: DatabaseBaseModel,
|
||||
ModelClass: { new (): DatabaseBaseModel },
|
||||
): ModelToolsResult {
|
||||
const modelName: string = model.tableName || ModelClass.name;
|
||||
const singularName: string = model.singularName || modelName;
|
||||
const pluralName: string = model.pluralName || `${singularName}s`;
|
||||
const apiPath: string | undefined = model.crudApiPath?.toString();
|
||||
const modelName: string = model.tableName || ModelClass.name;
|
||||
const singularName: string = model.singularName || modelName;
|
||||
const pluralName: string = model.pluralName || `${singularName}s`;
|
||||
const apiPath: string | undefined = model.crudApiPath?.toString();
|
||||
|
||||
const modelInfo = {
|
||||
tableName: modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
modelType: ModelType.Database,
|
||||
...(apiPath && { apiPath }),
|
||||
};
|
||||
const modelInfo = {
|
||||
tableName: modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
modelType: ModelType.Database,
|
||||
...(apiPath && { apiPath }),
|
||||
};
|
||||
|
||||
// Skip if model doesn't have required properties or MCP is disabled
|
||||
if (!modelName || !model.enableMCP || !apiPath) {
|
||||
return { tools: [], modelInfo };
|
||||
}
|
||||
// Skip if model doesn't have required properties or MCP is disabled
|
||||
if (!modelName || !model.enableMCP || !apiPath) {
|
||||
return { tools: [], modelInfo };
|
||||
}
|
||||
|
||||
// Generate schemas using ModelSchema
|
||||
const createSchema: ModelSchemaType = ModelSchema.getCreateModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const updateSchema: ModelSchemaType = ModelSchema.getUpdateModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const querySchema: ModelSchemaType = ModelSchema.getQueryModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const sortSchema: ModelSchemaType = ModelSchema.getSortModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
// Generate schemas using ModelSchema
|
||||
const createSchema: ModelSchemaType = ModelSchema.getCreateModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const updateSchema: ModelSchemaType = ModelSchema.getUpdateModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const querySchema: ModelSchemaType = ModelSchema.getQueryModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const sortSchema: ModelSchemaType = ModelSchema.getSortModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
|
||||
const tools: McpToolInfo[] = [
|
||||
createCreateTool(modelName, singularName, pluralName, apiPath, createSchema),
|
||||
createReadTool(modelName, singularName, pluralName, apiPath),
|
||||
createListTool(modelName, singularName, pluralName, apiPath, querySchema, sortSchema),
|
||||
createUpdateTool(modelName, singularName, pluralName, apiPath, updateSchema),
|
||||
createDeleteTool(modelName, singularName, pluralName, apiPath),
|
||||
createCountTool(modelName, singularName, pluralName, apiPath, querySchema),
|
||||
];
|
||||
const tools: McpToolInfo[] = [
|
||||
createCreateTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
createSchema,
|
||||
),
|
||||
createReadTool(modelName, singularName, pluralName, apiPath),
|
||||
createListTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
querySchema,
|
||||
sortSchema,
|
||||
),
|
||||
createUpdateTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
updateSchema,
|
||||
),
|
||||
createDeleteTool(modelName, singularName, pluralName, apiPath),
|
||||
createCountTool(modelName, singularName, pluralName, apiPath, querySchema),
|
||||
];
|
||||
|
||||
return { tools, modelInfo };
|
||||
return { tools, modelInfo };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate MCP tools for a specific analytics model
|
||||
*/
|
||||
export function generateToolsForAnalyticsModel(
|
||||
model: AnalyticsBaseModel,
|
||||
ModelClass: { new (): AnalyticsBaseModel }
|
||||
model: AnalyticsBaseModel,
|
||||
ModelClass: { new (): AnalyticsBaseModel },
|
||||
): ModelToolsResult {
|
||||
const modelName: string = model.tableName || ModelClass.name;
|
||||
const singularName: string = model.singularName || modelName;
|
||||
const pluralName: string = model.pluralName || `${singularName}s`;
|
||||
const apiPath: string | undefined = model.crudApiPath?.toString();
|
||||
const modelName: string = model.tableName || ModelClass.name;
|
||||
const singularName: string = model.singularName || modelName;
|
||||
const pluralName: string = model.pluralName || `${singularName}s`;
|
||||
const apiPath: string | undefined = model.crudApiPath?.toString();
|
||||
|
||||
const modelInfo = {
|
||||
tableName: modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
modelType: ModelType.Analytics,
|
||||
apiPath,
|
||||
};
|
||||
const modelInfo = {
|
||||
tableName: modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
modelType: ModelType.Analytics,
|
||||
apiPath,
|
||||
};
|
||||
|
||||
// Skip if model doesn't have required properties or MCP is disabled
|
||||
if (!modelName || !model.enableMCP || !apiPath) {
|
||||
return { tools: [], modelInfo };
|
||||
}
|
||||
// Skip if model doesn't have required properties or MCP is disabled
|
||||
if (!modelName || !model.enableMCP || !apiPath) {
|
||||
return { tools: [], modelInfo };
|
||||
}
|
||||
|
||||
// Generate schemas using AnalyticsModelSchema
|
||||
const createSchema: AnalyticsModelSchemaType = AnalyticsModelSchema.getCreateModelSchema({
|
||||
modelType: ModelClass,
|
||||
disableOpenApiSchema: true,
|
||||
// Generate schemas using AnalyticsModelSchema
|
||||
const createSchema: AnalyticsModelSchemaType =
|
||||
AnalyticsModelSchema.getCreateModelSchema({
|
||||
modelType: ModelClass,
|
||||
disableOpenApiSchema: true,
|
||||
});
|
||||
const querySchema: AnalyticsModelSchemaType = AnalyticsModelSchema.getQueryModelSchema({
|
||||
modelType: ModelClass,
|
||||
disableOpenApiSchema: true,
|
||||
const querySchema: AnalyticsModelSchemaType =
|
||||
AnalyticsModelSchema.getQueryModelSchema({
|
||||
modelType: ModelClass,
|
||||
disableOpenApiSchema: true,
|
||||
});
|
||||
const selectSchema: AnalyticsModelSchemaType = AnalyticsModelSchema.getSelectModelSchema({
|
||||
modelType: ModelClass,
|
||||
const selectSchema: AnalyticsModelSchemaType =
|
||||
AnalyticsModelSchema.getSelectModelSchema({
|
||||
modelType: ModelClass,
|
||||
});
|
||||
const sortSchema: AnalyticsModelSchemaType = AnalyticsModelSchema.getSortModelSchema({
|
||||
modelType: ModelClass,
|
||||
disableOpenApiSchema: true,
|
||||
const sortSchema: AnalyticsModelSchemaType =
|
||||
AnalyticsModelSchema.getSortModelSchema({
|
||||
modelType: ModelClass,
|
||||
disableOpenApiSchema: true,
|
||||
});
|
||||
|
||||
const tools: McpToolInfo[] = [
|
||||
createAnalyticsCreateTool(modelName, singularName, pluralName, apiPath, createSchema),
|
||||
createAnalyticsListTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
querySchema,
|
||||
selectSchema,
|
||||
sortSchema
|
||||
),
|
||||
createAnalyticsCountTool(modelName, singularName, pluralName, apiPath, querySchema),
|
||||
];
|
||||
const tools: McpToolInfo[] = [
|
||||
createAnalyticsCreateTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
createSchema,
|
||||
),
|
||||
createAnalyticsListTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
querySchema,
|
||||
selectSchema,
|
||||
sortSchema,
|
||||
),
|
||||
createAnalyticsCountTool(
|
||||
modelName,
|
||||
singularName,
|
||||
pluralName,
|
||||
apiPath,
|
||||
querySchema,
|
||||
),
|
||||
];
|
||||
|
||||
return { tools, modelInfo };
|
||||
return { tools, modelInfo };
|
||||
}
|
||||
|
||||
// Database Model Tool Creators
|
||||
|
||||
function createCreateTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
createSchema: ModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
createSchema: ModelSchemaType,
|
||||
): McpToolInfo {
|
||||
const schemaProperties: ZodToJsonSchemaResult = zodToJsonSchema(createSchema);
|
||||
const schemaProperties: ZodToJsonSchemaResult = zodToJsonSchema(createSchema);
|
||||
|
||||
return {
|
||||
name: `create_${sanitizeToolName(singularName)}`,
|
||||
description: `Create a new ${singularName} in OneUptime. Returns the created ${singularName} object with its ID and all fields. Use this to add new ${pluralName} to your project.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: schemaProperties.properties || {},
|
||||
required: schemaProperties.required || [],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Create,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
return {
|
||||
name: `create_${sanitizeToolName(singularName)}`,
|
||||
description: `Create a new ${singularName} in OneUptime. Returns the created ${singularName} object with its ID and all fields. Use this to add new ${pluralName} to your project.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: schemaProperties.properties || {},
|
||||
required: schemaProperties.required || [],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Create,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createReadTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
): McpToolInfo {
|
||||
return {
|
||||
name: `get_${sanitizeToolName(singularName)}`,
|
||||
description: `Retrieve a single ${singularName} by its unique ID from OneUptime. Returns the complete ${singularName} object with all its fields. Use list_${sanitizeToolName(pluralName)} first if you need to find the ID.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: `The unique identifier (UUID) of the ${singularName} to retrieve. Example: "550e8400-e29b-41d4-a716-446655440000"`,
|
||||
},
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
return {
|
||||
name: `get_${sanitizeToolName(singularName)}`,
|
||||
description: `Retrieve a single ${singularName} by its unique ID from OneUptime. Returns the complete ${singularName} object with all its fields. Use list_${sanitizeToolName(pluralName)} first if you need to find the ID.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: `The unique identifier (UUID) of the ${singularName} to retrieve. Example: "550e8400-e29b-41d4-a716-446655440000"`,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Read,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Read,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createListTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: ModelSchemaType,
|
||||
sortSchema: ModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: ModelSchemaType,
|
||||
sortSchema: ModelSchemaType,
|
||||
): McpToolInfo {
|
||||
return {
|
||||
name: `list_${sanitizeToolName(pluralName)}`,
|
||||
description: `List and search ${pluralName} from OneUptime with optional filtering, pagination, and sorting. Returns an array of ${singularName} objects. Use the 'query' parameter to filter results by specific field values. Supports pagination via 'skip' and 'limit' parameters.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
...zodToJsonSchema(querySchema),
|
||||
description: `Filter criteria for ${pluralName}. Each field can be used to filter results. Example: {"title": "My ${singularName}"} to find by title.`,
|
||||
},
|
||||
skip: {
|
||||
type: "number",
|
||||
description: "Number of records to skip for pagination. Default: 0. Example: skip=10 to start from the 11th record.",
|
||||
},
|
||||
limit: {
|
||||
type: "number",
|
||||
description: "Maximum number of records to return. Default: 10, Maximum: 100. Example: limit=25 to get 25 records.",
|
||||
},
|
||||
sort: {
|
||||
...zodToJsonSchema(sortSchema),
|
||||
description: `Sort order for results. Use 1 for ascending, -1 for descending. Example: {"createdAt": -1} to sort by newest first.`,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
return {
|
||||
name: `list_${sanitizeToolName(pluralName)}`,
|
||||
description: `List and search ${pluralName} from OneUptime with optional filtering, pagination, and sorting. Returns an array of ${singularName} objects. Use the 'query' parameter to filter results by specific field values. Supports pagination via 'skip' and 'limit' parameters.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
...zodToJsonSchema(querySchema),
|
||||
description: `Filter criteria for ${pluralName}. Each field can be used to filter results. Example: {"title": "My ${singularName}"} to find by title.`,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
skip: {
|
||||
type: "number",
|
||||
description:
|
||||
"Number of records to skip for pagination. Default: 0. Example: skip=10 to start from the 11th record.",
|
||||
},
|
||||
limit: {
|
||||
type: "number",
|
||||
description:
|
||||
"Maximum number of records to return. Default: 10, Maximum: 100. Example: limit=25 to get 25 records.",
|
||||
},
|
||||
sort: {
|
||||
...zodToJsonSchema(sortSchema),
|
||||
description: `Sort order for results. Use 1 for ascending, -1 for descending. Example: {"createdAt": -1} to sort by newest first.`,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createUpdateTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
updateSchema: ModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
updateSchema: ModelSchemaType,
|
||||
): McpToolInfo {
|
||||
const schemaProperties: ZodToJsonSchemaResult = zodToJsonSchema(updateSchema);
|
||||
const schemaProperties: ZodToJsonSchemaResult = zodToJsonSchema(updateSchema);
|
||||
|
||||
return {
|
||||
name: `update_${sanitizeToolName(singularName)}`,
|
||||
description: `Update an existing ${singularName} in OneUptime. Only include the fields you want to change - unspecified fields will remain unchanged. Returns the updated ${singularName} object.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: `The unique identifier (UUID) of the ${singularName} to update. Required. Use list_${sanitizeToolName(pluralName)} to find IDs.`,
|
||||
},
|
||||
...(schemaProperties.properties || {}),
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
return {
|
||||
name: `update_${sanitizeToolName(singularName)}`,
|
||||
description: `Update an existing ${singularName} in OneUptime. Only include the fields you want to change - unspecified fields will remain unchanged. Returns the updated ${singularName} object.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: `The unique identifier (UUID) of the ${singularName} to update. Required. Use list_${sanitizeToolName(pluralName)} to find IDs.`,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Update,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
...(schemaProperties.properties || {}),
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Update,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createDeleteTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
): McpToolInfo {
|
||||
return {
|
||||
name: `delete_${sanitizeToolName(singularName)}`,
|
||||
description: `Permanently delete a ${singularName} from OneUptime. This action cannot be undone. Returns a confirmation message upon successful deletion.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: `The unique identifier (UUID) of the ${singularName} to delete. This action is irreversible.`,
|
||||
},
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
return {
|
||||
name: `delete_${sanitizeToolName(singularName)}`,
|
||||
description: `Permanently delete a ${singularName} from OneUptime. This action cannot be undone. Returns a confirmation message upon successful deletion.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: `The unique identifier (UUID) of the ${singularName} to delete. This action is irreversible.`,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Delete,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
},
|
||||
required: ["id"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Delete,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createCountTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: ModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: ModelSchemaType,
|
||||
): McpToolInfo {
|
||||
return {
|
||||
name: `count_${sanitizeToolName(pluralName)}`,
|
||||
description: `Count the total number of ${pluralName} in OneUptime, optionally filtered by query criteria. Returns a single number. Useful for dashboards, reports, or checking if records exist before listing.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
...zodToJsonSchema(querySchema),
|
||||
description: `Optional filter criteria. If omitted, counts all ${pluralName}. Example: {"currentIncidentStateId": "..."} to count incidents in a specific state.`,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
return {
|
||||
name: `count_${sanitizeToolName(pluralName)}`,
|
||||
description: `Count the total number of ${pluralName} in OneUptime, optionally filtered by query criteria. Returns a single number. Useful for dashboards, reports, or checking if records exist before listing.`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
...zodToJsonSchema(querySchema),
|
||||
description: `Optional filter criteria. If omitted, counts all ${pluralName}. Example: {"currentIncidentStateId": "..."} to count incidents in a specific state.`,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Count,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Count,
|
||||
modelType: ModelType.Database,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
// Analytics Model Tool Creators
|
||||
|
||||
function createAnalyticsCreateTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
createSchema: AnalyticsModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
createSchema: AnalyticsModelSchemaType,
|
||||
): McpToolInfo {
|
||||
const schemaProperties: ZodToJsonSchemaResult = zodToJsonSchema(createSchema);
|
||||
const schemaProperties: ZodToJsonSchemaResult = zodToJsonSchema(createSchema);
|
||||
|
||||
return {
|
||||
name: `create_${sanitizeToolName(singularName)}`,
|
||||
description: `Create a new ${singularName} analytics record in OneUptime`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: schemaProperties.properties || {},
|
||||
required: schemaProperties.required || [],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Create,
|
||||
modelType: ModelType.Analytics,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
return {
|
||||
name: `create_${sanitizeToolName(singularName)}`,
|
||||
description: `Create a new ${singularName} analytics record in OneUptime`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: schemaProperties.properties || {},
|
||||
required: schemaProperties.required || [],
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Create,
|
||||
modelType: ModelType.Analytics,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createAnalyticsListTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: AnalyticsModelSchemaType,
|
||||
selectSchema: AnalyticsModelSchemaType,
|
||||
sortSchema: AnalyticsModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: AnalyticsModelSchemaType,
|
||||
selectSchema: AnalyticsModelSchemaType,
|
||||
sortSchema: AnalyticsModelSchemaType,
|
||||
): McpToolInfo {
|
||||
return {
|
||||
name: `list_${sanitizeToolName(pluralName)}`,
|
||||
description: `Query ${pluralName} analytics data from OneUptime`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: zodToJsonSchema(querySchema),
|
||||
select: zodToJsonSchema(selectSchema),
|
||||
skip: {
|
||||
type: "number",
|
||||
description: "Number of records to skip",
|
||||
},
|
||||
limit: {
|
||||
type: "number",
|
||||
description: "Maximum number of records to return",
|
||||
},
|
||||
sort: zodToJsonSchema(sortSchema),
|
||||
},
|
||||
additionalProperties: false,
|
||||
return {
|
||||
name: `list_${sanitizeToolName(pluralName)}`,
|
||||
description: `Query ${pluralName} analytics data from OneUptime`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: zodToJsonSchema(querySchema),
|
||||
select: zodToJsonSchema(selectSchema),
|
||||
skip: {
|
||||
type: "number",
|
||||
description: "Number of records to skip",
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Analytics,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
limit: {
|
||||
type: "number",
|
||||
description: "Maximum number of records to return",
|
||||
},
|
||||
sort: zodToJsonSchema(sortSchema),
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.List,
|
||||
modelType: ModelType.Analytics,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
||||
function createAnalyticsCountTool(
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: AnalyticsModelSchemaType
|
||||
modelName: string,
|
||||
singularName: string,
|
||||
pluralName: string,
|
||||
apiPath: string,
|
||||
querySchema: AnalyticsModelSchemaType,
|
||||
): McpToolInfo {
|
||||
return {
|
||||
name: `count_${sanitizeToolName(pluralName)}`,
|
||||
description: `Count ${pluralName} analytics records in OneUptime`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: zodToJsonSchema(querySchema),
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Count,
|
||||
modelType: ModelType.Analytics,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
return {
|
||||
name: `count_${sanitizeToolName(pluralName)}`,
|
||||
description: `Count ${pluralName} analytics records in OneUptime`,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: zodToJsonSchema(querySchema),
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
modelName,
|
||||
operation: OneUptimeOperation.Count,
|
||||
modelType: ModelType.Analytics,
|
||||
singularName,
|
||||
pluralName,
|
||||
tableName: modelName,
|
||||
apiPath,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue