Skip to content

Latest commit

 

History

History
467 lines (364 loc) · 13.2 KB

File metadata and controls

467 lines (364 loc) · 13.2 KB

Project specification

Definitions

  • MCP: Model Context Protocol, a protocol for LLMs to call tools for performing specific actions.

Goal

Create a simple MCP server in Go.

Requirements

  1. The codes serves for learning and demo purposes, so keep it as simple and concise as possible.
  2. The MCP server shall communicate via http.
  3. The MCP server shall provide a method "moonphase" that calculates the phase of the moon based on a received date and time, or if the date and time is empty, based on the current date and time.
  4. The MCP server shall answer a tool call with two values: The moon age and the illumination percentage (as an integer between 0 and 100).
  5. The HTTP endpoints shall be secured by an API key. The API key shall be read from the environment.

Steps

  1. Write a function that takes a time.Time value and calculates the phase of the moon for this time. The function returns the moon age and the illumination percentage between 0 and 100.
  2. Write an HTTP server that listens on port 8181.
  3. Write HTTP handlers that handle the MCP protocol's methods: tools/list and tools/call. Require an API key as X-Api-Token header.
  4. Write a handler to manage shutdown if the MCP client requests it.
  5. Create a tool definition for the MCP server in crush.json.
  6. Write unit tests for any unit-testable function.

About the MCP server

General

The MCP server shall communicate over HTTP. It shall listen on the port 8181.

A handler for the route /mcp handles all requests. The server shall respond with a 404 to equests to all other routes.

Messages

All messages between MCP clients and servers MUST follow the JSON-RPC 2.0 specification. The protocol defines these types of messages: ​

Requests

Requests are sent from the client to the server or vice versa, to initiate an operation.

The JSON-RPC schema:

{
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: {
    [key: string]: unknown;
  };
}

Requests MUST include a string or integer ID. Unlike base JSON-RPC, the ID MUST NOT be null. The request ID MUST NOT have been previously used by the requestor within the same session.

The handler must implement the tools/list and tools/call methods.

For tools/list, it must return the available tools

Responses

Responses are sent in reply to requests, containing the result or error of the operation.

{
  jsonrpc: "2.0";
  id: string | number;
  result?: {
    [key: string]: unknown;
  }
  error?: {
    code: number;
    message: string;
    data?: unknown;
  }
}

Responses MUST include the same ID as the request they correspond to. Responses are further sub-categorized as either successful results or errors. Either a result or an error MUST be set. A response MUST NOT set both. Results MAY follow any JSON object structure, while errors MUST include an error code and message at minimum. Error codes MUST be integers.

Tools - Model Context Protocol

Listing Tools

To discover available tools, clients send a tools/list request. This operation supports pagination.

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "cursor": "optional-cursor-value"
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "title": "Weather Information Provider",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name or zip code"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}

Calling Tools

To invoke a tool, clients send a tools/call request:

Request:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    }
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
      }
    ],
    "isError": false
  }
}

Lifecycle

Protocol Revision: 2025-06-18

The Model Context Protocol (MCP) defines a rigorous lifecycle for client-server connections that ensures proper capability negotiation and state management.

  1. Initialization: Capability negotiation and protocol version agreement
  2. Operation: Normal protocol communication
  3. Shutdown: Graceful termination of the connection
sequenceDiagram
    participant Client
    participant Server

    Note over Client,Server: Initialization Phase
    activate Client
    Client->>+Server: initialize request
    Server-->>Client: initialize response
    Client--)Server: initialized notification

    Note over Client,Server: Operation Phase
    rect rgb(200, 220, 250)
        note over Client,Server: Normal protocol operations
    end

    Note over Client,Server: Shutdown
    Client--)-Server: Disconnect
    deactivate Server
    Note over Client,Server: Connection closed
Loading

Lifecycle Phases

Initialization

The initialization phase MUST be the first interaction between client and server. During this phase, the client and server:

  • Establish protocol version compatibility
  • Exchange and negotiate capabilities
  • Share implementation details

The client MUST initiate this phase by sending an initialize request containing:

  • Protocol version supported
  • Client capabilities
  • Client implementation information
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {},
      "elicitation": {}
    },
    "clientInfo": {
      "name": "ExampleClient",
      "title": "Example Client Display Name",
      "version": "1.0.0"
    }
  }
}

