Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Model Context Protocol (MCP) Module for Tinystruct

This module provides comprehensive integration with the Model Context Protocol (MCP) for the Tinystruct framework. It enables AI model interactions, tool discovery and execution, resource management, and prompt handling through a unified interface.

Overview

The MCP module implements the Model Context Protocol specification, providing both client and server capabilities for AI model interactions. It supports JSON-RPC communication, Server-Sent Events (SSE), authentication, and a unified resource model.

Components

The MCP module consists of the following core components:

Core Classes

  1. MCPApplication: Abstract base class for MCP applications with JSON-RPC handling
  2. MCPServer: Complete MCP server implementation with tool and resource management
  3. MCPClient: Full-featured client for connecting to MCP servers

Resource Management

  1. MCPResource: Unified interface for all MCP resources (tools, data sources, prompts)
  2. MCPTool: Implementation for executable tools with method registration
  3. MCPDataResource: Implementation for data resources
  4. MCPPrompt: Implementation for prompt templates with parameter substitution

Protocol Support

  1. MCPSpecification: Protocol constants, methods, and error codes
  2. JsonRpcHandler: JSON-RPC 2.0 protocol implementation
  3. AuthorizationHandler: JWT-based authentication
  4. MCPPushManager: Server-Sent Events management for real-time updates

Supporting Classes

  1. MCPException: Custom exception handling for MCP operations
  2. AbstractMCPResource: Base implementation for MCP resources
  3. MCPLifecycle: Lifecycle management utilities

MCP Server Implementation

Creating an MCP Server

import org.tinystruct.mcp.MCPServer;
import org.tinystruct.mcp.tools.CalculatorTool;

public class MyMCPServer extends MCPServer {

    @Override
    public void init() {
        super.init();

        // Register tools
        CalculatorTool calculator = new CalculatorTool();
        this.registerTool(calculator);

        // Register custom tools
        this.registerTool(new MyCustomTool());

        // Register prompts
        MCPPrompt greetingPrompt = new MCPPrompt(
                "greeting",
                "A greeting prompt",
                "Hello, {{name}}!",
                createPromptSchema(),
                null
        ) {
            @Override
            protected boolean supportsLocalExecution() {
                return true;
            }
        };
        this.registerPrompt(greetingPrompt);
    }
}

Starting the Server

# Start with Netty HTTP Server
bin/dispatcher start --import org.tinystruct.mcp.examples.SampleMCPServerApplication

# Or programmatically
MCPServer server = new MyMCPServer();
ApplicationManager.install(server, settings);

Context serverContext = new ApplicationContext();
serverContext.setAttribute("--server-port", "8004");
ApplicationManager.install(new Dispatcher());
ApplicationManager.install(new HttpServer());
ApplicationManager.call("start", serverContext, Action.Mode.CLI);

Using the MCP CLI Client

The module provides a CLI application for interacting with MCP servers:

# Connect to a server
bin/dispatcher mcp/connect --url http://localhost:8004 [--token auth-token]

# List available tools
bin/dispatcher mcp/list/tools

# Call a tool
bin/dispatcher mcp/call --name calculator/add --arguments "a:10,b:20"

# List available resources
bin/dispatcher mcp/list

# Disconnect
bin/dispatcher mcp/disconnect

Supported MCP Methods

The server implements the following MCP methods:

  • initialize: Establish connection and return server capabilities
  • tools/list: List available tools with schemas
  • tools/call: Execute tools with parameters
  • resources/list: List available data resources
  • resources/read: Read data from resources
  • prompts/list: List available prompt templates
  • prompts/get: Retrieve prompt templates
  • get-capabilities: Get server capabilities
  • get-status: Get server status
  • shutdown: Gracefully shutdown the server

Programmatic Usage

import org.tinystruct.mcp.MCPClient;
import org.tinystruct.mcp.MCPResource;
import org.tinystruct.mcp.MCPException;

// Create and connect the client
MCPClient client = new MCPClient("http://localhost:8080", "your-auth-token");
client.connect();

try {
    // List all resources
    List<MCPResource> resources = client.listResources();
    for (MCPResource resource : resources) {
        System.out.println(resource.getName() + ": " + resource.getDescription());
    }
    
    // Execute a tool
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("a", 10);
    parameters.put("b", 5);
    Object result = client.executeResource("calculator/add", parameters);
    System.out.println("Result: " + result);
    
} catch (MCPException e) {
    e.printStackTrace();
} finally {
    client.disconnect();
}

