Skip to content

MCP Admon Server

Extensible MCP server that gives AI agents structured, rule-driven access to any domain of the Admon platform — starting with ticket management.

Overview

The mcp-admon-server is a Model Context Protocol (MCP) server that exposes platform operations as structured AI-callable tools. It lives in the mcp/ root folder as an independent TypeScript/Node.js package and is intentionally designed to grow: adding a new domain (e.g. shipments, billing, companies) only requires registering a new module — no changes to the transport, auth, or logging layers.

The first domain implemented is ticket management. Before this server existed, AI agents had to infer required fields, validations, and backend endpoints per ticket type — leading to inconsistencies. This server centralizes all that logic: business rules are loaded dynamically from the database, validations run before any backend call, and every tool invocation is logged to mcp_activity_log.

The server supports two transports: HTTP Streamable (for Cursor) and Stdio (for Claude Desktop), selected via the MCP_TRANSPORT environment variable.

Key Concepts

ConceptDescription
MCP ToolA function the AI agent can invoke, with a typed input schema and a description. Analogous to a REST endpoint but for agents.
MCP ResourceRead-only catalog data the agent can fetch (e.g. ticket statuses, shipment statuses).
ModuleA self-contained domain registered into the server (e.g. tickets). Each module lives under src/modules/<domain>/ and exposes its own tools, resources, caches, and business rules. Adding a new module only requires a registerXxxModule(server, client, ...) call in server.ts.
Shared layer (src/shared/)Cross-cutting infrastructure available to all modules: BackendClient, ActivityLogger, ShipmentStatusesCache, shared constants, and base TypeScript types.
TicketRulesCacheIn-memory cache loaded from GET /catalog/ticket-types. Holds the full rules JSON per ticket type including mcp_context, conditions, inputs, and files. TTL: 12 hours.
McpContextJSON sub-field inside catalog_ticket_types.rules. Single source of truth for MCP-specific ticket configuration: use_case, is_blocked, requires_guide, amount_limit_mxn, requires_credit_id, agent_notes. Defined once in the DB, consumed by both the cache and the tools.
BackendClientInternal HTTP client that injects Authorization: Bearer + X-Bot-AI-Signature (HMAC-SHA256) on every request to the Hapi.js backend.
ActivityLoggerFire-and-forget logger that POSTs each tool call result to POST /mcp/activity-log. Never blocks a tool response. Applies automatically to every tool via the withLogging wrapper in server.ts.

Architecture

Source tree

mcp/src/
├── index.ts                        # Entry point — selects transport (HTTP / Stdio)
├── server.ts                       # Wires modules, shared layer, and logging wrapper
├── shared/
│   ├── constants.ts                # CACHE_TTL_MS and other cross-module constants
│   ├── http/
│   │   ├── backend.client.ts       # Authenticated HTTP client (Bearer + HMAC-SHA256)
│   │   └── shipment-statuses.cache.ts
│   ├── logging/
│   │   └── activity-logger.ts      # Fire-and-forget POST /mcp/activity-log
│   ├── resources/
│   │   └── index.ts                # registerSharedResources (shipments://statuses)
│   └── types/
│       └── backend.types.ts        # Shared TypeScript response interfaces
└── modules/
    └── tickets/                    # First domain module
        ├── index.ts                # registerTicketsModule — wires all ticket tools
        ├── business-rules/
        │   └── ticket-statuses.constants.ts
        ├── http/
        │   └── ticket-rules.cache.ts   # TicketRulesCache + McpContext interface
        ├── resources/
        │   └── ticket-statuses.ts
        ├── schemas/
        │   └── ticket.schemas.ts
        └── tools/
            ├── get-ticket-types.ts
            ├── create-ticket.ts
            ├── update-ticket.ts
            ├── get-ticket.ts
            └── list-tickets.ts

Adding a new module

To expose a new platform domain to AI agents:

  1. Create src/modules/<domain>/index.ts that exports registerXxxModule(server, client, registerTool).
  2. Implement tools under src/modules/<domain>/tools/, schemas under schemas/, and any domain-specific caches under http/.
  3. Call registerXxxModule(...) in server.ts — that's the only file that needs to change outside the new module folder.

