mcp-vala-architecture-design.md 22 KB

Vala MCP Library Architecture Design

Overview

This document outlines the architecture for a Vala library implementing the Model Context Protocol (MCP) version "2025-11-25" using jsonrpc-glib-1.0 for JSON-RPC communication. The design prioritizes developer experience while maintaining protocol compliance and performance.

Core Architecture

High-Level Design

┌─────────────────────────────────────────────────────────────┐
│                    Application Layer                      │
├─────────────────────────────────────────────────────────────┤
│                    MCP Library API                      │
├─────────────────────────────────────────────────────────────┤
│  Resource Manager  │  Tool Manager  │  Prompt Manager   │
├─────────────────────────────────────────────────────────────┤
│              Jsonrpc.Server Implementation               │
├─────────────────────────────────────────────────────────────┤
│              jsonrpc-glib-1.0 + Json-Glib               │
└─────────────────────────────────────────────────────────────┘

Key Design Principles

  1. Developer Experience First: Simple, intuitive API with minimal boilerplate
  2. Type Safety: Leverage Vala's type system while using Json-Glib for JSON handling
  3. Asynchronous Design: Non-blocking operations throughout
  4. Extensibility: Clean interfaces for custom implementations
  5. Protocol Compliance: Full MCP v2025-11-25 specification support

Namespace Organization

Mcp
├── Core                    // Core server infrastructure
│   └── Server             // Main server class using Jsonrpc.Server
├── Types                   // Shared data types and interfaces
│   ├── Protocol            // MCP protocol types
│   └── Common             // Common content types
├── Resources              // Resource management
│   ├── Manager
│   ├── Provider
│   └── Types
├── Tools                 // Tool execution framework
│   ├── Manager
│   ├── Executor
│   └── Types
├── Prompts               // Prompt handling system
│   ├── Manager
│   ├── Template
│   └── Types
└── Core                  // Core error handling
    └── Error

Core Components

1. Server Infrastructure

Mcp.Core.Server

namespace Mcp.Core {
    public class Server : Object {
        // Server configuration
        public ServerInfo server_info { get; construct; }
        public ServerCapabilities capabilities { get; construct; }
        
        // Core managers
        public ResourceManager resource_manager { get; construct; }
        public ToolManager tool_manager { get; construct; }
        public PromptManager prompt_manager { get; construct; }
        
        // Jsonrpc server instance
        private Jsonrpc.Server jsonrpc_server;
        
        // Lifecycle
        public signal void initialized ();
        public signal void shutdown ();
        
        public async bool start () throws Error;
        public async void stop ();
        
        // Jsonrpc method handlers
        private Variant handle_initialize (Jsonrpc.Client client, string method, Variant params) throws Error;
        private Variant handle_ping (Jsonrpc.Client client, string method, Variant params) throws Error;
        private Variant handle_resources_list (Jsonrpc.Client client, string method, Variant params) throws Error;
        // ... other handlers
    }
}

2. Jsonrpc Integration

The library now uses the standard Jsonrpc.Server from jsonrpc-glib-1.0 for handling JSON-RPC communication. The custom JSON-RPC message types and transport layer have been removed in favor of the standard implementation.

Key benefits:

  • Standard-compliant JSON-RPC 2.0 implementation
  • Robust error handling
  • Built-in support for async/await patterns
  • Proper STDIO transport handling

Protocol Types

namespace Mcp.Types.Protocol {
    public class ServerInfo : Object {
        public string name { get; set; }
        public string version { get; set; }
        public string? description { get; set; }
        public string? website_url { get; set; }
    }
    
    public class ServerCapabilities : Object {
        public bool logging { get; set; }
        public bool completions { get; set; }
        public PromptsCapabilities? prompts { get; set; }
        public ResourcesCapabilities? resources { get; set; }
        public ToolsCapabilities? tools { get; set; }
    }
    
    public class PromptsCapabilities : Object {
        public bool list_changed { get; set; }
    }
    
    public class ResourcesCapabilities : Object {
        public bool subscribe { get; set; }
        public bool list_changed { get; set; }
    }
    
    public class ToolsCapabilities : Object {
        public bool list_changed { get; set; }
    }
}

4. Resource Management System

Mcp.Resources.Manager

namespace Mcp.Resources {
    public interface Provider : Object {
        public abstract async List<Resource> list_resources (string? cursor) throws Error;
        public abstract async ResourceContents read_resource (string uri) throws Error;
        public virtual async void subscribe (string uri) throws Error { /* Optional */ }
        public virtual async void unsubscribe (string uri) throws Error { /* Optional */ }
    }
    
    public class Manager : Object {
        private HashTable<string, Provider> providers;
        
        public void register_provider (string uri_scheme, Provider provider);
        public void unregister_provider (string uri_scheme);
        
