MCP Server Data Structures Documentation

View Source

This document describes all the data structures needed to support JSON encoding for the Model Context Protocol (MCP) server callbacks defined in McpServer behaviour.

Overview

The MCP server requires structured data for four main capabilities:

  1. Tools - Callable functions with input/output validation
  2. Prompts - Interactive message templates with argument completion
  3. Resources - Data sources with URI-based access
  4. Apps - Interactive UI rendering via sandboxed iframes (MCP Apps extension)

All structures must be JSON-encodable (using Jason library) and follow the MCP protocol specification.


1.1 Tool Definition Structure

Purpose: Represents a complete tool definition with metadata and schema
Used by: list_tools/1 callback
JSON Field: Top-level in tools list

%McpServer.Tool{
  name: String.t(),           # Unique tool identifier
  description: String.t(),    # Human-readable description
  inputSchema: map(),         # JSON Schema for input validation
  annotations: map()          # Optional metadata (hints, title)
}

JSON Example:

{
  "name": "calculator",
  "description": "Performs arithmetic operations",
  "inputSchema": {
    "type": "object",
    "properties": {
      "operation": {
        "type": "string",
        "description": "Operation to perform",
        "enum": ["add", "subtract", "multiply", "divide"]
      },
      "a": {
        "type": "number",
        "description": "First operand"
      },
      "b": {
        "type": "number",
        "description": "Second operand"
      }
    },
    "required": ["operation", "a", "b"]
  },
  "annotations": {
    "title": "Calculator",
    "readOnlyHint": true,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": false
  }
}

1.2 Tool Annotations Structure

Purpose: Behavioral hints for tools
Used by: list_tools/1 callback (nested in Tool)
Fields:

%McpServer.Tool.Annotations{
  title: String.t() | nil,              # Display title
  readOnlyHint: boolean(),              # Doesn't modify state
  destructiveHint: boolean(),           # May have side effects
  idempotentHint: boolean(),            # Same result on repeated calls
  openWorldHint: boolean()              # Works with unbounded data
}

1.3 Input/Output Schema Structure

Purpose: JSON Schema for validating tool parameters
Used by: list_tools/1 callback (nested in Tool)
Format: Standard JSON Schema object

%McpServer.Schema{
  type: String.t(),                     # "object", "string", "number", etc.
  properties: map(),                    # Field definitions
  required: list(String.t()),           # Required field names
  # Optional fields for nested schemas:
  description: String.t() | nil,
  enum: list() | nil,
  default: any() | nil
}

1.4 Tool Call Result Structure

Purpose: Response from tool execution Used by: call_tool/3 callback return value Return Format: {:ok, content_list} or {:ok, %CallResult{}} or {:error, message}

# Standard response — list of content items
{:ok, [McpServer.Tool.Content.text("Result text")]}

# Extended response with structured content (MCP Apps)
{:ok, McpServer.Tool.CallResult.new(
  content: [McpServer.Tool.Content.text("Result text")],
  structured_content: %{"key" => "value"},
  _meta: %{"timestamp" => "2024-01-01"}
)}

# Error response
{:error, "Error message"}

1.5 Tool Call Result (Extended) — McpServer.Tool.CallResult

Purpose: Extended tool result supporting structured content for UI rendering Used by: call_tool/3 callback return value (MCP Apps) Module: McpServer.Tool.CallResult

%McpServer.Tool.CallResult{
  content: list(),                 # Standard content for model context (required)
  structured_content: map() | nil, # Structured data for UI rendering (excluded from model context)
  _meta: map() | nil               # Additional metadata (timestamps, source info)
}

JSON Example:

{
  "content": [{"type": "text", "text": "Weather in NYC: 72F"}],
  "structuredContent": {
    "temperature": 72,
    "unit": "fahrenheit",
    "condition": "Sunny"
  },
  "_meta": {"source": "weather-api"},
  "isError": false
}

Note: structuredContent and _meta are omitted from the JSON response when nil.


2.1 Prompt Definition Structure

Purpose: Represents a prompt template definition
Used by: prompts_list/1 (Router-generated function)
JSON Field: Top-level in prompts list

