The Modal Context Protocol: Bridging Applications and AI

Dove-WingDove-Wing
9 min read

Introduction to MCP

The Modal Context Protocol (MCP) establishes a transformative open standard enabling rich interactions between applications and Large Language Models (LLMs). By providing a common interface, MCP allows diverse applications to expose their data and functionality to AI systems in a consistent, secure manner. This article explores key MCP components and demonstrates implementations across three application types: database systems, filesystems, and email servers.

Core Components of MCP

Core Verbs

MCP defines standardized verbs forming the foundation of all context operations:

  • LOAD: Retrieves context from an application

  • STORE: Persists context data for future use

  • QUERY: Requests specific context information based on criteria

  • UPDATE: Modifies existing context

  • CLEAR: Removes specific context

  • MERGE: Combines multiple context sources

  • TRANSFORM: Converts context from one format to another

  • PRIORITIZE: Assigns importance levels to context elements

  • CAPABILITIES: Discovers supported features and operations

Example: PostgreSQL QUERY Implementation

Here's how an MCP QUERY might be implemented in PostgreSQL:

CREATE FUNCTION mcp_query(collection TEXT, filter JSONB) RETURNS JSONB AS $$
DECLARE
    result JSONB;
BEGIN
    -- Validate query schema
    IF NOT validate_query_schema(collection, filter) THEN
        RETURN jsonb_build_object(
            'status', 'error',
            'data', jsonb_build_object('message', 'Invalid query schema', 'code', '400'),
            'metadata', jsonb_build_object(
                'operation', 'QUERY',
                'timestamp', current_timestamp,
                'version', '1.0'
            )
        );
    END IF;

    -- Execute query and format response
    EXECUTE 'SELECT jsonb_agg(row_to_json(t)) FROM ' || collection || 
            ' t WHERE ' || jsonb_build_sql_filter(filter) INTO result;

    RETURN jsonb_build_object(
        'status', 'success',
        'data', COALESCE(result, '[]'::jsonb),
        'metadata', jsonb_build_object(
            'operation', 'QUERY',
            'timestamp', current_timestamp,
            'version', '1.0'
        )
    );
END;
$$ LANGUAGE plpgsql;

Example: Email Server TRANSFORM Implementation

Email server implementations can transform content between formats:

json_object* mcp_transform_email(struct mail *mail, const char *transform_type) {
    json_object *response = json_object_new_object();
    json_object *data = json_object_new_object();

    // Apply transformation based on transform_type
    if (strcmp(transform_type, "summarize") == 0) {
        char *summary = generate_email_summary(mail);
        json_object_object_add(data, "summary", json_object_new_string(summary));
        free(summary);
    } 
    else if (strcmp(transform_type, "extract_entities") == 0) {
        json_object *entities = extract_entities_from_email(mail);
        json_object_object_add(data, "entities", entities);
    }

    // Add standard response structure
    json_object_object_add(response, "status", json_object_new_string("success"));
    json_object_object_add(response, "data", data);
    json_object_object_add(response, "metadata", create_standard_metadata("TRANSFORM"));

    return response;
}

Context Scoping

MCP defines three scopes for contextual information:

  • Global: Persists across all sessions and users

  • Session: Persists for the duration of a user session

  • Request: Exists only for the current request

Example: Filesystem STORE with Scoping

json_object* mcp_store_context(const char *data_json, const char *context_name, const char *scope) {
    json_object *response = json_object_new_object();

    // Determine base directory based on scope
    char *base_dir;
    if (strcmp(scope, "global") == 0) {
        base_dir = "/var/lib/mcp/global/";
    } 
    else if (strcmp(scope, "session") == 0) {
        base_dir = "/var/lib/mcp/sessions/";
        // Append session ID
        char session_path[PATH_MAX];
        snprintf(session_path, PATH_MAX, "%s%s/", base_dir, get_session_id());
        base_dir = session_path;
    } 
    else if (strcmp(scope, "request") == 0) {
        base_dir = "/tmp/mcp/requests/";
        char request_path[PATH_MAX];
        snprintf(request_path, PATH_MAX, "%s%s/", base_dir, get_request_id());
        base_dir = request_path;
    }

    // Store data to appropriate location
    char path[PATH_MAX];
    snprintf(path, PATH_MAX, "%s%s.json", base_dir, context_name);
    write_json_file(path, data_json);

    // Format standard response
    json_object_object_add(response, "status", json_object_new_string("success"));
    json_object_object_add(response, "data", 
                          json_object_new_object_add("context_id", json_object_new_string(context_name)));
    json_object_object_add(response, "metadata", create_standard_metadata("STORE"));

    return response;
}

Capability Negotiation

MCP's capability negotiation allows clients and servers to advertise and discover supported operations.

