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
# 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 )