        // Protocol handlers
        public async Json.Node handle_list (Json.Object params) throws Error;
        public async Json.Node handle_read (Json.Object params) throws Error;
        public async Json.Node handle_subscribe (Json.Object params) throws Error;
        public async Json.Node handle_unsubscribe (Json.Object params) throws Error;
        
        // Notifications
        public void notify_list_changed ();
        public void notify_resource_updated (string uri);
    }
}

Resource Types

namespace Mcp.Resources.Types {
    public class Resource : Object {
        public string uri { get; set; }
        public string name { get; set; }
        public string? title { get; set; }
        public string? description { get; set; }
        public string? mime_type { get; set; }
        public Annotations? annotations { get; set; }
        public int64? size { get; set; }
    }
    
    public class ResourceContents : Object {
        public string uri { get; set; }
        public string? mime_type { get; set; }
    }
    
    public class TextResourceContents : ResourceContents {
        public string text { get; set; }
    }
    
    public class BlobResourceContents : ResourceContents {
        public string blob { get; set; } // Base64 encoded
    }
}

5. Tool Execution Framework

Mcp.Tools.Manager

namespace Mcp.Tools {
    public interface Executor : Object {
        public abstract ToolDefinition get_definition ();
        public abstract async CallToolResult execute (Json.Object arguments) throws Error;
    }
    
    public class Manager : Object {
        private HashTable<string, Executor> executors;
        
        public void register_executor (string name, Executor executor);
        public void unregister_executor (string name);
        
        // Protocol handlers
        public async Json.Node handle_list (Json.Object params) throws Error;
        public async Json.Node handle_call (Json.Object params) throws Error;
        
        // Notifications
        public void notify_list_changed ();
    }
}

Tool Types

namespace Mcp.Tools.Types {
    public class ToolDefinition : Object {
        public string name { get; set; }
        public string? title { get; set; }
        public string? description { get; set; }
        public Json.Object input_schema { get; set; }
        public Json.Object? output_schema { get; set; }
        public ToolExecution? execution { get; set; }
        public ToolAnnotations? annotations { get; set; }
    }
    
    public class CallToolResult : Object {
        public List<ContentBlock> content { get; set; }
        public Json.Object? structured_content { get; set; }
        public bool is_error { get; set; default = false; }
    }
    
    public class ToolExecution : Object {
        public string task_support { get; set; default = "forbidden"; } // forbidden, optional, required
    }
}

6. Prompt Handling System

Mcp.Prompts.Manager

namespace Mcp.Prompts {
    public interface Template : Object {
        public abstract PromptDefinition get_definition ();
        public abstract async GetPromptResult get_prompt (Json.Object arguments) throws Error;
    }
    
    public class Manager : Object {
        private HashTable<string, Template> templates;
        
        public void register_template (string name, Template template);
        public void unregister_template (string name);
        
        // Protocol handlers
        public async Json.Node handle_list (Json.Object params) throws Error;
        public async Json.Node handle_get (Json.Object params) throws Error;
        
        // Notifications
        public void notify_list_changed ();
    }
}

Prompt Types

namespace Mcp.Prompts.Types {
    public class PromptDefinition : Object {
        public string name { get; set; }
        public string? title { get; set; }
        public string? description { get; set; }
        public List<PromptArgument> arguments { get; set; }
    }
    
    public class PromptArgument : Object {
        public string name { get; set; }
        public string? title { get; set; }
        public string? description { get; set; }
        public bool required { get; set; default = false; }
    }
    
    public class GetPromptResult : Object {
        public string? description { get; set; }
        public List<PromptMessage> messages { get; set; }
    }
    
    public class PromptMessage : Object {
        public string role { get; set; } // "user" | "assistant"
        public ContentBlock content { get; set; }
    }
}

7. Content System

namespace Mcp.Types.Common {
    public interface ContentBlock : Object {
        public string type { get; }
    }
    
    public class TextContent : Object, ContentBlock {
        public string type { get; construct; default = "text"; }
        public string text { get; set; }
        public Annotations? annotations { get; set; }
    }
    
    public class ImageContent : Object, ContentBlock {
        public string type { get; construct; default = "image"; }
        public string data { get; set; } // Base64 encoded
        public string mime_type { get; set; }
        public Annotations? annotations { get; set; }
    }
    
    public class ResourceLink : Object, ContentBlock {
        public string type { get; construct; default = "resource_link"; }
        public string uri { get; set; }
        public string name { get; set; }
        public string? title { get; set; }
        public string? description { get; set; }
        public string? mime_type { get; set; }
        public Annotations? annotations { get; set; }
    }
    
    public class EmbeddedResource : Object, ContentBlock {
        public string type { get; construct; default = "resource"; }
        public ResourceContents resource { get; set; }
        public Annotations? annotations { get; set; }
    }
}

