Tiago Fortunato
ProjectsOdysMCP Server

MCP Server

MCP server overview: purpose, architecture, not deployed

MCP Server

The Model Context Protocol (MCP) server within the odys application serves as a crucial interface, exposing core business logic and data access as a set of well-defined tools. These tools are designed to be consumed by external AI models or other intelligent agents, allowing them to interact with and manage odys's operational data and functionalities programmatically. While the server is fully implemented, it is currently configured for local execution via standard I/O, rather than being deployed as a standalone service.

Overview

The odys-mcp-server is a dedicated component, residing in the mcp-server/ directory, built to adhere to the Model Context Protocol. Its primary purpose is to translate natural language requests from an AI model into structured calls to odys's internal functions, and then return the results in a format the AI can understand. This architecture enables intelligent automation and integration with AI-driven workflows for managing professionals, clients, and appointments.

The server leverages the @modelcontextprotocol/sdk to establish communication and handle requests. It defines a set of 4 distinct tools, each encapsulating a specific business operation: get_appointments_today, get_customer_by_phone, get_revenue_summary, and update_appointment_status. These tools are declared in mcp-server/src/tools.ts and exposed through the server's request handlers.

Communication with the MCP server is facilitated by a StdioServerTransport, as configured in mcp-server/src/server.ts. This means the server communicates over standard input/output streams, making it suitable for local development and integration with processes that can pipe data. The .mcp.json file explicitly defines how to run this server, specifying node ./mcp-server/dist/server.js as the command.

The odys application, at version 0.1.0, utilizes a robust Drizzle ORM for database interactions across its various tables, which include professionals, clients, appointments, and messages. The MCP server interacts directly with this database schema to fulfill tool requests.

Core Components

mcp-server/src/server.ts

This file acts as the entry point and orchestrator for the MCP server. It initializes a Server instance from the @modelcontextprotocol/sdk, identifying itself as "odys" with version "0.1.0" and declaring its tool capabilities.

The server sets up two primary request handlers:

  1. ListToolsRequestSchema: When an MCP client requests a list of available tools, this handler responds by providing the TOOL_DEFINITIONS array imported from mcp-server/src/tools.ts. This allows the AI model to dynamically discover the capabilities it can invoke.
  2. CallToolRequestSchema: This is the core handler for executing specific tools. It receives a request containing the name of the tool to call and its arguments. A switch statement dispatches the request to the corresponding asynchronous function (e.g., getAppointmentsToday, getCustomerByPhone) also imported from mcp-server/src/tools.ts. The results are then serialized into a JSON string and returned as content. Robust error handling is built into this handler, catching any exceptions during tool execution and returning a structured error message to the client, along with logging the error to console.error.

The main function in this file establishes the StdioServerTransport and connects the server, printing a confirmation message to console.error once it's running.

mcp-server/src/tools.ts

This file defines the actual business logic for each of the 4 MCP tools and their associated input schemas. It leverages zod for input validation and drizzle-orm for database operations. Each tool function also incorporates internal try/catch blocks, returning structured error objects (e.g., with an error field or success: false) upon failure in database operations or business logic.

  • GetAppointmentsTodayInput: This zod schema defines the expected input for get_appointments_today, requiring a professionalId (UUID string) and optionally a timezone (defaulting to "America/Sao_Paulo"). The getAppointmentsToday function calculates the start and end times for "today" in the specified timezone using a custom tzOffsetMs utility, then queries the appointments, clients, and professionals tables to retrieve relevant details.
  • GetCustomerByPhoneInput: For get_customer_by_phone, this schema expects a phone string. The getCustomerByPhone function normalizes the input phone number using normalizeBrazilianPhone and generates getPhoneVariants to ensure comprehensive matching against the clients table. It then fetches client details and their last 5 appointments from the appointments table.
  • GetRevenueSummaryInput: This schema requires a professionalId and a period (enum: "week", "month", "year"). The getRevenueSummary function calculates a rolling time window based on the period. It retrieves the sessionPrice from the professionals table and then counts appointments for that professional within the window where paymentStatus is 'captured'. It returns the total revenue, appointment count, and average ticket.
  • UpdateAppointmentStatusInput: This schema defines the input for update_appointment_status, requiring appointmentId, professionalId, and a status (from APPOINTMENT_STATUSES), with an optional reason. The updateAppointmentStatus function first verifies that the appointment exists and belongs to the specified professionalId to prevent unauthorized updates. It also checks if the status is already the target status, making the operation idempotent. If a change is needed, it updates the status and optionally the notes column in the appointments table.