%McpServer.Prompt{
  name: String.t(),              # Unique prompt identifier
  description: String.t(),       # Human-readable description
  arguments: list(map())         # List of argument definitions
}

JSON Example:

{
  "name": "code_review",
  "description": "Generates a code review prompt",
  "arguments": [
    {
      "name": "language",
      "description": "Programming language",
      "required": true
    },
    {
      "name": "code",
      "description": "Code to review",
      "required": true
    }
  ]
}

2.2 Prompt Argument Structure

Purpose: Defines an argument for a prompt
Used by: Nested in Prompt Definition

%McpServer.Prompt.Argument{
  name: String.t(),              # Argument identifier
  description: String.t(),       # Human-readable description
  required: boolean()            # Whether argument is mandatory
}

2.3 Prompt Message Structure

Purpose: Represents a single message in a prompt response
Used by: get_prompt/3 callback return value
Helper: McpServer.Controller.message/3

%McpServer.Prompt.Message{
  role: String.t(),              # "user", "assistant", or "system"
  content: map()                 # Content object with type and text
}

JSON Example:

{
  "role": "user",
  "content": {
    "type": "text",
    "text": "Hello world!"
  }
}

2.4 Message Content Structure

Purpose: Content of a prompt message
Used by: Nested in Prompt Message

%McpServer.Prompt.MessageContent{
  type: String.t(),              # "text", "image", etc.
  text: String.t() | nil,        # For text type
  # ... extensible for other content types
}

2.5 Completion Result Structure

Purpose: Completion suggestions for prompt arguments
Used by: complete_prompt/3 callback return value
Helper: McpServer.Controller.completion/2

%McpServer.Completion{
  values: list(String.t()),      # Completion suggestions
  total: integer() | nil,        # Total available completions
  hasMore: boolean() | nil       # Whether more completions exist
}

JSON Example:

{
  "values": ["Alice", "Bob", "Charlie"],
  "total": 10,
  "hasMore": true
}

3.1 Resource Definition Structure

Purpose: Represents a static resource
Used by: list_resources/1 callback
JSON Field: Top-level in resources list

%McpServer.Resource{
  name: String.t(),              # Unique resource identifier
  uri: String.t(),               # Static URI
  description: String.t() | nil, # Human-readable description
  mimeType: String.t() | nil,    # MIME type (e.g., "application/json")
  title: String.t() | nil        # Display title
}

JSON Example:

{
  "name": "config",
  "uri": "file:///app/config.json",
  "description": "Application configuration file",
  "mimeType": "application/json",
  "title": "App Config"
}

3.2 Resource Template Structure

Purpose: Represents a templated resource with variables
Used by: list_templates_resource/1 (Router-generated function)
JSON Field: Top-level in resource templates list

%McpServer.ResourceTemplate{
  name: String.t(),              # Unique resource identifier
  uriTemplate: String.t(),       # URI with {variable} placeholders
  description: String.t() | nil, # Human-readable description
  mimeType: String.t() | nil,    # MIME type
  title: String.t() | nil        # Display title
}

JSON Example:

{
  "name": "user",
  "uriTemplate": "https://api.example.com/users/{id}",
  "description": "User profile data",
  "mimeType": "application/json",
  "title": "User Profile"
}

3.3 Resource Read Result Structure

Purpose: Response from reading a resource
Used by: read_resource/3 callback return value
Helper: McpServer.Controller.content/3

%McpServer.Resource.ReadResult{
  contents: list(map())          # List of content items
}

JSON Example:

{
  "contents": [
    {
      "name": "user_data.json",
      "uri": "https://api.example.com/users/123",
      "mimeType": "application/json",
      "text": "{\"id\": 123, \"name\": \"Alice\"}",
      "title": "User 123"
    }
  ]
}

3.4 Resource Content Structure

Purpose: Represents a single content item from a resource
Used by: Nested in Resource Read Result
Helper: McpServer.Controller.content/3