Example: Database CAPABILITIES Implementation

CREATE FUNCTION mcp_capabilities() RETURNS JSONB AS $$
BEGIN
    RETURN jsonb_build_object(
        'status', 'success',
        'data', jsonb_build_object(
            'supported_verbs', jsonb_build_object(
                'LOAD', jsonb_build_object('supported', true),
                'STORE', jsonb_build_object('supported', true),
                'QUERY', jsonb_build_object(
                    'supported', true,
                    'filter_operations', jsonb_build_array('eq', 'gt', 'lt', 'contains')
                ),
                'UPDATE', jsonb_build_object('supported', true),
                'CAPABILITIES', jsonb_build_object('supported', true)
            ),
            'supported_versions', jsonb_build_array('1.0', '1.1'),
            'system_limits', jsonb_build_object(
                'max_payload_size', 10485760,
                'max_results_per_query', 1000
            )
        ),
        'metadata', jsonb_build_object(
            'operation', 'CAPABILITIES',
            'timestamp', current_timestamp,
            'version', '1.0'
        )
    );
END;
$$ LANGUAGE plpgsql;

Content Type Negotiation

MCP supports different content representations through content type negotiation.

Example: Filesystem Content Type Handler

json_object* handle_request_with_content_type(const char *operation, 
                                             const char *params_json,
                                             const char *accept_header) {
    // Default is JSON
    const char *content_type = "application/json";

    // Parse Accept header
    if (accept_header && strstr(accept_header, "text/csv")) {
        content_type = "text/csv";
    }

    // Process operation and get result
    json_object *result = process_operation(operation, params_json);

    // Convert result to requested format if needed
    if (strcmp(content_type, "text/csv") == 0) {
        char *csv_data = convert_json_to_csv(result);
        json_object_put(result);

        // Create new result with CSV data
        result = create_formatted_response("success", 
                                          json_object_new_string(csv_data),
                                          "text/csv", 
                                          operation);
        free(csv_data);
    }

    return result;
}

Prompt Templates

MCP includes a system for prompt templates that can be filled with context-specific information.

Example: Email Server PROMPT_LIST

json_object* mcp_prompt_list() {
    json_object *response = json_object_new_object();
    json_object *templates_array = json_object_new_array();

    // Define email reply template
    json_object *reply_template = json_object_new_object();
    json_object_object_add(reply_template, "template_id", json_object_new_string("email_reply"));
    json_object_object_add(reply_template, "description", 
                          json_object_new_string("Generate a reply to an email"));
    json_object_object_add(reply_template, "version", json_object_new_string("1.0"));

    // Add parameters summary
    json_object *params = json_object_new_object();
    json_object_object_add(params, "count", json_object_new_int(3));
    json_object_object_add(params, "required_count", json_object_new_int(2));
    json_object_object_add(reply_template, "parameters", params);

    json_object_array_add(templates_array, reply_template);

    // Format response
    json_object_object_add(response, "status", json_object_new_string("success"));
    json_object_object_add(response, "data", 
                          json_object_new_object_add("templates", templates_array));
    json_object_object_add(response, "metadata", create_standard_metadata("PROMPT_LIST"));

    return response;
}

Example: PostgreSQL PROMPT_GET

CREATE FUNCTION mcp_prompt_get(template_id TEXT) RETURNS JSONB AS $$
DECLARE
    template JSONB;
BEGIN
    -- Look up template by ID
    SELECT prompt_template INTO template 
    FROM mcp_prompt_templates 
    WHERE id = template_id;

    IF template IS NULL THEN
        RETURN jsonb_build_object(
            'status', 'error',
            'data', jsonb_build_object('message', 'Template not found', 'code', '404'),
            'metadata', jsonb_build_object(
                'operation', 'PROMPT_GET',
                'timestamp', current_timestamp,
                'version', '1.0'
            )
        );
    END IF;

    RETURN jsonb_build_object(
        'status', 'success',
        'data', template,
        'metadata', jsonb_build_object(
            'operation', 'PROMPT_GET',
            'timestamp', current_timestamp,
            'version', '1.0'
        )
    );
END;
$$ LANGUAGE plpgsql;

Tools & Function Calling

MCP provides a standardized mechanism for exposing application functionality as tools.

Example: Filesystem TOOL_LIST