The TOOL_DEFINITIONS array provides the metadata for each tool, including its name, description, and inputSchema in a format consumable by the MCP SDK. This array is what the ListToolsRequestSchema handler returns.

Design Decisions

The design of the odys-mcp-server reflects several deliberate choices aimed at clarity, robustness, and maintainability:

  • Clear Separation of Concerns: The architecture cleanly separates the Model Context Protocol handling (mcp-server/src/server.ts) from the actual business logic and database interactions (mcp-server/src/tools.ts). This makes the server's role as a protocol adapter explicit and keeps the tool implementations focused on their specific tasks.
  • Strong Input Validation with Zod: Each tool's input is rigorously validated using zod schemas (e.g., GetAppointmentsTodayInput, GetCustomerByPhoneInput). This ensures that incoming requests conform to expected data types and formats, preventing common errors and improving the reliability of the tool calls.
  • Type-Safe Database Interactions with Drizzle ORM: The use of drizzle-orm for all database queries provides strong type safety, reducing the likelihood of runtime errors related to schema mismatches or incorrect query construction. This is evident in how schema.appointments, schema.clients, and schema.professionals are used throughout mcp-server/src/tools.ts.
  • Centralized Error Handling: The CallToolRequestSchema handler in mcp-server/src/server.ts includes a try/catch block that wraps all tool invocations. This provides a consistent mechanism for handling errors, logging them, and returning a structured error response to the MCP client, which is crucial for debugging and AI model feedback.
  • Explicit Tool Definitions: The TOOL_DEFINITIONS array in mcp-server/src/tools.ts serves as a self-documenting API for the MCP client. By providing a name, description, and inputSchema for each tool, AI models can better understand how to use them without needing out-of-band information.

Potential Improvements

  1. Define Explicit Output Schemas for Tools: Currently, the CallToolRequestSchema handler in mcp-server/src/server.ts returns tool results by simply JSON.stringify(result, null, 2). While functional, this means the output structure is implicitly defined by the tool functions. Introducing explicit zod schemas for tool outputs, similar to how inputs are handled (e.g., GetAppointmentsTodayOutput), would provide clearer contracts for AI models, enable better type-checking, and improve the overall robustness of the MCP interface.
  2. Refine getRevenueSummary Calculation Logic: The getRevenueSummary function in mcp-server/src/tools.ts calculates totalRevenueCents as appointmentCount * professional.sessionPrice. This assumes a fixed sessionPrice for all appointments within the period. If sessionPrice can vary per appointment or if discounts are applied, this calculation might be inaccurate. A more robust approach would involve storing the actual price charged for each appointment in the appointments table and summing those values, rather than relying on the current sessionPrice from the professionals table.
  3. Enhance Timezone Handling: The tzOffsetMs function in mcp-server/src/tools.ts is a custom implementation for calculating timezone offsets. While it works, timezone logic can be complex, especially with daylight saving time changes and historical data. Replacing this custom utility with a well-tested, dedicated library (e.g., luxon or date-fns-tz) would likely improve accuracy, reduce potential bugs, and simplify maintenance for timezone-aware operations like getAppointmentsToday.

References

  • mcp-server/src/server.ts
  • mcp-server/src/tools.ts
  • mcp-server/package.json
  • .mcp.json

On this page