%McpServer.Resource.Content{
  name: String.t(),              # Display name (e.g., filename)
  uri: String.t(),               # Canonical URI
  mimeType: String.t() | nil,    # MIME type
  text: String.t() | nil,        # Textual content
  blob: String.t() | nil,        # Base64-encoded binary content
  title: String.t() | nil        # Display title
}

JSON Example (text content):

{
  "name": "example.txt",
  "uri": "file:///path/to/example.txt",
  "mimeType": "text/plain",
  "text": "File content here...",
  "title": "Example File"
}

JSON Example (binary content):

{
  "name": "image.png",
  "uri": "file:///path/to/image.png",
  "mimeType": "image/png",
  "blob": "iVBORw0KGgoAAAANS..."
}

3.5 Resource Completion Result Structure

Purpose: Completion suggestions for resource URI template variables
Used by: complete_resource/3 callback return value
Format: Same as Prompt Completion Result

%McpServer.Completion{
  values: list(String.t()),      # Completion suggestions
  total: integer() | nil,        # Total available completions
  hasMore: boolean() | nil       # Whether more completions exist
}

4. MCP Apps Structures

These structures support the MCP Apps extension (io.modelcontextprotocol/ui). For usage details, see Building MCP Apps.

4.1 Tool Metadata Container — McpServer.Tool.Meta

Purpose: Wraps UI configuration in the _meta field on tools Used by: list_tools/1 responses Module: McpServer.Tool.Meta

%McpServer.Tool.Meta{
  ui: McpServer.Tool.Meta.UI.t() | nil
}

JSON Example:

{"ui": {"resourceUri": "ui://weather/dashboard", "visibility": ["model", "app"]}}

4.2 Resource Metadata Container — McpServer.Resource.Meta

Purpose: Wraps UI configuration in the _meta field on resources Used by: list_resources/1 responses Module: McpServer.Resource.Meta

%McpServer.Resource.Meta{
  ui: McpServer.Resource.Meta.UI.t() | nil
}

4.3 Tool UI Metadata — McpServer.Tool.Meta.UI

Purpose: Links tools to UI resources and controls visibility Used by: Nested in McpServer.Tool.Meta Module: McpServer.Tool.Meta.UI

%McpServer.Tool.Meta.UI{
  resource_uri: String.t() | nil,           # URI of the UI resource
  visibility: [McpServer.Tool.Meta.UI.visibility()]  # Default: [:model, :app]
}

# visibility is an enum type:
@type visibility :: :model | :app

JSON Example:

{"resourceUri": "ui://weather/dashboard", "visibility": ["model", "app"]}

4.4 Resource UI Metadata — McpServer.Resource.Meta.UI

Purpose: CSP, permissions, and sandbox settings for UI resources Used by: Nested in McpServer.Resource.Meta Module: McpServer.Resource.Meta.UI

%McpServer.Resource.Meta.UI{
  csp: McpServer.Resource.Meta.UI.CSP.t() | nil,
  permissions: McpServer.Resource.Meta.UI.Permissions.t() | nil,
  domain: String.t() | nil,                # Dedicated sandbox origin
  prefers_border: boolean() | nil           # Visual boundary preference
}

# CSP struct
%McpServer.Resource.Meta.UI.CSP{
  connect_domains: list(String.t()),        # connect-src domains
  resource_domains: list(String.t()),       # script/style/img/font-src domains
  frame_domains: list(String.t()),          # frame-src domains
  base_uri_domains: list(String.t())        # base-uri domains
}

# Permissions struct
%McpServer.Resource.Meta.UI.Permissions{
  camera: boolean(),                        # Camera access
  microphone: boolean(),                    # Microphone access
  geolocation: boolean(),                   # Location access
  clipboard_write: boolean()                # Clipboard write
}

JSON Example:

{
  "csp": {
    "connectDomains": ["api.example.com"],
    "resourceDomains": ["cdn.example.com"]
  },
  "permissions": {"camera": {}, "clipboardWrite": {}},
  "domain": "a904794854a047f6.example.com",
  "prefersBorder": true
}

5. Common Structures

5.1 Connection Context

