Acme-MCP

 view release on metacpan or  search on metacpan

lib/Acme/MCP.pm  view on Meta::CPAN

use feature 'class';
no warnings 'experimental::class';
#
class Acme::MCP v1.0.1 {
    use JSON::PP;
    use Carp qw[carp croak];
    #
    field $name    : param : reader : writer = 'Generic MCP Server';
    field $version : param : reader : writer = '1.0.0';
    field %tools   : reader;
    field $json = JSON::PP->new->utf8(1);
    #
    method add_tool (%params) {
        my $name = $params{name} or croak 'Tool name required';
        my $code = $params{code} or croak 'Tool code (sub) required';
        $tools{$name}
            = { description => $params{description} // '', inputSchema => $params{schema} // { type => 'object', properties => {} }, code => $code };
    }

    method run () {
        $| = 1;    # Autoflush for stdio communication

t/basics.t  view on Meta::CPAN


    # Internal check
    my %all_tools = $mcp->tools;
    is $all_tools{echo}, E(), 'Tool registered';
};

# We mock STDIN/STDOUT to test the JSON-RPC loop
subtest 'JSON-RPC Handling' => sub {
    my $mcp = Acme::MCP->new( name => 'TestServer' );
    $mcp->add_tool( name => 'add', code => sub ($args) { $args->{a} + $args->{b} } );
    my $json = JSON::PP->new->utf8(1);
    my $request
        = $json->encode( { jsonrpc => '2.0', id => 1, method => 'tools/call', params => { name => 'add', arguments => { a => 5, b => 10 } } } );

    # Mock handle_request directly to avoid loop
    my $response;
    no warnings 'redefine';
    local *Acme::MCP::_send_response = sub ( $self, $id, $res ) {
        $response = $res;
    };
    $mcp->_handle_request( $json->decode($request) );



( run in 0.434 second using v1.01-cache-2.11-cpan-e93a5daba3e )