Refactor code structure for improved readability and maintainability

This commit is contained in:
Simon Larsen
2025-06-27 14:02:59 +01:00
parent 747d46eb83
commit 2a74183de5
11 changed files with 17904 additions and 180 deletions

View File

@@ -1,12 +1,16 @@
# Environment variables for OneUptime MCP Hello World Server
# OneUptime MCP Server Configuration
# Logging
LOG_LEVEL=info
# OneUptime Instance Configuration
ONEUPTIME_HOSTNAME=localhost:3002
ONEUPTIME_PROTOCOL=http
ONEUPTIME_BASE_ROUTE=/api/v1
# Server Configuration
# Authentication (Required for production)
ONEUPTIME_API_KEY=your_oneuptime_api_key_here
ONEUPTIME_PROJECT_ID=your_project_id_here
# Server Configuration
NODE_ENV=development
PORT=3002
# OneUptime Configuration
APP_NAME=mcp-hello-world
LOG_LEVEL=info
APP_NAME=oneuptime-mcp-server
APP_VERSION=1.0.0

View File

@@ -8,16 +8,24 @@ import {
} from "@modelcontextprotocol/sdk/types.js";
import logger from "Common/Server/Utils/Logger";
import dotenv from "dotenv";
import DynamicToolGenerator from "./Utils/DynamicToolGenerator";
import OneUptimeApiService, { OneUptimeApiConfig } from "./Services/OneUptimeApiService";
import { McpToolInfo, OneUptimeToolCallArgs } from "./Types/McpTypes";
import OneUptimeOperation from "./Types/OneUptimeOperation";
import ModelType from "./Types/ModelType";
import Protocol from "Common/Types/API/Protocol";
import Route from "Common/Types/API/Route";
// Load environment variables
dotenv.config();
const APP_NAME: string = "mcp-hello-world";
const APP_NAME: string = "oneuptime-mcp-server";
logger.info("OneUptime Hello World MCP Server is starting...");
logger.info("OneUptime MCP Server is starting...");
class HelloWorldMCPServer {
class OneUptimeMCPServer {
private server: Server;
private tools: McpToolInfo[] = [];
constructor() {
this.server = new Server(
@@ -32,52 +40,46 @@ class HelloWorldMCPServer {
}
);
this.initializeServices();
this.generateTools();
this.setupHandlers();
}
private initializeServices(): void {
// Initialize OneUptime API Service
const config: OneUptimeApiConfig = {
hostname: process.env.ONEUPTIME_HOSTNAME || "localhost:3002",
protocol: process.env.ONEUPTIME_PROTOCOL === "http" ? Protocol.HTTP : Protocol.HTTPS,
apiKey: process.env.ONEUPTIME_API_KEY,
projectId: process.env.ONEUPTIME_PROJECT_ID,
baseRoute: new Route(process.env.ONEUPTIME_BASE_ROUTE || "/api/v1"),
};
OneUptimeApiService.initialize(config);
logger.info("OneUptime API Service initialized");
}
private generateTools(): void {
try {
this.tools = DynamicToolGenerator.generateAllTools();
logger.info(`Generated ${this.tools.length} OneUptime MCP tools`);
} catch (error) {
logger.error(`Failed to generate tools: ${error}`);
throw error;
}
}
private setupHandlers(): void {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "hello",
description: "Say hello with a personalized greeting",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Name of the person to greet",
},
},
required: ["name"],
},
},
{
name: "get_time",
description: "Get the current server time",
inputSchema: {
type: "object",
properties: {},
},
},
{
name: "echo",
description: "Echo back any message",
inputSchema: {
type: "object",
properties: {
message: {
type: "string",
description: "Message to echo back",
},
},
required: ["message"],
},
},
],
};
const mcpTools = this.tools.map((tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
}));
logger.info(`Listing ${mcpTools.length} available tools`);
return { tools: mcpTools };
});
// Handle tool calls
@@ -85,89 +87,120 @@ class HelloWorldMCPServer {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "hello": {
const personName = args?.name as string;
if (!personName) {
throw new McpError(
ErrorCode.InvalidParams,
"Name parameter is required"
);
}
logger.info(`Saying hello to: ${personName}`);
return {
content: [
{
type: "text",
text: `Hello, ${personName}! Welcome to OneUptime's Hello World MCP Server! 🚀`,
},
],
};
}
case "get_time": {
const currentTime = new Date().toISOString();
logger.info(`Returning current time: ${currentTime}`);
return {
content: [
{
type: "text",
text: `Current server time: ${currentTime}`,
},
],
};
}
case "echo": {
const message = args?.message as string;
if (!message) {
throw new McpError(
ErrorCode.InvalidParams,
"Message parameter is required"
);
}
logger.info(`Echoing message: ${message}`);
return {
content: [
{
type: "text",
text: `Echo: ${message}`,
},
],
};
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
// Find the tool by name
const tool = this.tools.find((t) => t.name === name);
if (!tool) {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
logger.info(`Executing tool: ${name} for model: ${tool.modelName}`);
// Execute the OneUptime operation
const result = await OneUptimeApiService.executeOperation(
tool.modelName,
tool.operation,
tool.modelType,
tool.apiPath || "",
args as OneUptimeToolCallArgs
);
// Format the response
const responseText = this.formatToolResponse(tool, result, args as OneUptimeToolCallArgs);
return {
content: [
{
type: "text",
text: responseText,
},
],
};
} catch (error) {
logger.error(`Error executing tool ${name}:`, error);
throw error;
logger.error(`Error executing tool ${name}: ${error}`);
if (error instanceof McpError) {
throw error;
}
throw new McpError(
ErrorCode.InternalError,
`Failed to execute ${name}: ${error}`
);
}
});
}
private formatToolResponse(tool: McpToolInfo, result: any, args: OneUptimeToolCallArgs): string {
const operation = tool.operation;
const modelName = tool.singularName;
const pluralName = tool.pluralName;
switch (operation) {
case OneUptimeOperation.Create:
return `✅ Successfully created ${modelName}: ${JSON.stringify(result, null, 2)}`;
case OneUptimeOperation.Read:
if (result) {
return `📋 Retrieved ${modelName} (ID: ${args.id}): ${JSON.stringify(result, null, 2)}`;
} else {
return `${modelName} not found with ID: ${args.id}`;
}
case OneUptimeOperation.List:
const items = Array.isArray(result) ? result : result?.data || [];
const count = items.length;
const summary = `📊 Found ${count} ${count === 1 ? modelName : pluralName}`;
if (count === 0) {
return `${summary}. No items match the criteria.`;
}
const limitedItems = items.slice(0, 5); // Show first 5 items
const itemsText = limitedItems.map((item: any, index: number) =>
`${index + 1}. ${JSON.stringify(item, null, 2)}`
).join('\n');
const hasMore = count > 5 ? `\n... and ${count - 5} more items` : '';
return `${summary}:\n${itemsText}${hasMore}`;
case OneUptimeOperation.Update:
return `✅ Successfully updated ${modelName} (ID: ${args.id}): ${JSON.stringify(result, null, 2)}`;
case OneUptimeOperation.Delete:
return `🗑️ Successfully deleted ${modelName} (ID: ${args.id})`;
case OneUptimeOperation.Count:
const totalCount = result?.count || result || 0;
return `📊 Total count of ${pluralName}: ${totalCount}`;
default:
return `✅ Operation ${operation} completed successfully: ${JSON.stringify(result, null, 2)}`;
}
}
async run(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
logger.info("OneUptime Hello World MCP Server is running!");
logger.info("Available tools: hello, get_time, echo");
logger.info("OneUptime MCP Server is running!");
logger.info(`Available tools: ${this.tools.length} total`);
// Log some example tools
const exampleTools = this.tools.slice(0, 5).map(t => t.name);
logger.info(`Example tools: ${exampleTools.join(', ')}`);
}
}
// Start the server
async function main(): Promise<void> {
try {
const mcpServer = new HelloWorldMCPServer();
const mcpServer = new OneUptimeMCPServer();
await mcpServer.run();
} catch (error) {
logger.error("Failed to start MCP server:", error);
logger.error(`Failed to start MCP server: ${error}`);
process.exit(1);
}
}
@@ -185,6 +218,6 @@ process.on("SIGTERM", () => {
// Start the server
main().catch((error) => {
logger.error("Unhandled error:", error);
logger.error(`Unhandled error: ${error}`);
process.exit(1);
});

View File

@@ -1,92 +1,171 @@
# OneUptime Hello World MCP Server
# OneUptime MCP Server
A basic Hello World implementation of a Model Context Protocol (MCP) server for OneUptime.
A production-ready Model Context Protocol (MCP) server for OneUptime that provides dynamic tools for all OneUptime models and operations.
## What is this?
## Features
This is a simple MCP server that demonstrates how to create a Model Context Protocol server within the OneUptime ecosystem. It provides basic tools that can be used by AI assistants like Claude to interact with the server.
- **Dynamic Tool Generation**: Automatically generates MCP tools for all OneUptime models (Database and Analytics)
- **Full CRUD Operations**: Supports Create, Read, Update, Delete, List, and Count operations
- **Production Ready**: Built with proper error handling, logging, and configuration
- **Extensible**: Automatically supports new models as they are added to OneUptime
- **Type Safe**: Fully typed with TypeScript
## Available Tools
## Available Operations
1. **hello** - Say hello with a personalized greeting
- Parameters: `name` (string, required)
- Example: Returns "Hello, [name]! Welcome to OneUptime's Hello World MCP Server! 🚀"
The MCP server automatically generates tools for each OneUptime model with the following operations:
2. **get_time** - Get the current server time
- Parameters: None
- Example: Returns current ISO timestamp
### Database Models
- `oneuptime_create{ModelName}` - Create a new record
- `oneuptime_get{ModelName}` - Retrieve a record by ID
- `oneuptime_list{ModelName}s` - List records with filtering
- `oneuptime_update{ModelName}` - Update a record
- `oneuptime_delete{ModelName}` - Delete a record
- `oneuptime_count{ModelName}s` - Count records
3. **echo** - Echo back any message
- Parameters: `message` (string, required)
- Example: Returns "Echo: [your message]"
### Analytics Models
- `oneuptime_create{ModelName}` - Create analytics data
- `oneuptime_list{ModelName}s` - Query analytics data
- `oneuptime_count{ModelName}s` - Count analytics records
## Development
## Supported Models
### Prerequisites
The server automatically generates tools for all OneUptime models including:
- Node.js 18+
- npm or yarn
- TypeScript
**Database Models**: Incident, Alert, Monitor, Project, User, Team, StatusPage, and 100+ more
**Analytics Models**: Log, Metric, Span, TelemetryAttribute, ExceptionInstance, MonitorLog
### Setup
## Configuration
1. Install dependencies:
```bash
npm install
```
2. Start development server:
```bash
npm run dev
```
3. Start production server:
```bash
npm start
```
### Docker
Build and run with Docker:
Copy `.env.example` to `.env` and configure:
```bash
# Build the Docker image
docker build -f Dockerfile.tpl -t oneuptime-mcp-hello-world .
# OneUptime Instance Configuration
ONEUPTIME_HOSTNAME=localhost:3002
ONEUPTIME_PROTOCOL=http
ONEUPTIME_BASE_ROUTE=/api/v1
# Run the container
docker run -it oneuptime-mcp-hello-world
# Authentication (Required for production)
ONEUPTIME_API_KEY=your_oneuptime_api_key_here
ONEUPTIME_PROJECT_ID=your_project_id_here
# Server Configuration
NODE_ENV=development
LOG_LEVEL=info
```
## Installation
1. Install dependencies:
```bash
npm install
```
2. Configure environment variables:
```bash
cp .env.example .env
# Edit .env with your OneUptime configuration
```
3. Build the server:
```bash
npm run build
```
## Usage
This MCP server communicates over stdio and is designed to be used with MCP-compatible clients like Claude Desktop or other AI assistants that support the Model Context Protocol.
### Development
```bash
npm run dev
```
### Example Configuration for Claude Desktop
### Production
```bash
npm start
```
Add this to your Claude Desktop MCP settings:
## Example Tool Usage
### List Incidents
```json
{
"mcpServers": {
"oneuptime": {
"command": "node",
"args": ["--require", "ts-node/register", "/path/to/mcp-hello-world/Index.ts"]
"name": "oneuptime_listIncidents",
"arguments": {
"query": {"projectId": "your-project-id"},
"limit": 10
}
}
```
### Create Alert
```json
{
"name": "oneuptime_createAlert",
"arguments": {
"data": {
"title": "High CPU Usage",
"description": "CPU usage above 90%",
"projectId": "your-project-id"
}
}
}
```
### Get Monitor by ID
```json
{
"name": "oneuptime_getMonitor",
"arguments": {
"id": "monitor-id-here"
}
}
```
### Query Logs
```json
{
"name": "oneuptime_listLogs",
"arguments": {
"query": {
"serviceId": "service-id",
"severity": "error"
},
"limit": 50,
"sort": {"time": -1}
}
}
```
## Architecture
The server is built using:
- **@modelcontextprotocol/sdk**: Official MCP SDK for TypeScript
- **OneUptime Common**: Shared utilities and logging from OneUptime
- **TypeScript**: For type safety and better development experience
- **DynamicToolGenerator**: Automatically discovers and generates tools for all OneUptime models
- **OneUptimeApiService**: Handles API communication with OneUptime instance
- **Type Definitions**: Provides type safety and IntelliSense support
- **Error Handling**: Comprehensive error handling and user-friendly messages
## Contributing
## Development
This is part of the OneUptime project. Follow the standard OneUptime development practices and coding standards.
### Adding New Models
New models are automatically supported! When new models are added to OneUptime:
1. Database models added to `Common/Models/DatabaseModels/Index.ts`
2. Analytics models added to `Common/Models/AnalyticsModels/Index.ts`
The MCP server will automatically generate tools for them on the next restart.
### Testing
```bash
npm test
```
### Linting
```bash
npm run audit
npm run dep-check
```
## License
Apache-2.0 - see the OneUptime project license for details.
Apache-2.0 - See LICENSE file for details.

View File

@@ -0,0 +1,186 @@
import OneUptimeOperation from "../Types/OneUptimeOperation";
import ModelType from "../Types/ModelType";
import { OneUptimeToolCallArgs } from "../Types/McpTypes";
import Logger from "Common/Server/Utils/Logger";
import API from "Common/Utils/API";
import URL from "Common/Types/API/URL";
import Protocol from "Common/Types/API/Protocol";
import Hostname from "Common/Types/API/Hostname";
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";
export interface OneUptimeApiConfig {
hostname: string;
protocol?: Protocol;
apiKey?: string;
projectId?: string;
baseRoute?: Route;
}
export default class OneUptimeApiService {
private static api: API;
private static config: OneUptimeApiConfig;
public static initialize(config: OneUptimeApiConfig): void {
this.config = config;
const protocol = config.protocol || Protocol.HTTPS;
const hostname = new Hostname(config.hostname);
const baseRoute = config.baseRoute || new Route("/api/v1");
this.api = new API(protocol, hostname, baseRoute);
Logger.info(`OneUptime API Service initialized with: ${protocol}://${hostname}${baseRoute}`);
}
/**
* Execute a OneUptime API operation
*/
public static async executeOperation(
modelName: string,
operation: OneUptimeOperation,
modelType: ModelType,
apiPath: string,
args: OneUptimeToolCallArgs
): Promise<any> {
if (!this.api) {
throw new Error("OneUptime API Service not initialized. Please call initialize() first.");
}
this.validateOperationArgs(operation, args);
const route = this.buildApiRoute(apiPath, operation, args.id);
const headers = this.getHeaders();
const data = this.getRequestData(operation, args);
Logger.info(`Executing ${operation} operation for ${modelName} at ${route.toString()}`);
try {
let response: HTTPResponse<any> | HTTPErrorResponse;
switch (operation) {
case OneUptimeOperation.Create:
response = await this.api.post(route, data, headers);
break;
case OneUptimeOperation.Read:
case OneUptimeOperation.List:
case OneUptimeOperation.Count:
response = await this.api.get(route, data, headers);
break;
case OneUptimeOperation.Update:
response = await this.api.put(route, data, headers);
break;
case OneUptimeOperation.Delete:
response = await this.api.delete(route, data, headers);
break;
default:
throw new Error(`Unsupported operation: ${operation}`);
}
if (response instanceof HTTPErrorResponse) {
throw new Error(`API request failed: ${response.statusCode} - ${response.message}`);
}
Logger.info(`Successfully executed ${operation} operation for ${modelName}`);
return response.data;
} catch (error) {
Logger.error(`Error executing ${operation} operation for ${modelName}: ${error}`);
throw error;
}
}
private static buildApiRoute(apiPath: string, operation: OneUptimeOperation, id?: string): Route {
let route = new Route(apiPath);
switch (operation) {
case OneUptimeOperation.Read:
case OneUptimeOperation.Update:
case OneUptimeOperation.Delete:
if (id) {
route = route.addRoute(new Route(`/${id}`));
}
break;
case OneUptimeOperation.Count:
route = route.addRoute(new Route('/count'));
break;
case OneUptimeOperation.List:
case OneUptimeOperation.Create:
default:
// No additional path needed
break;
}
return route;
}
private static getRequestData(operation: OneUptimeOperation, args: OneUptimeToolCallArgs): JSONObject | undefined {
switch (operation) {
case OneUptimeOperation.Create:
return args.data;
case OneUptimeOperation.Update:
return args.data;
case OneUptimeOperation.List:
case OneUptimeOperation.Count:
return {
query: args.query || {},
select: args.select,
skip: args.skip,
limit: args.limit,
sort: args.sort,
} as JSONObject;
case OneUptimeOperation.Read:
case OneUptimeOperation.Delete:
default:
return undefined;
}
}
private static getHeaders(): Headers {
const headers: Headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
if (this.config.apiKey) {
headers['Authorization'] = `Bearer ${this.config.apiKey}`;
}
if (this.config.projectId) {
headers['X-Project-ID'] = this.config.projectId;
}
return headers;
}
/**
* Validate arguments for a specific operation
*/
public static validateOperationArgs(operation: OneUptimeOperation, args: OneUptimeToolCallArgs): void {
switch (operation) {
case OneUptimeOperation.Create:
if (!args.data) {
throw new Error('Data is required for create operation');
}
break;
case OneUptimeOperation.Read:
case OneUptimeOperation.Update:
case OneUptimeOperation.Delete:
if (!args.id) {
throw new Error(`ID is required for ${operation} operation`);
}
if (operation === OneUptimeOperation.Update && !args.data) {
throw new Error('Data is required for update operation');
}
break;
case OneUptimeOperation.List:
case OneUptimeOperation.Count:
// No required arguments for list/count operations
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
}
}

36
MCP/Types/McpTypes.ts Normal file
View File

@@ -0,0 +1,36 @@
import OneUptimeOperation from "./OneUptimeOperation";
import ModelType from "./ModelType";
export interface McpToolInfo {
name: string;
description: string;
inputSchema: any;
modelName: string;
operation: OneUptimeOperation;
modelType: ModelType;
singularName: string;
pluralName: string;
tableName: string;
apiPath?: string;
}
export interface ModelToolsResult {
tools: McpToolInfo[];
modelInfo: {
tableName: string;
singularName: string;
pluralName: string;
modelType: ModelType;
apiPath?: string;
};
}
export interface OneUptimeToolCallArgs {
id?: string;
data?: any;
query?: any;
select?: any;
skip?: number;
limit?: number;
sort?: any;
}

6
MCP/Types/ModelType.ts Normal file
View File

@@ -0,0 +1,6 @@
export enum ModelType {
Database = "database",
Analytics = "analytics",
}
export default ModelType;

View File

@@ -0,0 +1,10 @@
export enum OneUptimeOperation {
Create = "create",
Read = "read",
List = "list",
Update = "update",
Delete = "delete",
Count = "count",
}
export default OneUptimeOperation;

View File

@@ -0,0 +1,364 @@
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 OneUptimeOperation from "../Types/OneUptimeOperation";
import ModelType from "../Types/ModelType";
import { McpToolInfo, ModelToolsResult } from "../Types/McpTypes";
import Logger from "Common/Server/Utils/Logger";
export default class DynamicToolGenerator {
/**
* Generate all MCP tools for all OneUptime models
*/
public static generateAllTools(): McpToolInfo[] {
const allTools: McpToolInfo[] = [];
// Generate tools for Database Models
for (const ModelClass of DatabaseModels) {
try {
const model: DatabaseBaseModel = new ModelClass();
const tools = this.generateToolsForDatabaseModel(model, ModelClass);
allTools.push(...tools.tools);
} catch (error) {
Logger.error(`Error generating tools for database model ${ModelClass.name}: ${error}`);
}
}
// Generate tools for Analytics Models
for (const ModelClass of AnalyticsModels) {
try {
const model: AnalyticsBaseModel = new ModelClass();
const tools = this.generateToolsForAnalyticsModel(model, ModelClass);
allTools.push(...tools.tools);
} catch (error) {
Logger.error(`Error generating tools for analytics model ${ModelClass.name}: ${error}`);
}
}
Logger.info(`Generated ${allTools.length} MCP tools for OneUptime models`);
return allTools;
}
/**
* Generate MCP tools for a specific database model
*/
public static generateToolsForDatabaseModel(
model: DatabaseBaseModel,
ModelClass: { new (): DatabaseBaseModel }
): ModelToolsResult {
const tools: McpToolInfo[] = [];
const modelName = model.tableName || ModelClass.name;
const singularName = model.singularName || modelName;
const pluralName = model.pluralName || `${singularName}s`;
const apiPath = model.crudApiPath?.toString();
// Skip if model doesn't have required properties or documentation is disabled
if (!modelName || !model.enableDocumentation || !apiPath) {
return {
tools: [],
modelInfo: {
tableName: modelName,
singularName,
pluralName,
modelType: ModelType.Database,
apiPath
}
};
}
const baseToolName = singularName.toLowerCase().replace(/\s+/g, '_');
// CREATE Tool
tools.push({
name: `oneuptime_create${singularName.replace(/\s+/g, '')}`,
description: `Create a new ${singularName} in OneUptime`,
inputSchema: {
type: "object",
properties: {
data: {
type: "object",
description: `${singularName} data to create`,
}
},
required: ["data"]
},
modelName,
operation: OneUptimeOperation.Create,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// READ Tool
tools.push({
name: `oneuptime_get${singularName.replace(/\s+/g, '')}`,
description: `Retrieve a single ${singularName} by ID from OneUptime`,
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: `ID of the ${singularName} to retrieve`,
}
},
required: ["id"]
},
modelName,
operation: OneUptimeOperation.Read,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// LIST Tool
tools.push({
name: `oneuptime_list${pluralName.replace(/\s+/g, '')}`,
description: `List all ${pluralName} from OneUptime`,
inputSchema: {
type: "object",
properties: {
query: {
type: "object",
description: `Query filters for ${pluralName}`,
},
select: {
type: "object",
description: "Fields to select",
},
skip: {
type: "number",
description: "Number of records to skip",
},
limit: {
type: "number",
description: "Maximum number of records to return",
},
sort: {
type: "object",
description: "Sort order",
}
}
},
modelName,
operation: OneUptimeOperation.List,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// UPDATE Tool
tools.push({
name: `oneuptime_update${singularName.replace(/\s+/g, '')}`,
description: `Update an existing ${singularName} in OneUptime`,
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: `ID of the ${singularName} to update`,
},
data: {
type: "object",
description: `Updated ${singularName} data`,
}
},
required: ["id", "data"]
},
modelName,
operation: OneUptimeOperation.Update,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// DELETE Tool
tools.push({
name: `oneuptime_delete${singularName.replace(/\s+/g, '')}`,
description: `Delete a ${singularName} from OneUptime`,
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: `ID of the ${singularName} to delete`,
}
},
required: ["id"]
},
modelName,
operation: OneUptimeOperation.Delete,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// COUNT Tool
tools.push({
name: `oneuptime_count${pluralName.replace(/\s+/g, '')}`,
description: `Count the number of ${pluralName} in OneUptime`,
inputSchema: {
type: "object",
properties: {
query: {
type: "object",
description: `Query filters for counting ${pluralName}`,
}
}
},
modelName,
operation: OneUptimeOperation.Count,
modelType: ModelType.Database,
singularName,
pluralName,
tableName: modelName,
apiPath
});
return {
tools,
modelInfo: {
tableName: modelName,
singularName,
pluralName,
modelType: ModelType.Database,
apiPath
}
};
}
/**
* Generate MCP tools for a specific analytics model
*/
public static generateToolsForAnalyticsModel(
model: AnalyticsBaseModel,
ModelClass: { new (): AnalyticsBaseModel }
): ModelToolsResult {
const tools: McpToolInfo[] = [];
const modelName = model.tableName || ModelClass.name;
const singularName = model.singularName || modelName;
const pluralName = model.pluralName || `${singularName}s`;
const apiPath = model.crudApiPath?.toString();
// Skip if model doesn't have required properties
if (!modelName || !apiPath) {
return {
tools: [],
modelInfo: {
tableName: modelName,
singularName,
pluralName,
modelType: ModelType.Analytics,
apiPath
}
};
}
const baseToolName = singularName.toLowerCase().replace(/\s+/g, '_');
// CREATE Tool for Analytics
tools.push({
name: `oneuptime_create${singularName.replace(/\s+/g, '')}`,
description: `Create a new ${singularName} analytics record in OneUptime`,
inputSchema: {
type: "object",
properties: {
data: {
type: "object",
description: `${singularName} analytics data to create`,
}
},
required: ["data"]
},
modelName,
operation: OneUptimeOperation.Create,
modelType: ModelType.Analytics,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// LIST Tool for Analytics (most common operation)
tools.push({
name: `oneuptime_list${pluralName.replace(/\s+/g, '')}`,
description: `Query ${pluralName} analytics data from OneUptime`,
inputSchema: {
type: "object",
properties: {
query: {
type: "object",
description: `Query filters for ${pluralName} analytics data`,
},
select: {
type: "object",
description: "Fields to select",
},
skip: {
type: "number",
description: "Number of records to skip",
},
limit: {
type: "number",
description: "Maximum number of records to return",
},
sort: {
type: "object",
description: "Sort order",
}
}
},
modelName,
operation: OneUptimeOperation.List,
modelType: ModelType.Analytics,
singularName,
pluralName,
tableName: modelName,
apiPath
});
// COUNT Tool for Analytics
tools.push({
name: `oneuptime_count${pluralName.replace(/\s+/g, '')}`,
description: `Count ${pluralName} analytics records in OneUptime`,
inputSchema: {
type: "object",
properties: {
query: {
type: "object",
description: `Query filters for counting ${pluralName} analytics data`,
}
}
},
modelName,
operation: OneUptimeOperation.Count,
modelType: ModelType.Analytics,
singularName,
pluralName,
tableName: modelName,
apiPath
});
return {
tools,
modelInfo: {
tableName: modelName,
singularName,
pluralName,
modelType: ModelType.Analytics,
apiPath
}
};
}
}

16994
MCP/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@
"version": "1.0.0",
"description": "OneUptime MCP Server",
"main": "Index.ts",
"type": "module",
"bin": {
"oneuptime-mcp-server": "./build/Index.js"
},
@@ -23,11 +22,13 @@
"Common": "file:../Common",
"@modelcontextprotocol/sdk": "^0.6.0",
"ts-node": "^10.9.1",
"dotenv": "^16.4.5"
"dotenv": "^16.4.5",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"@types/jest": "^27.5.0",
"@types/node": "^17.0.31",
"@types/node-fetch": "^2.6.4",
"jest": "^28.1.0",
"nodemon": "^2.0.20",
"ts-jest": "^28.0.2",

View File

@@ -2,15 +2,14 @@
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["es6"],
"lib": ["es6", "es2017", "es2020"],
"allowJs": true,
"outDir": "./build",
"rootDir": "./",
"strict": true,
"moduleResolution": "node",
"baseUrl": "./",
"baseUrl": "../",
"paths": {
"Common/*": ["../Common/*"]
"Common/*": ["Common/*"]
},
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
@@ -21,7 +20,19 @@
"resolveJsonModule": true
},
"include": [
"**/*.ts"
"**/*.ts",
"../Common/Models/**/*.ts",
"../Common/Types/API/**/*.ts",
"../Common/Types/Database/**/*.ts",
"../Common/Types/AnalyticsDatabase/**/*.ts",
"../Common/Types/JSON.ts",
"../Common/Types/ObjectID.ts",
"../Common/Types/Permission.ts",
"../Common/Types/Dictionary.ts",
"../Common/Types/Sleep.ts",
"../Common/Types/Exception/**/*.ts",
"../Common/Server/Utils/Logger.ts",
"../Common/Utils/API.ts"
],
"exclude": [
"node_modules",