The server MUST respond with its own capabilities and information:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "logging": {},
      "prompts": {
        "listChanged": true
      },
      "resources": {
        "subscribe": true,
        "listChanged": true
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "ExampleServer",
      "title": "Example Server Display Name",
      "version": "1.0.0"
    },
    "instructions": "Optional instructions for the client"
  }
}

After successful initialization, the client MUST send an initialized notification to indicate it is ready to begin normal operations:

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}
  • The client SHOULD NOT send requests other than pings before the server has responded to the initialize request.
  • The server SHOULD NOT send requests other than pings and logging before receiving the initialized notification.

Version Negotiation

In the initialize request, the client MUST send a protocol version it supports. This SHOULD be the latest version supported by the client.

If the server supports the requested protocol version, it MUST respond with the same version. Otherwise, the server MUST respond with another protocol version it supports. This SHOULD be the latest version supported by the server.

If the client does not support the version in the server's response, it SHOULD disconnect.

If using HTTP, the client **MUST** include the `MCP-Protocol-Version: ` HTTP header on all subsequent requests to the MCP server. For details, see [the Protocol Version Header section in Transports](/specification/2025-06-18/basic/transports#protocol-version-header).

Capability Negotiation

Client and server capabilities establish which optional protocol features will be available during the session.

Key capabilities include:

Category Capability Description
Client roots Ability to provide filesystem roots
Client sampling Support for LLM sampling requests
Client elicitation Support for server elicitation requests
Client experimental Describes support for non-standard experimental features
Server prompts Offers prompt templates
Server resources Provides readable resources
Server tools Exposes callable tools
Server logging Emits structured log messages
Server completions Supports argument autocompletion
Server experimental Describes support for non-standard experimental features

Capability objects can describe sub-capabilities like:

  • listChanged: Support for list change notifications (for prompts, resources, and tools)
  • subscribe: Support for subscribing to individual items' changes (resources only)

Operation

During the operation phase, the client and server exchange messages according to the negotiated capabilities.

Both parties MUST:

  • Respect the negotiated protocol version
  • Only use capabilities that were successfully negotiated

Shutdown

During the shutdown phase, one side (usually the client) cleanly terminates the protocol connection. No specific shutdown messages are defined—instead, the underlying transport mechanism should be used to signal connection termination:

stdio

For the stdio transport, the client SHOULD initiate shutdown by:

  1. First, closing the input stream to the child process (the server)
  2. Waiting for the server to exit, or sending SIGTERM if the server does not exit within a reasonable time
  3. Sending SIGKILL if the server does not exit within a reasonable time after SIGTERM

The server MAY initiate shutdown by closing its output stream to the client and exiting.

HTTP

For HTTP transports, shutdown is indicated by closing the associated HTTP connection(s).

Timeouts

Implementations SHOULD establish timeouts for all sent requests, to prevent hung connections and resource exhaustion. When the request has not received a success or error response within the timeout period, the sender SHOULD issue a cancellation notification for that request and stop waiting for a response.

SDKs and other middleware SHOULD allow these timeouts to be configured on a per-request basis.

Implementations MAY choose to reset the timeout clock when receiving a progress notification corresponding to the request, as this implies that work is actually happening. However, implementations SHOULD always enforce a maximum timeout, regardless of progress notifications, to limit the impact of a misbehaving client or server.

Error Handling

Implementations SHOULD be prepared to handle these error cases:

  • Protocol version mismatch
  • Failure to negotiate required capabilities
  • Request timeouts

Example initialization error:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Unsupported protocol version",
    "data": {
      "supported": ["2024-11-05"],
      "requested": "1.0.0"
    }
  }
}

Security

Requests must include an X-Api-Token header. The token is set via env var MOONPHASE_API_KEY.

MCP configuration files

Some MCP clients use MCP configuration files for adding MCP servers.

Crush MCP configuration file format

Example

{
  "$schema": "https://charm.land/crush.json",
  "mcp": {
    "filesystem": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/mcp-server.js"],
      "env": {
        "NODE_ENV": "production"
      }
    },
    "github": {
      "type": "http",
      "url": "https://example.com/mcp/",
      "headers": {
        "Authorization": "$(echo Bearer $EXAMPLE_MCP_TOKEN)"
      }
    },
    "streaming-service": {
      "type": "sse",
      "url": "https://example.com/mcp/sse",
      "headers": {
        "API-Key": "$(echo $API_KEY)"
      }
    }
  }
}