[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build an MCP Server for Internal Tools

Build an MCP Server for Internal Tools

Build a Model Context Protocol server that exposes internal tools (database queries, deployments, incident management) to AI assistants — letting developers interact with infrastructure through natural language.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 4 skills

Avg quality 95/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Ava leads platform engineering at a 50-person company. Developers constantly ask DevOps for help: "What's the status of the staging deployment?", "Can you restart the payment service?", "Show me the error logs from the last hour." DevOps answers the same questions 30 times a day via Slack. They want AI assistants (Claude, Cursor, internal chatbots) to answer these questions directly by connecting to internal systems through MCP — a standardized protocol that lets AI models call tools safely.

Step 1: Build the MCP Server

typescript
// src/mcp/server.ts — MCP server exposing internal tools to AI assistants
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { pool } from "../db";
import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

const server = new Server(
  { name: "platform-tools", version: "1.0.0" },
  { capabilities: { tools: {}, resources: {} } }
);

// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_service_status",
      description: "Get the current status of a microservice including health, version, replicas, and recent errors",
      inputSchema: {
        type: "object",
        properties: {
          service: { type: "string", description: "Service name (e.g., 'api-gateway', 'payment-service')" },
        },
        required: ["service"],
      },
    },
    {
      name: "query_logs",
      description: "Search application logs by service, level, and time range",
      inputSchema: {
        type: "object",
        properties: {
          service: { type: "string", description: "Service name" },
          level: { type: "string", enum: ["error", "warn", "info"], description: "Log level filter" },
          minutes: { type: "number", description: "Look back this many minutes (default 60)", default: 60 },
          query: { type: "string", description: "Text search within log messages" },
        },
        required: ["service"],
      },
    },
    {
      name: "restart_service",
      description: "Restart a service (requires confirmation). Triggers a rolling restart with zero downtime.",
      inputSchema: {
        type: "object",
        properties: {
          service: { type: "string", description: "Service to restart" },
          reason: { type: "string", description: "Reason for restart (logged for audit)" },
        },
        required: ["service", "reason"],
      },
    },
    {
      name: "run_database_query",
      description: "Run a READ-ONLY SQL query against the analytics database. No mutations allowed.",
      inputSchema: {
        type: "object",
        properties: {
          query: { type: "string", description: "SQL SELECT query" },
          database: { type: "string", enum: ["analytics", "reporting"], default: "analytics" },
        },
        required: ["query"],
      },
    },
    {
      name: "get_deployment_status",
      description: "Get the status of recent deployments for a service",
      inputSchema: {
        type: "object",
        properties: {
          service: { type: "string", description: "Service name" },
          limit: { type: "number", description: "Number of recent deployments", default: 5 },
        },
        required: ["service"],
      },
    },
    {
      name: "create_incident",
      description: "Create an incident in the incident management system",
      inputSchema: {
        type: "object",
        properties: {
          title: { type: "string", description: "Incident title" },
          severity: { type: "string", enum: ["critical", "high", "medium", "low"] },
          service: { type: "string", description: "Affected service" },
          description: { type: "string", description: "Detailed description" },
        },
        required: ["title", "severity", "service"],
      },
    },
  ],
}));

// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "get_service_status": {
      const service = args?.service as string;
      const status = await getServiceStatus(service);
      return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
    }

    case "query_logs": {
      const { service, level, minutes = 60, query } = args as any;

      // Validate: read-only, bounded time range
      const maxMinutes = Math.min(minutes, 1440); // max 24h

      const { rows } = await pool.query(
        `SELECT timestamp, level, message, metadata
         FROM logs
         WHERE service = $1
           AND ($2::text IS NULL OR level = $2)
           AND timestamp > NOW() - INTERVAL '${maxMinutes} minutes'
           AND ($3::text IS NULL OR message ILIKE '%' || $3 || '%')
         ORDER BY timestamp DESC
         LIMIT 50`,
        [service, level || null, query || null]
      );

      return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
    }

    case "restart_service": {
      const { service, reason } = args as any;

      // Audit log
      await pool.query(
        "INSERT INTO audit_log (action, target, reason, actor, created_at) VALUES ('restart', $1, $2, 'mcp-server', NOW())",
        [service, reason]
      );

      // Trigger rolling restart via K8s API
      const result = await triggerRestart(service);
      return { content: [{ type: "text", text: `Restart initiated for ${service}. ${result}` }] };
    }

    case "run_database_query": {
      const sql = (args?.query as string).trim();

      // Safety: only SELECT queries
      if (!/^SELECT\s/i.test(sql)) {
        return { content: [{ type: "text", text: "Error: Only SELECT queries are allowed." }], isError: true };
      }

      // Disallow dangerous patterns
      if (/\b(DROP|DELETE|UPDATE|INSERT|ALTER|TRUNCATE|CREATE)\b/i.test(sql)) {
        return { content: [{ type: "text", text: "Error: Mutation queries are not allowed." }], isError: true };
      }

      const { rows } = await pool.query({ text: sql, rowMode: "array" });
      return { content: [{ type: "text", text: JSON.stringify(rows.slice(0, 100), null, 2) }] };
    }

    case "get_deployment_status": {
      const { service, limit = 5 } = args as any;
      const { rows } = await pool.query(
        `SELECT version, environment, status, deployed_by, started_at, completed_at, duration_seconds
         FROM deployments WHERE service = $1 ORDER BY started_at DESC LIMIT $2`,
        [service, limit]
      );
      return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
    }

    case "create_incident": {
      const { title, severity, service, description } = args as any;
      const { rows: [incident] } = await pool.query(
        `INSERT INTO incidents (title, severity, service, description, status, created_at)
         VALUES ($1, $2, $3, $4, 'open', NOW()) RETURNING id`,
        [title, severity, service, description || ""]
      );
      // Notify on-call via PagerDuty/Slack
      await redis.publish("incidents:new", JSON.stringify({ id: incident.id, title, severity, service }));
      return { content: [{ type: "text", text: `Incident #${incident.id} created (${severity}): ${title}` }] };
    }

    default:
      return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
  }
});

// Resources: expose dashboards and runbooks
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
  resources: [
    { uri: "platform://services", name: "All Services", description: "List of all microservices and their status" },
    { uri: "platform://runbooks", name: "Runbooks", description: "Incident response runbooks" },
  ],
}));

server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const { uri } = request.params;

  if (uri === "platform://services") {
    const { rows } = await pool.query("SELECT name, version, status, replicas FROM services ORDER BY name");
    return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(rows, null, 2) }] };
  }

  if (uri === "platform://runbooks") {
    const { rows } = await pool.query("SELECT title, service, steps FROM runbooks ORDER BY service");
    return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(rows, null, 2) }] };
  }

  throw new Error(`Unknown resource: ${uri}`);
});

async function getServiceStatus(service: string) {
  const { rows: [svc] } = await pool.query(
    "SELECT * FROM services WHERE name = $1", [service]
  );
  if (!svc) throw new Error(`Service not found: ${service}`);

  const { rows: errors } = await pool.query(
    "SELECT COUNT(*) as count FROM logs WHERE service = $1 AND level = 'error' AND timestamp > NOW() - INTERVAL '1 hour'",
    [service]
  );

  return { ...svc, recentErrors: parseInt(errors[0].count) };
}

async function triggerRestart(service: string): Promise<string> {
  // K8s rollout restart
  return `Rolling restart initiated. Pods will be replaced one at a time.`;
}

// Start server
const transport = new StdioServerTransport();
await server.connect(transport);

Results

  • DevOps Slack questions dropped 80% — developers ask their AI assistant "what's the error rate on payment-service?" instead of pinging DevOps; the MCP server queries logs directly
  • Incident creation: 5 minutes → 30 seconds — "create a high severity incident for checkout-service, payments are timing out" generates a properly formatted incident with PagerDuty notification
  • Read-only SQL queries are safe — the MCP server validates queries, blocks mutations, and limits results; developers explore analytics data through natural language
  • Every action is audited — restarts, queries, and incidents are logged with timestamps; the team knows exactly what the AI did and when
  • Works with any MCP client — Claude Desktop, Cursor, custom chatbots — any tool that speaks MCP can use these platform tools without custom integrations

Related use cases