json_object* mcp_tool_list() {
    json_object *response = json_object_new_object();
    json_object *tools_array = json_object_new_array();

    // Define file search tool
    json_object *search_tool = json_object_new_object();
    json_object_object_add(search_tool, "tool_id", json_object_new_string("file_search"));
    json_object_object_add(search_tool, "name", json_object_new_string("File Search"));
    json_object_object_add(search_tool, "description", 
                          json_object_new_string("Search files by name and content"));

    // Define parameters
    json_object *params = json_object_new_array();

    json_object *param1 = json_object_new_object();
    json_object_object_add(param1, "name", json_object_new_string("query"));
    json_object_object_add(param1, "type", json_object_new_string("string"));
    json_object_object_add(param1, "required", json_object_new_boolean(true));
    json_object_array_add(params, param1);

    json_object_object_add(search_tool, "parameters", params);
    json_object_array_add(tools_array, search_tool);

    // Format response
    json_object_object_add(response, "status", json_object_new_string("success"));
    json_object_object_add(response, "data", 
                          json_object_new_object_add("tools", tools_array));
    json_object_object_add(response, "metadata", create_standard_metadata("TOOL_LIST"));

    return response;
}

Example: Email Server TOOL_EXECUTE

json_object* mcp_tool_execute(const char *tool_id, const char *params_json) {
    json_object *response = json_object_new_object();
    json_object *params_obj = json_tokener_parse(params_json);

    if (strcmp(tool_id, "find_conversation") == 0) {
        // Extract parameters
        const char *participant = json_get_string(params_obj, "participant");
        const char *subject = json_get_string(params_obj, "subject_contains");
        int days = json_get_int(params_obj, "days_back", 7);  // Default to 7 days

        // Validate required parameters
        if (!participant) {
            return create_error_response("Missing required parameter: participant", "400");
        }

        // Execute tool logic
        json_object *conversation = find_email_conversation(participant, subject, days);

        // Return results
        json_object_object_add(response, "status", json_object_new_string("success"));
        json_object_object_add(response, "data", 
                             json_object_new_object_add("conversation", conversation));
        json_object_object_add(response, "metadata", create_standard_metadata("TOOL_EXECUTE"));
    }
    else {
        return create_error_response("Unknown tool", "404");
    }

    json_object_put(params_obj);
    return response;
}

Context Retrieval Patterns

MCP supports multiple patterns for retrieving and updating context:

  • Pull pattern: Client explicitly requests context

  • Push pattern: Server proactively provides context

  • Streaming pattern: Context delivered incrementally

  • Event-based pattern: Context updates based on triggers

Example: PostgreSQL Streaming Pattern

CREATE FUNCTION mcp_stream_query(collection TEXT, filter JSONB, batch_size INTEGER DEFAULT 10)
RETURNS SETOF JSONB AS $$
DECLARE
    cursor_name TEXT;
    batch_data JSONB;
    batch_num INTEGER := 0;
    total_rows INTEGER;
BEGIN
    -- Count total rows for metadata
    EXECUTE 'SELECT COUNT(*) FROM ' || collection || 
            ' WHERE ' || jsonb_build_sql_filter(filter) INTO total_rows;

    -- Set up cursor for streaming
    cursor_name := 'mcp_stream_' || md5(random()::text);
    OPEN cursor_name FOR EXECUTE 'SELECT row_to_json(t) FROM ' || collection || 
                                ' t WHERE ' || jsonb_build_sql_filter(filter);

    -- Stream results in batches
    LOOP
        batch_data := jsonb_build_array();

        -- Fetch batch
        FOR i IN 1..batch_size LOOP
            FETCH cursor_name INTO batch_data;
            EXIT WHEN NOT FOUND;
            batch_data := batch_data || batch_data;
        END LOOP;

        -- Exit if batch is empty
        IF jsonb_array_length(batch_data) = 0 THEN
            EXIT;
        END IF;

        -- Return batch with metadata
        batch_num := batch_num + 1;
        RETURN NEXT jsonb_build_object(
            'status', 'success',
            'data', batch_data,
            'metadata', jsonb_build_object(
                'operation', 'QUERY_STREAM',
                'batch', batch_num,
                'total_records', total_rows,
                'is_last', jsonb_array_length(batch_data) < batch_size
            )
        );

        -- Exit if this was the last batch
        IF jsonb_array_length(batch_data) < batch_size THEN
            EXIT;
        END IF;
    END LOOP;

    CLOSE cursor_name;
    RETURN;
END;
$$ LANGUAGE plpgsql;

Example: Email Server Event-Based Pattern

// Event listener setup
void setup_mcp_event_listeners(struct mail_user *user) {
    mail_user_hook_register(user, "mail-new", mcp_handle_new_mail_event, NULL);
    mail_user_hook_register(user, "mail-read", mcp_handle_mail_read_event, NULL);
}