Creating Custom Tools

Simple Tool Implementation

import org.tinystruct.mcp.MCPTool;
import org.tinystruct.system.annotation.Action;
import org.tinystruct.system.annotation.Argument;

public class MyCustomTool extends MCPTool {
    
    public MyCustomTool() {
        super("my-tool", "A custom tool", null, null, true);
    }
    
    @Action(value = "my-tool/process", 
            description = "Process data", 
            arguments = {
                @Argument(key = "input", description = "Input data", type = "string")
            })
    public String processData(String input) {
        return "Processed: " + input.toUpperCase();
    }
}

Advanced Tool with Schema

import org.tinystruct.data.component.Builder;
import org.tinystruct.mcp.MCPTool;
import org.tinystruct.mcp.MCPException;

public class AdvancedTool extends MCPTool {
    
    public AdvancedTool() {
        super("advanced", "Advanced processing tool", createSchema(), null, true);
    }
    
    private static Builder createSchema() {
        Builder schema = new Builder();
        Builder properties = new Builder();
        
        Builder inputParam = new Builder();
        inputParam.put("type", "string");
        inputParam.put("description", "Input data to process");
        
        properties.put("input", inputParam);
        schema.put("type", "object");
        schema.put("properties", properties);
        schema.put("required", new String[]{"input"});
        
        return schema;
    }
    
    @Override
    protected Object executeLocally(Builder builder) throws MCPException {
        String input = builder.get("input").toString();
        // Custom processing logic
        return "Advanced processing result: " + input;
    }
}

Creating Custom Prompts

import org.tinystruct.mcp.MCPPrompt;
import org.tinystruct.data.component.Builder;

public class CustomPrompt extends MCPPrompt {
    
    public CustomPrompt() {
        super(
            "custom-prompt",
            "A custom prompt template",
            "Hello {{name}}, your score is {{score}}!",
            createPromptSchema(),
            null
        );
    }
    
    private static Builder createPromptSchema() {
        Builder schema = new Builder();
        Builder properties = new Builder();
        
        Builder nameParam = new Builder();
        nameParam.put("type", "string");
        nameParam.put("description", "User's name");
        
        Builder scoreParam = new Builder();
        scoreParam.put("type", "number");
        scoreParam.put("description", "User's score");
        
        properties.put("name", nameParam);
        properties.put("score", scoreParam);
        schema.put("type", "object");
        schema.put("properties", properties);
        schema.put("required", new String[]{"name", "score"});
        
        return schema;
    }
    
    @Override
    protected boolean supportsLocalExecution() {
        return true;
    }
    
    @Override
    protected Object executeLocally(Builder builder) throws MCPException {
        // Use the default implementation which handles placeholder substitution
        return super.executeLocally(builder);
    }
}

Server-Sent Events (SSE) Support

The MCP module includes built-in SSE support for real-time updates:

// Server-side: Push events to clients
MCPPushManager.getInstance().push(sessionId, eventData);

// Client-side: Receive SSE events
// The MCPClient automatically handles SSE connections
client.connect(); // Establishes both JSON-RPC and SSE connections

Authentication and Security

JWT Authentication

The MCP module supports JWT-based authentication:

// Server configuration
getConfiguration().set("mcp.auth.token", "your-secret-token");

// Client connection with token
MCPClient client = new MCPClient("http://localhost:8080", "your-secret-token");

Session Management

// Automatic session management
String sessionId = request.getSession().getId();
sessionMap.put(sessionId, System.currentTimeMillis());

Error Handling

The MCP module provides comprehensive error handling:

try {
    Object result = client.executeResource("tool-name", parameters);
} catch (MCPException e) {
    // Handle MCP-specific errors
    System.err.println("MCP Error: " + e.getMessage());
} catch (IOException e) {
    // Handle connection errors
    System.err.println("Connection Error: " + e.getMessage());
}

Protocol Features

JSON-RPC 2.0 Support

  • Full JSON-RPC 2.0 protocol implementation
  • Batch request support
  • Error handling with standard error codes
  • Method validation and routing

Resource Types

  • TOOL: Executable functions with parameters
  • DATA: Data sources and repositories
  • PROMPT: Template-based text generation
  • TEMPLATE: URI templates for resource access