The logging wrapper, authentication, and transport are inherited automatically.

Data Flow

Generic tool call lifecycle


Tickets module — create_ticket workflow

Tickets module — amount validation (update_ticket to CLAIM_IN_REVIEW)

Tickets module — file upload (embedded in create/update)

Database

Current tables (tickets module)

TablePurpose
company_ticketsMain ticket records
company_ticket_variablesKey-value pairs per ticket (e.g. days, amount)
company_filesFile metadata (S3 URL, type, ticket reference)
catalog_ticket_typesTicket type definitions including rules JSON with mcp_context
catalog_ticket_statusesAvailable ticket statuses
catalog_shipment_statusesShipment statuses used to validate avaliable_status
mcp_activity_logAudit log of every MCP tool call across all modules (tool name, status, execution time)

Key Relationships

  • company_tickets.ticket_type_idcatalog_ticket_types.id
  • company_tickets.ticket_status_idcatalog_ticket_statuses.id
  • company_files.ticket_idcompany_tickets.id
  • mcp_activity_log.mcp_name identifies which MCP server wrote the entry

Key Decisions

DecisionReasoningAlternatives Considered
mcp_context in catalog_ticket_types.rules as single source of truthUse-case descriptions, blocked types, amount limits, and agent notes all live in the DB — no code change or redeploy needed to update themHardcoded TypeScript constants (TICKET_TYPE_USE_CASES, BLOCKED_TICKET_TYPE_IDS) — required code change + redeploy for every rule update
get_ticket_types tool (dual-mode) instead of passive tickets://types resourceAn active tool is reliably called by agents at the right step; a passive resource was often ignored, leading to wrong field collectionKeep tickets://types as a resource — agents didn't consistently consult it before creating tickets
Amount validation at CLAIM_IN_REVIEW transition (not at creation)Amount may be unknown at creation; validation only makes sense when the ticket is about to be reviewed for a claimValidate at creation — too early; amount may change or be absent during initial ticket creation
Block any ticket type without mcp_context by defaultMissing mcp_context means the DB has no guidance for agents — safer to reject than to guessAllow creation without mcp_context — would let agents proceed without knowing required fields
Dynamic rules via TicketRulesCache with 12-hour TTLRules in catalog_ticket_types.rules can change without redeploying the MCP; TTL avoids a backend call on every tool invocationNo cache (query on every call) — adds latency; or no TTL — stale rules if DB is updated
Single entry point (index.ts) for both HTTP and Stdio transportsOne binary to maintain; transport selected by MCP_TRANSPORT env varSeparate stdio.ts entry — duplicates startup logic
Fire-and-forget activity loggingA logging failure must never impact the tool response latency or resultAwaited logging — would add ~10–50ms per call and could propagate DB errors to agents
File uploads as base64 in tool inputAgent makes a single create_ticket call even when attaching filesSeparate upload_ticket_file tool — required agents to orchestrate 2 calls, caused confusion and duplicate uploads
check_ticket_by_tracking removed in favor of list_ticketslist_tickets with tracking_number filter returns the same data plus richer contextKeep as standalone tool — redundant given list_tickets capabilities
withLogging wrapper in server.ts instead of per-tool loggingCentralized; any new tool registered via registerTool gets logging automaticallyPer-tool try/catch with logActivity — boilerplate that could be forgotten in new tools

Dependencies

  • Internal: backend/routes/companies.routes.js (ticket CRUD), backend/routes/catalogs.routes.js (ticket types + shipment statuses), backend/routes/mcp.routes.js (activity log). New modules will add their own backend route dependencies.
  • External:
    • @modelcontextprotocol/sdk — MCP server and transport primitives
    • express@^5 — HTTP server for Streamable transport
    • zod@^4 — Input schema validation and type inference
    • Node.js ≥ 24.0.0 — Required for native fetch (no polyfills)
  • API Reference: ./api — Tools, resources, backend endpoints, and environment variables
  • User Guide: No UI — this module is used exclusively by AI agents

Envia Admin