Purpose: Provides session and request context to all callbacks
Not JSON-encoded: Internal structure passed to all callbacks

%McpServer.Conn{
  session_id: String.t(),        # Unique session identifier
  private: map()                 # Private storage for custom data
}

5.2 Error Response Structure

Purpose: Standard error format for all callbacks
Used by: All callbacks can return error tuples

{:error, String.t()}             # Simple error message

Note: For JSON-RPC errors, see McpServer.JsonRpc.Error


6. JSON-RPC Structures

6.1 JSON-RPC Request

Purpose: Incoming RPC request wrapper
Used by: HTTP transport layer

%McpServer.JsonRpc.Request{
  jsonrpc: "2.0",                # Protocol version
  method: String.t(),            # Method name (e.g., "tools/list")
  params: map() | list() | nil,  # Method parameters
  id: String.t() | integer() | nil  # Request ID
}

6.2 JSON-RPC Response

Purpose: Outgoing RPC response wrapper
Used by: HTTP transport layer

%McpServer.JsonRpc.Response{
  jsonrpc: "2.0",                # Protocol version
  result: any() | nil,           # Success result
  error: map() | nil,            # Error object
  id: String.t() | integer() | nil  # Request ID
}

6.3 JSON-RPC Error

Purpose: Standard error format for JSON-RPC
Used by: Nested in JSON-RPC Response

%McpServer.JsonRpc.Error{
  code: integer(),               # Error code
  message: String.t(),           # Error message
  data: any() | nil              # Additional error data
}

Standard Error Codes:

  • -32700: Parse error
  • -32600: Invalid request
  • -32601: Method not found
  • -32602: Invalid params
  • -32603: Internal error

7. Implementation Notes

7.1 JSON Encoding Protocol

All structures should implement the Jason.Encoder protocol for proper JSON serialization:

defimpl Jason.Encoder, for: McpServer.Tool do
  def encode(value, opts) do
    Jason.Encode.map(%{
      "name" => value.name,
      "description" => value.description,
      "inputSchema" => value.inputSchema,
      "annotations" => value.annotations
    }, opts)
  end
end

7.2 Field Naming Conventions

  • Elixir structs: Use snake_case for field names (e.g., session_id)
  • JSON output: Use camelCase for field names (e.g., "hasMore")
  • Exception: Special cases like inputSchema, uriTemplate (as per MCP spec)

7.3 Optional Fields

Optional fields (marked with | nil) should be omitted from JSON if nil:

# In encoder implementation
map
|> Map.reject(fn {_, v} -> is_nil(v) end)
|> Jason.Encode.map(opts)

7.4 Struct Modules Organization

Recommended module structure:

lib/mcp_server/
 tool.ex               # Tool, Tool.Annotations
 tool/
    call_result.ex    # Tool.CallResult (MCP Apps)
    meta.ex           # Tool.Meta
    meta/
        ui.ex         # Tool.Meta.UI (tool UI metadata)
 resource/
    meta.ex           # Resource.Meta
    meta/
        ui.ex         # Resource.Meta.UI (resource UI metadata)
        ui/
            csp.ex        # Resource.Meta.UI.CSP
            permissions.ex # Resource.Meta.UI.Permissions
 prompt.ex             # Prompt, Prompt.Argument, Prompt.Message, Prompt.MessageContent
 resource.ex           # Resource, ResourceTemplate, Resource.Content, Resource.ReadResult
 completion.ex         # Completion (shared by prompts and resources)
 schema.ex             # Schema (for JSON Schema validation)
 json_rpc/
    request.ex
    response.ex
    error.ex

8. Validation Requirements

8.1 Tool Validation

  • Tool names must be unique within a router
  • Input/output field names must be unique within a tool
  • Field types must be valid JSON Schema types
  • Required fields must be properly declared

8.2 Prompt Validation

  • Prompt names must be unique within a router
  • Argument names must be unique within a prompt
  • Get and complete functions must be defined
  • Message roles must be "user", "assistant", or "system"

