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.
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
├─────────────────────────────────────────────────────────────┤
│ MCP Library API │
├─────────────────────────────────────────────────────────────┤
│ Resource Manager │ Tool Manager │ Prompt Manager │
├─────────────────────────────────────────────────────────────┤
│ Jsonrpc.Server Implementation │
├─────────────────────────────────────────────────────────────┤
│ jsonrpc-glib-1.0 + Json-Glib │
└─────────────────────────────────────────────────────────────┘
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
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
}
}
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:
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; }
}
}
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);
}
}
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
}
}
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 ();
}
}
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
}
}
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 ();
}
}
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; }
}
}
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; }
}
}
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);
}
}
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
}
}
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;
}
}
}
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 ())
};
}
}
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
};
}
}
}
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'
)