// Event handler for new mail
void mcp_handle_new_mail_event(struct mail *mail, void *context) {
    // Check if any clients are subscribed
    if (!has_active_subscriptions("mail-new")) {
        return;
    }

    // Create event notification
    json_object *event = json_object_new_object();
    json_object *data = json_object_new_object();

    // Add mail information
    const char *subject = get_mail_header(mail, "Subject");
    const char *from = get_mail_header(mail, "From");

    json_object_object_add(data, "subject", json_object_new_string(subject));
    json_object_object_add(data, "from", json_object_new_string(from));
    json_object_object_add(data, "id", json_object_new_string(get_mail_id(mail)));

    // Format and send notification
    json_object_object_add(event, "status", json_object_new_string("success"));
    json_object_object_add(event, "data", data);
    json_object_object_add(event, "metadata", 
                         json_object_new_object_add("event_type", 
                                                  json_object_new_string("mail-new")));

    // Send to subscribed clients
    notify_subscribers("mail-new", event);
    json_object_put(event);
}

Implementation in Practice

PostgreSQL Database MCP

Key aspects of a PostgreSQL MCP implementation:

  1. Schema Exposure: Generate context about database schema structure

     -- Function to expose table schema
     CREATE FUNCTION mcp_get_table_schema(table_name TEXT) RETURNS JSONB AS $$
     BEGIN
         RETURN (SELECT jsonb_agg(row_to_json(cols))
                 FROM (SELECT column_name, data_type, character_maximum_length
                       FROM information_schema.columns 
                       WHERE table_name = $1) cols);
     END;
     $$ LANGUAGE plpgsql;
    
  2. Query Translation: Map MCP QUERY operations to SQL queries

  3. Transaction Support: Ensure ACID properties for context operations

  4. Row-Level Security: Apply existing database security to MCP operations

  5. Tools Integration: Expose database functions as MCP tools

Filesystem MCP Implementation

Key aspects of a filesystem MCP implementation:

  1. Content Extraction: Extract text and metadata from various file formats

     // Example content extraction function
     json_object* extract_file_content(const char *path) {
         const char *extension = get_file_extension(path);
    
         if (strcmp(extension, "txt") == 0) {
             return extract_text_file_content(path);
         }
         else if (strcmp(extension, "pdf") == 0) {
             return extract_pdf_content(path);
         }
         // Support for other formats
    
         return json_object_new_object();  // Empty if unsupported
     }
    
  2. Path-Based Context: Use directory hierarchy to structure context

  3. Permission Mapping: Respect file system permissions for MCP operations

  4. File Monitoring: Implement event-based updates for file changes

  5. Search Integration: Leverage existing file indexing for fast context retrieval

Email Server MCP Implementation

Key aspects of an email server MCP implementation:

  1. Thread Analysis: Reconstruct email threads as conversational context

     // Thread reconstruction example
     json_object* reconstruct_thread(const char *message_id) {
         json_object *thread = json_object_new_array();
    
         // Find root message of thread
         const char *root_id = find_thread_root(message_id);
    
         // Get all messages in thread
         struct mail_list *messages = get_messages_in_thread(root_id);
    
         // Sort by date
         sort_messages_by_date(messages);
    
         // Convert to JSON array
         for (int i = 0; i < messages->count; i++) {
             json_object *msg = convert_mail_to_json(messages->items[i]);
             json_object_array_add(thread, msg);
         }
    
         free_mail_list(messages);
         return thread;
     }
    
  2. Contact Management: Provide relationship context about correspondents

  3. Content Prioritization: Surface important messages through PRIORITIZE

  4. Template Integration: Offer prompt templates for email composition

  5. Tool Support: Implement tools for email analysis and management

Security Considerations

MCP implementations must address several security concerns:

  1. Authentication: Use OAuth 2.0 or equivalent

     bool verify_auth_token(const char *token, const char *required_permission) {
         // Validate token format
         if (!is_valid_token_format(token)) {
             return false;
         }
    
         // Verify token signature
         if (!verify_token_signature(token)) {
             return false;
         }
    
         // Check expiration
         if (is_token_expired(token)) {
             return false;
         }
    
         // Check permissions
         return token_has_permission(token, required_permission);
     }
    
  2. Authorization: Implement fine-grained access control

  3. Data Protection: Ensure sensitive data is properly protected

  4. Audit Logging: Maintain comprehensive logs of all operations

  5. Rate Limiting: Protect against denial of service attacks

  6. Transport Security: Use TLS for all MCP communications

Conclusion

The Modal Context Protocol provides a comprehensive framework for applications to expose their data and functionality to LLMs in a standardized way. Through code samples from PostgreSQL databases, filesystems, and email servers, we've seen how MCP enables context-aware AI interactions while maintaining security and control.

The protocol's standardized verbs, content negotiation mechanisms, and security features enable a new generation of AI tools that can seamlessly integrate with existing applications. As the MCP ecosystem grows, we can expect increasingly sophisticated AI interactions that leverage deep application context while respecting security and privacy boundaries.

0
Subscribe to my newsletter

Read articles from Dove-Wing directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dove-Wing
Dove-Wing