Capabilities

  • logging: Log level management
  • prompts: Prompt template support with list change notifications
  • resources: Resource subscription and list change notifications
  • tools: Tool discovery with list change notifications

Key Updates (2026)

Resource Management

  • MCPApplication and MCPServer now support registerTool, registerResource, and registerPrompt methods for resource management.
  • Example:
// Register tools/resources/prompts
this.registerTool(tool);
this.registerResource(resource);
this.registerPrompt(prompt);

JSON-RPC Error Response Handling

  • Error responses include a unique request ID and standard error codes.
  • Example:
String response = handler.createErrorResponse("Error message", -32600);

Batch Request Support

  • JsonRpcHandler supports batch JSON-RPC requests and returns batch responses.
  • Example:
String batchResponse = handler.handleBatchRequest(batchJson, methodHandler);

MCPPushManager Clarification

  • MCPPushManager is a thin wrapper for SSEPushManager, providing an MCP-specific entry point for server-sent events.
  • Use MCPPushManager.getInstance() for real-time updates.

Protocol Handler Registration

  • Custom JSON-RPC methods can be registered via registerRpcHandler.
  • Example:
this.registerRpcHandler("custom/method", (req, res, app) -> {
    res.setResult("Custom result");
});

Usage Example Updates

  • All resource, tool, and prompt management examples updated to use new lifecycle methods.
  • Error handling and batch request examples added.

Examples

Complete Server Example

import org.tinystruct.mcp.MCPServer;
import org.tinystruct.mcp.tools.CalculatorTool;

public class CompleteMCPServer extends MCPServer {

    @Override
    public void init() {
        super.init();

        // Register calculator tool
        CalculatorTool calculator = new CalculatorTool();
        this.registerTool(calculator);

        // Register custom tools
        this.registerTool(new FileProcessorTool());
        this.registerTool(new DataAnalysisTool());

        // Register prompts
        this.registerPrompt(new GreetingPrompt());
        this.registerPrompt(new ReportPrompt());

        // Register data resources
        this.registerResource(new DatabaseResource());
        this.registerResource(new FileSystemResource());
    }
}

Complete Client Example

import org.tinystruct.mcp.MCPClient;
import java.util.HashMap;
import java.util.Map;

public class MCPClientExample {
    
    public static void main(String[] args) {
        MCPClient client = new MCPClient("http://localhost:8080", "auth-token");
        
        try {
            // Connect to server
            client.connect();
            System.out.println("Connected to MCP server");
            
            // List available tools
            List<MCPResource> tools = client.listResources();
            System.out.println("Available tools:");
            for (MCPResource tool : tools) {
                System.out.println("- " + tool.getName() + ": " + tool.getDescription());
            }
            
            // Execute calculator tool
            Map<String, Object> params = new HashMap<>();
            params.put("a", 15);
            params.put("b", 3);
            Object result = client.executeResource("calculator/add", params);
            System.out.println("15 + 3 = " + result);
            
            // Execute custom tool
            Map<String, Object> fileParams = new HashMap<>();
            fileParams.put("filename", "data.txt");
            Object fileResult = client.executeResource("file-processor/read", fileParams);
            System.out.println("File content: " + fileResult);
            
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
        } finally {
            client.disconnect();
        }
    }
}

Configuration

Server Configuration

# MCP Server Configuration
mcp.auth.token=your-secret-token
mcp.server.port=8080
mcp.server.host=localhost
mcp.sse.enabled=true
mcp.jsonrpc.enabled=true

Client Configuration

# MCP Client Configuration
mcp.client.timeout=30000
mcp.client.retry.attempts=3
mcp.client.retry.delay=1000
mcp.sse.reconnect.enabled=true

Extending the MCP Module

Creating Custom Resource Types

public class CustomResource extends AbstractMCPResource {
    
    public CustomResource(String name, String description) {
        super(name, description, ResourceType.CUSTOM);
    }
    
    @Override
    public Object execute(Builder builder) throws MCPException {
        // Custom execution logic
        return "Custom resource executed";
    }
}

Adding Custom RPC Handlers

// Register custom RPC method handler
this.registerRpcHandler("custom/method", (req, res, app) -> {
    // Custom method implementation
    res.setResult("Custom method result");
});

License

This module is licensed under the Apache License 2.0. See the LICENSE file for details.