8. Error Handling

namespace Mcp.Core {
    public errordomain McpError {
        PARSE_ERROR,
        INVALID_REQUEST,
        METHOD_NOT_FOUND,
        INVALID_PARAMS,
        INTERNAL_ERROR,
        RESOURCE_NOT_FOUND,
        TOOL_EXECUTION_FAILED,
        PROMPT_NOT_FOUND
    }
    
    public class ErrorHandler : Object {
        public static Json.Node create_error_response (string id, McpError error);
        public static Json.Node create_error_notification (McpError error);
        public static McpError from_json_code (int code, string message);
    }
}

9. Event System

namespace Mcp.Notifications {
    public class Emitter : Object {
        public signal void resource_list_changed ();
        public signal void resource_updated (string uri);
        public signal void tool_list_changed ();
        public signal void prompt_list_changed ();
        public signal void logging_message (LoggingLevel level, string? logger, Json.Node data);
        public signal void progress (ProgressToken token, double progress, double? total, string? message);
    }
    
    public enum LoggingLevel {
        DEBUG,
        INFO,
        NOTICE,
        WARNING,
        ERROR,
        CRITICAL,
        ALERT,
        EMERGENCY
    }
}

Public API Design

Simple Server Creation

using Mcp;

public class MyServer : Object {
    private Mcp.Core.Server server;
    
    public MyServer () {
        // Create server with basic info
        server = new Mcp.Core.Server (
            new Mcp.Types.Protocol.ServerInfo () {
                name = "my-mcp-server",
                version = "1.0.0",
                description = "My awesome MCP server"
            },
            new Mcp.Types.Protocol.ServerCapabilities () {
                resources = new Mcp.Types.Protocol.ResourcesCapabilities () {
                    subscribe = true,
                    list_changed = true
                },
                tools = new Mcp.Types.Protocol.ToolsCapabilities () {
                    list_changed = true
                }
            }
        );
        
        // Register providers
        setup_resources ();
        setup_tools ();
        setup_prompts ();
    }
    
    private void setup_resources () {
        server.resource_manager.register_provider ("file", new FileResourceProvider ());
        server.resource_manager.register_provider ("http", new HttpResourceProvider ());
    }
    
    private void setup_tools () {
        server.tool_manager.register_executor ("calculate", new CalculateTool ());
        server.tool_manager.register_executor ("fetch", new FetchTool ());
    }
    
    private void setup_prompts () {
        server.prompt_manager.register_template ("summarize", new SummarizePrompt ());
    }
    
    public async int run () {
        try {
            yield server.start ();
            new MainLoop ().run ();
            return 0;
        } catch (Error e) {
            stderr.printf ("Server error: %s\n", e.message);
            return 1;
        }
    }
}

Resource Provider Example

using Mcp;

public class FileResourceProvider : Object, Mcp.Resources.Provider {
    public async List<Mcp.Resources.Types.Resource> list_resources (string? cursor) throws Error {
        var resources = new List<Mcp.Resources.Types.Resource> ();
        
        // List files in current directory
        var dir = File.new_for_path (".");
        var enumerator = await dir.enumerate_children_async (
            "standard::name,standard::type,standard::size",
            FileQueryInfoFlags.NONE
        );
        
        FileInfo file_info;
        while ((file_info = yield enumerator.next_file_async ()) != null) {
            if (file_info.get_file_type () == FileType.REGULAR) {
                var file = enumerator.get_child (file_info);
                var resource = new Mcp.Resources.Types.Resource () {
                    uri = file.get_uri (),
                    name = file_info.get_name (),
                    mime_type = guess_mime_type (file_info.get_name ()),
                    size = file_info.get_size ()
                };
                resources.append (resource);
            }
        }
        
        return resources;
    }
    
    public async Mcp.Resources.Types.ResourceContents read_resource (string uri) throws Error {
        var file = File.new_for_uri (uri);
        if (!file.query_exists ()) {
            throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (uri));
        }
        
        uint8[] contents;
        yield file.load_contents_async (null, out contents);
        
        var text = (string) contents;
        return new Mcp.Resources.Types.TextResourceContents () {
            uri = uri,
            text = text,
            mime_type = guess_mime_type (file.get_basename ())
        };
    }
}

Tool Executor Example

using Mcp;

public class CalculateTool : Object, Mcp.Tools.Executor {
    public Mcp.Tools.Types.ToolDefinition get_definition () {
        return new Mcp.Tools.Types.ToolDefinition () {
            name = "calculate",
            description = "Perform mathematical calculations",
            input_schema = new Json.Object () {
                type = "object",
                properties = new Json.Object () {
                    expression = new Json.Object () {
                        type = "string",
                        description = "Mathematical expression to evaluate"
                    }
                },
                required = new string[] { "expression" }
            }
        };
    }
    
