File Coverage

blib/lib/MCP/Server/Transport/Stdio.pm
Criterion Covered Total %
statement 15 61 24.5
branch 0 12 0.0
condition 0 3 0.0
subroutine 5 11 45.4
pod 3 3 100.0
total 23 90 25.5


line stmt bran cond sub pod time code
1             package MCP::Server::Transport::Stdio;
2 4     4   24 use Mojo::Base 'MCP::Server::Transport', -signatures;
  4         7  
  4         23  
3              
4 4     4   944 use MCP::Server::Context;
  4         8  
  4         17  
5 4     4   105 use Mojo::JSON qw(decode_json encode_json);
  4         4  
  4         238  
6 4     4   434 use Mojo::Log;
  4         11529  
  4         27  
7 4     4   121 use Scalar::Util qw(blessed);
  4         5  
  4         2854  
8              
9 0     0 1   sub handle_requests ($self) {
  0            
  0            
10 0           my $server = $self->server;
11              
12 0           binmode STDIN, ':raw';
13 0           binmode STDOUT, ':raw';
14 0           STDOUT->autoflush(1);
15              
16 0           my $buffer = '';
17 0           while (defined(my $input = _read_line(\$buffer))) {
18 0 0         next if $input eq '';
19 0           my $request = eval { decode_json($input) };
  0            
20 0 0         next unless my $response = $server->handle($request, MCP::Server::Context->new(transport => $self));
21              
22 0 0 0       if (blessed($response) && $response->isa('Mojo::Promise')) {
23 0     0     $response->then(sub { _print_response($_[0]) })->wait;
  0            
24             }
25 0           else { _print_response($response) }
26             }
27             }
28              
29 0     0 1   sub notify ($self, $session_id, $method, $params = {}) {
  0            
  0            
  0            
  0            
  0            
30 0           _print_response({jsonrpc => '2.0', method => $method, params => $params});
31 0           return 1;
32             }
33              
34 0     0 1   sub notify_all ($self, $method, $params = {}) { $self->notify(undef, $method, $params) }
  0            
  0            
  0            
  0            
  0            
35              
36 0     0     sub _print_response ($response) { print encode_json($response) . "\n" }
  0            
  0            
  0            
37              
38 0     0     sub _read_line ($buffer) {
  0            
  0            
39 0           while (index($$buffer, "\n") < 0) {
40 0 0         last unless sysread STDIN, my $chunk, 131072;
41 0           $$buffer .= $chunk;
42             }
43 0 0         return undef if $$buffer eq '';
44              
45 0           my $pos = index($$buffer, "\n");
46 0 0         my $line = $pos < 0 ? substr($$buffer, 0, length($$buffer), '') : substr($$buffer, 0, $pos + 1, '');
47 0           $line =~ s/\r?\n?$//;
48 0           return $line;
49             }
50              
51             1;
52              
53             =encoding utf8
54              
55             =head1 NAME
56              
57             MCP::Server::Transport::Stdio - Stdio transport for MCP servers
58              
59             =head1 SYNOPSIS
60              
61             use MCP::Server::Transport::Stdio;
62              
63             my $stdio = MCP::Server::Transport::Stdio->new;
64              
65             =head1 DESCRIPTION
66              
67             L is a transport for MCP (Model Context Protocol) server that reads requests from
68             standard input (STDIN) and writes responses to standard output (STDOUT). It is designed for command-line tools and
69             debugging tasks.
70              
71             =head1 ATTRIBUTES
72              
73             L inherits all attributes from L.
74              
75             =head1 METHODS
76              
77             L inherits all methods from L and implements the following new
78             ones.
79              
80             =head2 handle_requests
81              
82             $stdio->handle_requests;
83              
84             Reads requests from standard input and prints responses to standard output.
85              
86             =head2 notify
87              
88             my $bool = $stdio->notify($session_id, $method);
89             my $bool = $stdio->notify($session_id, $method, {foo => 'bar'});
90              
91             Send a JSON-RPC notification to standard output. The C<$session_id> is ignored.
92              
93             =head2 notify_all
94              
95             my $bool = $stdio->notify_all($method);
96             my $bool = $stdio->notify_all($method, {foo => 'bar'});
97              
98             Send a JSON-RPC notification to standard output.
99              
100             =head1 SEE ALSO
101              
102             L, L, L.
103              
104             =cut