8.3 Resource Validation

  • Resource names must be unique within a router
  • URIs must be valid
  • Template variables must match completion function expectations
  • Read function must always be defined
  • Complete function only for templated resources

9. Example Usage Patterns

9.1 Creating a Tool Definition

tool = %McpServer.Tool{
  name: "echo",
  description: "Echoes back the input",
  inputSchema: %McpServer.Schema{
    type: "object",
    properties: %{
      "message" => %{
        "type" => "string",
        "description" => "Message to echo"
      }
    },
    required: ["message"]
  },
  annotations: %McpServer.Tool.Annotations{
    title: "Echo",
    readOnlyHint: true,
    destructiveHint: false,
    idempotentHint: true,
    openWorldHint: false
  }
}

9.2 Creating Prompt Messages

messages = [
  %McpServer.Prompt.Message{
    role: "system",
    content: %McpServer.Prompt.MessageContent{
      type: "text",
      text: "You are a helpful assistant."
    }
  },
  %McpServer.Prompt.Message{
    role: "user",
    content: %McpServer.Prompt.MessageContent{
      type: "text",
      text: "Hello!"
    }
  }
]

9.3 Creating Resource Content

content = %McpServer.Resource.Content{
  name: "config.json",
  uri: "file:///app/config.json",
  mimeType: "application/json",
  text: Jason.encode!(%{setting: "value"}),
  title: "Application Configuration"
}

read_result = %McpServer.Resource.ReadResult{
  contents: [content]
}

10. Migration Path

To implement these structures in the existing codebase:

  1. Phase 1: Create struct definitions in new modules
  2. Phase 2: Implement Jason.Encoder protocols
  3. Phase 3: Update Router macro to use structs instead of maps
  4. Phase 4: Update Controller helpers to return structs
  5. Phase 5: Add tests for JSON encoding/decoding
  6. Phase 6: Update documentation and examples

11. Summary Table

StructureModulePurposeUsed By
ToolMcpServer.ToolTool definitionlist_tools/1
Tool.AnnotationsMcpServer.ToolTool metadataNested in Tool
SchemaMcpServer.SchemaJSON SchemaTool input/output
PromptMcpServer.PromptPrompt definitionprompts_list/1
Prompt.ArgumentMcpServer.PromptPrompt argumentNested in Prompt
Prompt.MessageMcpServer.PromptChat messageget_prompt/3
Prompt.MessageContentMcpServer.PromptMessage contentNested in Message
ResourceMcpServer.ResourceStatic resourcelist_resources/1
ResourceTemplateMcpServer.ResourceTemplated resourcelist_templates_resource/1
Resource.ContentMcpServer.ResourceResource contentread_resource/3
Resource.ReadResultMcpServer.ResourceRead responseread_resource/3
CompletionMcpServer.CompletionCompletionscomplete_prompt/3, complete_resource/3
ConnMcpServer.ConnConnection contextAll callbacks (exists)
JsonRpc.RequestMcpServer.JsonRpcRPC requestHTTP transport (exists)
JsonRpc.ResponseMcpServer.JsonRpcRPC responseHTTP transport (exists)
JsonRpc.ErrorMcpServer.JsonRpcRPC errorNested in Response (exists)
Tool.CallResultMcpServer.Tool.CallResultExtended tool result with structured contentcall_tool/3 (MCP Apps)
Tool.MetaMcpServer.Tool.MetaTool metadata containerlist_tools/1
Resource.MetaMcpServer.Resource.MetaResource metadata containerlist_resources/1
Tool.Meta.UIMcpServer.Tool.Meta.UITool UI metadataNested in Meta (tools)
Resource.Meta.UIMcpServer.Resource.Meta.UIResource UI metadata (CSP, permissions)Nested in Meta (resources)
Resource.Meta.UI.CSPMcpServer.Resource.Meta.UI.CSPCSP domain configurationNested in Resource.Meta.UI
Resource.Meta.UI.PermissionsMcpServer.Resource.Meta.UI.PermissionsSandbox permissionsNested in Resource.Meta.UI

This document provides a complete reference for all data structures needed to support the MCP server implementation with proper JSON encoding.