    public async Mcp.Tools.Types.CallToolResult execute (Json.Object arguments) throws Error {
        var expression = arguments.get_string_member ("expression");
        
        try {
            // Simple expression evaluation (in real implementation, use proper parser)
            var result = evaluate_expression (expression);
            
            var content = new Mcp.Types.Common.TextContent () {
                text = "Result: %s = %s".printf (expression, result.to_string ())
            };
            
            var contents = new List<Mcp.Types.Common.ContentBlock> ();
            contents.append (content);
            
            return new Mcp.Tools.Types.CallToolResult () {
                content = contents,
                structured_content = new Json.Object () {
                    expression = expression,
                    result = result.to_string ()
                }
            };
        } catch (Error e) {
            var content = new Mcp.Types.Common.TextContent () {
                text = "Error evaluating expression: %s".printf (e.message)
            };
            
            var contents = new List<Mcp.Types.Common.ContentBlock> ();
            contents.append (content);
            
            return new Mcp.Tools.Types.CallToolResult () {
                content = contents,
                is_error = true
            };
        }
    }
}

Build System

Meson Build Configuration

project('mcp-vala', 'vala', 'c',
  version: '1.0.0',
  default_options: ['warning_level=2', 'werror=false']
)

# Dependencies
glib_dep = dependency('glib-2.0', version: '>= 2.70')
gobject_dep = dependency('gobject-2.0', version: '>= 2.70')
gio_dep = dependency('gio-2.0', version: '>= 2.70')
json_glib_dep = dependency('json-glib-1.0', version: '>= 1.6')
jsonrpc_glib_dep = dependency('jsonrpc-glib-1.0', version: '>= 3.34')
gio_unix_dep = dependency('gio-unix-2.0')

# Library configuration
mcp_sources = [
  'src/core/server.vala',
  'src/core/error.vala',
  'src/types/protocol.vala',
  'src/types/common.vala',
  'src/resources/manager.vala',
  'src/resources/provider.vala',
  'src/resources/types.vala',
  'src/tools/manager.vala',
  'src/tools/executor.vala',
  'src/tools/types.vala',
  'src/prompts/manager.vala',
  'src/prompts/template.vala',
  'src/prompts/types.vala'
]

mcp_vala_args = [
  '--target-glib=2.70',
  '--pkg', 'glib-2.0',
  '--pkg', 'gobject-2.0',
  '--pkg', 'gio-2.0',
  '--pkg', 'json-glib-1.0',
  '--pkg', 'jsonrpc-glib-1.0',
  '--pkg', 'gio-unix-2.0'
]

# Build library
mcp_lib = shared_library(
  'mcp-vala',
  mcp_sources,
  dependencies: [
    glib_dep,
    gobject_dep,
    gio_dep,
    json_glib_dep,
    jsonrpc_glib_dep,
    gio_unix_dep
  ],
  vala_args: mcp_vala_args,
  install: true,
  install_dir: get_option('libdir')
)

# Install headers
install_headers(
  'mcp-vala.h',
  install_dir: join_paths(get_option('includedir'), 'mcp-vala')
)

# VAPI file
mcp_vapi = custom_target(
  'mcp-vala.vapi',
  input: mcp_sources,
  output: 'mcp-vala.vapi',
  command: [find_program('valac'), '--vapi', '@OUTPUT@'] + mcp_vala_args + ['@INPUT@'],
  install: true,
  install_dir: get_option('datadir') / 'vala' / 'vapi'
)

# PC file
pkg_mod = import('pkgconfig')
pkg_mod.generate(
  libraries: mcp_lib,
  version: meson.project_version(),
  name: 'mcp-vala',
  description: 'Vala library for Model Context Protocol',
  filebase: 'mcp-vala',
  subdirs: 'mcp-vala'
)

Implementation Considerations

JSON Handling Strategy

  1. Use Json-Glib for data structures: Use Json.Node, Json.Object, and Json.Array for MCP protocol types
  2. Use GLib.Variant for Jsonrpc communication: Convert between Json.Node and GLib.Variant when interfacing with Jsonrpc.Server
  3. Type-safe serialization: Create helper methods for converting between Vala objects and JSON
  4. Validation: Use Json-Glib's schema validation where applicable
  5. Memory management: Leverage Vala's reference counting for JSON objects

Asynchronous Operations

  1. Gio.Async patterns: Use async/await throughout for non-blocking operations
  2. Jsonrpc integration: Properly handle async method calls with Jsonrpc.Server

Jsonrpc Integration Benefits

  1. Standard compliance: Full JSON-RPC 2.0 compliance through jsonrpc-glib-1.0
  2. Robust error handling: Built-in error handling and response formatting
  3. IO handling: Proper STDIO transport handling with buffering and error recovery
  4. Performance: Optimized JSON parsing and message handling