Files
oneuptime/MCP/Tools/ToolGenerator.ts

542 lines
16 KiB
TypeScript

/**
* Tool Generator
* Generates MCP tools for OneUptime models
*/
import DatabaseModels from "Common/Models/DatabaseModels/Index";
import AnalyticsModels from "Common/Models/AnalyticsModels/Index";
import DatabaseBaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
import AnalyticsBaseModel from "Common/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
import { ModelSchema, ModelSchemaType } from "Common/Utils/Schema/ModelSchema";
import {
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 { 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[] = [];
// 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 helper tools for discovery and guidance
const helperTools: McpToolInfo[] = generateHelperTools(allTools);
allTools.push(...helperTools);
// 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[] = [];
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;
}
/**
* Generate tools for all analytics models
*/
function generateAnalyticsModelTools(): 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}`,
);
}
}
return tools;
}
/**
* Generate MCP tools for a specific database model
*/
export function generateToolsForDatabaseModel(
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 modelInfo: ModelToolsResult["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 };
}
// 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),
];
return { tools, modelInfo };
}
/**
* Generate MCP tools for a specific analytics model
*/
export function generateToolsForAnalyticsModel(
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 modelInfo: ModelToolsResult["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 };
}
// Generate schemas using AnalyticsModelSchema
const createSchema: AnalyticsModelSchemaType =
AnalyticsModelSchema.getCreateModelSchema({
modelType: ModelClass,
disableOpenApiSchema: true,
});
const querySchema: AnalyticsModelSchemaType =
AnalyticsModelSchema.getQueryModelSchema({
modelType: ModelClass,
disableOpenApiSchema: true,
});
const selectSchema: AnalyticsModelSchemaType =
AnalyticsModelSchema.getSelectModelSchema({
modelType: ModelClass,
});
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,
),
];
return { tools, modelInfo };
}
// Database Model Tool Creators
function createCreateTool(
modelName: string,
singularName: string,
pluralName: string,
apiPath: string,
createSchema: ModelSchemaType,
): McpToolInfo {
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,
};
}
function createReadTool(
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,
},
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,
): 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,
},
modelName,
operation: OneUptimeOperation.List,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath,
};
}
function createUpdateTool(
modelName: string,
singularName: string,
pluralName: string,
apiPath: string,
updateSchema: ModelSchemaType,
): McpToolInfo {
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,
},
modelName,
operation: OneUptimeOperation.Update,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath,
};
}
function createDeleteTool(
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,
},
modelName,
operation: OneUptimeOperation.Delete,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath,
};
}
function createCountTool(
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,
},
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,
): McpToolInfo {
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,
};
}
function createAnalyticsListTool(
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,
},
modelName,
operation: OneUptimeOperation.List,
modelType: ModelType.Analytics,
singularName,
pluralName,
tableName: modelName,
apiPath,
};
}
function createAnalyticsCountTool(
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,
};
}