Skip to main content
Glama
oaslananka

MCP Health Monitor

Register MCP Server

register_server

Register an MCP server with configurable transport, alerts, and check interval to monitor its health and uptime.

Instructions

Register an MCP server to monitor. Supports http, sse, and stdio transports.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesUnique name for this MCP server
typeYesTransport type: http (Streamable HTTP), sse (legacy SSE), stdio
urlNoURL for http/sse servers (e.g. https://mcp-ssh-tool.onrender.com/mcp)
commandNoCommand for stdio servers (e.g. npx mcp-debug-recorder)
argsNoArgs for stdio command
tagsNoTags for grouping
alert_on_downNoAlert when server goes down
check_interval_minutesNo

Implementation Reference

  • Zod schema for register_server input validation: defines name, type (http/stdio/sse), url, command, args, tags, alert_on_down, and check_interval_minutes.
    export const RegisterServerSchema = z.object({
      name: z.string().min(1).max(100).describe('Unique name for this MCP server'),
      type: McpServerTypeSchema.describe(
        'Transport type: http (Streamable HTTP), sse (legacy SSE), stdio'
      ),
      url: z
        .string()
        .url()
        .optional()
        .describe('URL for http/sse servers (e.g. https://mcp-ssh-tool.onrender.com/mcp)'),
      command: z
        .string()
        .optional()
        .describe('Command for stdio servers (e.g. npx mcp-debug-recorder)'),
      args: z.array(z.string()).default([]).describe('Args for stdio command'),
      tags: z.array(z.string()).default([]).describe('Tags for grouping'),
      alert_on_down: z.boolean().default(true).describe('Alert when server goes down'),
      check_interval_minutes: z.number().int().min(1).max(60).default(5)
    });
  • src/app.ts:217-237 (registration)
    Tool registration: registers the 'register_server' tool with title, description, inputSchema (RegisterServerSchema), and handler invocation.
    server.registerTool(
      'register_server',
      {
        title: 'Register MCP Server',
        description: 'Register an MCP server to monitor. Supports http, sse, and stdio transports.',
        inputSchema: RegisterServerSchema,
        annotations: {
          readOnlyHint: false,
          destructiveHint: false,
          openWorldHint: false
        }
      },
      async (input: RegisterServerInput) => {
        const result = registerServer(input);
        return formatResponse({
          ...result,
          message: `${input.name} registered. Run check_server to verify connectivity.`
        });
      }
    );
  • Handler function: inserts/upserts server config into the database using SQLite (better-sqlite3), returns registered=true and the server name.
    export function registerServer(input: RegisterServerInput): { registered: true; name: string } {
      const db = getDb();
      const now = Date.now();
    
      db.prepare(
        `
          INSERT INTO servers (
            name,
            type,
            url,
            command,
            args,
            tags,
            alert_on_down,
            check_interval_minutes,
            created_at
          )
          VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
          ON CONFLICT(name) DO UPDATE SET
            type = excluded.type,
            url = excluded.url,
            command = excluded.command,
            args = excluded.args,
            tags = excluded.tags,
            alert_on_down = excluded.alert_on_down,
            check_interval_minutes = excluded.check_interval_minutes
        `
      ).run(
        input.name,
        input.type,
        input.url ?? null,
        input.command ?? null,
        JSON.stringify(input.args),
        JSON.stringify(input.tags),
        input.alert_on_down ? 1 : 0,
        input.check_interval_minutes,
        now
      );
    
      return { registered: true, name: input.name };
    }
  • Handler wrapper in app.ts: calls registerServer(input) and wraps result with a message via formatResponse.
    } from './types.js';
    
    type ToolResponse = {
      content: Array<{
        type: 'text';
        text: string;
      }>;
    };
    
    type ToolConfig = {
      title?: string;
      description?: string;
      inputSchema?: object;
      annotations?: {
        readOnlyHint?: boolean;
        destructiveHint?: boolean;
        openWorldHint?: boolean;
      };
    };
    
    type ToolRegistrar = {
      registerTool: (name: string, config: ToolConfig, handler: unknown) => unknown;
    };
    
    function formatResponse(payload: unknown): ToolResponse {
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(payload, null, 2)
          }
        ]
      };
    }
    
    function formatTextResponse(text: string): ToolResponse {
      return {
        content: [
          {
            type: 'text',
            text
          }
        ]
      };
    }
    
    function buildErrorResult(error: unknown): CheckResult {
      return {
        status: 'error',
        response_time_ms: null,
        tool_count: null,
        error_message: error instanceof Error ? error.message : 'Unknown error',
        tools: null
      };
    }
    
    function enrichWithAlerts(
      serverName: string,
      result: CheckResult,
      options: { hours?: number } = {}
    ): AlertEvaluation {
      return options.hours === undefined
        ? evaluateAlertState(serverName, result)
        : evaluateAlertState(serverName, result, { uptimeWindowHours: options.hours });
    }
    
    function getLatestDashboardResult(server: Pick<RegisteredServer, 'name'>): CheckResult | null {
      const latest = getLatestHealthCheck(server.name);
      if (!latest) {
        return null;
      }
    
      return {
        status: latest.status,
        response_time_ms: latest.response_time_ms,
        tool_count: latest.tool_count,
        error_message: latest.error_message,
        tools: latest.tools_snapshot ? (JSON.parse(latest.tools_snapshot) as string[]) : null
      };
    }
    
    function formatCurrentStatus(status: string): string {
      if (status === 'up') {
        return 'UP';
      }
    
      if (status === 'unknown') {
        return 'UNKNOWN';
      }
    
      return status.toUpperCase();
    }
    
    function formatMetric(value: number | null, suffix = ''): string {
      return value === null ? '--' : `${value}${suffix}`;
    }
    
    function formatMarkdownReport(input: GetReportInput): string {
      const report = getDashboardReport(input.hours);
      const lines = [
        '# MCP Health Report',
        '',
        `Generated: ${new Date().toISOString()}`,
        `Period: ${input.hours}h`,
        '',
        '| Server | Status | Uptime | Avg RT | P50 RT | P95 RT | Failures |',
        '| ------ | ------ | ------ | ------ | ------ | ------ | -------- |'
      ];
    
      for (const entry of report) {
        lines.push(
          `| ${entry.name} | ${formatCurrentStatus(entry.current_status)} | ${formatMetric(
            entry.uptime_percent,
            '%'
          )} | ${formatMetric(entry.avg_response_time_ms, 'ms')} | ${formatMetric(
            entry.p50_response_time_ms,
            'ms'
          )} | ${formatMetric(entry.p95_response_time_ms, 'ms')} | ${entry.consecutive_failures} |`
        );
      }
    
      if (report.length === 0) {
        lines.push('| -- | -- | -- | -- | -- | -- | -- |');
      }
    
      return lines.join('\n');
    }
    
    export function registerMonitoringTools(server: ToolRegistrar): void {
      server.registerTool(
        'register_azure_pipelines',
        {
          title: 'Register Azure DevOps Pipelines',
          description: 'Register Azure DevOps pipelines to monitor for CI, publish, and mirror status.',
          inputSchema: RegisterAzurePipelineSchema,
          annotations: {
            readOnlyHint: false,
            destructiveHint: false,
            openWorldHint: true
          }
        },
        async (input: RegisterAzurePipelineInput) => {
          const available = await listPipelines(input.organization, input.project, input.pat_token);
          const resolved: Array<{ name: string; id: number | null }> = input.pipeline_names.map(
            (name: string) => ({
              name,
              id: available.find((pipeline) => pipeline.name === name)?.id ?? null
            })
          );
    
          registerAzurePipelines(input, resolved);
    
          return formatResponse({
            registered: true,
            group: input.name,
            pipelines: resolved.map((pipeline) => pipeline.name),
            resolved_ids: resolved
          });
        }
      );
    
      server.registerTool(
        'register_server',
        {
          title: 'Register MCP Server',
          description: 'Register an MCP server to monitor. Supports http, sse, and stdio transports.',
          inputSchema: RegisterServerSchema,
          annotations: {
            readOnlyHint: false,
            destructiveHint: false,
            openWorldHint: false
          }
        },
        async (input: RegisterServerInput) => {
  • TypeScript type derived from the Zod schema for RegisterServerInput.
    export type RegisterServerInput = z.infer<typeof RegisterServerSchema>;
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate write-only and non-destructive behavior. Description adds transport support context but omits details like immediate monitoring start, authentication needs, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence that is front-loaded with the main action and resource, no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With 8 parameters, no output schema, and sibling management tools, the description is too minimal. It does not explain return value, error conditions, or how registration affects monitoring.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema descriptions cover 88% of parameters; the description adds value by explaining the 'type' parameter enum values (http, sse, stdio) and their meanings.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Register') and the resource ('an MCP server to monitor'), and distinguishes from sibling tools like list_servers and unregister_server by noting supported transports.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for registering new servers but lacks explicit guidance on prerequisites, when to use vs alternatives like register_azure_pipelines, or when not to use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

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

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oaslananka/mcp-health-monitor'

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