=begin pod

=head1 MCP::Server

A framework for building MCP (Model Context Protocol) servers in Raku.
Register tools, resources, and prompts — the framework handles the
JSON-RPC 2.0 protocol, initialization handshake, and message dispatch.

=head2 Synopsis

=begin code :lang<raku>
use MCP::Server;

my $server = MCP::Server.new(:name<my-tools>, :version<1.0>);

$server.tool: 'greet',
    description => 'Greet someone by name',
    params => {
        name => { type => 'string', description => 'Name to greet', required => True },
    },
    handler => -> :%args { "Hello, {%args<name>}!" };

$server.run;  # Listens on stdin/stdout
=end code

Use with Claude Code:

=begin code :lang<json>
{
  "mcpServers": {
    "my-tools": {
      "command": "raku",
      "args": ["-I", "lib", "examples/my-server.raku"]
    }
  }
}
=end code

=head2 Tools

Tools are functions the LLM can call. Define parameters with types and
descriptions — the framework generates JSON Schema automatically.

=begin code :lang<raku>
$server.tool: 'search',
    description => 'Search the web',
    params => {
        query => { type => 'string', description => 'Search query', required => True },
        limit => { type => 'integer', description => 'Max results' },
    },
    handler => -> :%args {
        # Return a string (wrapped as text content)
        "Results for {%args<query>}..."
    };
=end code

Tool handlers receive C<:%args> and return:
=item C<Str> — wrapped as C<[{type: "text", text: $result}]>
=item C<List> — passed through as content items
=item On exception — returned as C<isError: true> content

=head2 Tool Groups

Group related tools under a common prefix using C</> as separator:

=begin code :lang<raku>
$server.tool-group: 'file', -> $g {
    $g.tool: 'read',
        description => 'Read a file',
        params => { path => { type => 'string', required => True } },
        handler => -> :%args { %args<path>.IO.slurp };

    $g.tool: 'list',
        description => 'List a directory',
        params => { path => { type => 'string', required => True } },
        handler => -> :%args { %args<path>.IO.dir.join("\n") };
};
# Registers as: file/read, file/list
=end code

=head2 LLM Tool Bridge

Tools registered with MCP::Server can be used with any OpenAI-compatible
LLM API. Define tools once, use them both as MCP tools (for Claude Code)
and as function-calling tools (for LLM API calls).

=begin code :lang<raku>
use MCP::Server;
use LLM::Chat::Backend::OpenAICommon;
use LLM::Chat::Backend::Settings;
use LLM::Chat::Conversation::Message;

# Define tools
my $server = MCP::Server.new(:name<my-tools>);

$server.tool: 'get_weather',
    description => 'Get weather for a location',
    params => { location => { type => 'string', required => True } },
    handler => -> :%args {
        my $proc = run 'curl', '-s', "https://wttr.in/{%args<location>}?format=3", :out;
        $proc.out.slurp(:close).trim;
    };

# Convert to OpenAI format and send to LLM
my @tools = $server.tools-for-llm;
my $backend = LLM::Chat::Backend::OpenAICommon.new(...);
my @messages = (Message.new(:role<user>, :content<What is the weather in London?>),);

my $resp = $backend.chat-completion(@messages, :@tools);

# If LLM wants to call a tool, execute it
if $resp.has-tool-calls {
    my @results = $server.execute-tool-calls($resp.tool-calls);
    # @results are {role=>"tool", tool_call_id=>"...", content=>"..."}
    # Append to messages and call LLM again for final answer
}
=end code

C<tools-for-llm> converts registered tools to the OpenAI function-calling
format. C<execute-tool-calls> routes the LLM's tool call requests to
your registered handlers and returns results ready to send back.

=head2 Resources

Resources expose data the LLM can read.

=begin code :lang<raku>
$server.resource: 'config://app',
    name => 'App Config',
    description => 'Application configuration',
    mime-type => 'application/json',
    handler => -> :%args { '{"debug": true}' };
=end code

=head2 Prompts

Prompts are pre-defined templates for the user.

=begin code :lang<raku>
$server.prompt: 'review',
    description => 'Code review prompt',
    arguments => [
        { name => 'code', description => 'Code to review', required => True },
    ],
    handler => -> :%args { "Please review:\n{%args<code>}" };
=end code

=head2 Examples

=item C<examples/echo-server.raku> — Minimal echo and reverse tools
=item C<examples/weather-server.raku> — Weather via wttr.in
=item C<examples/file-server.raku> — File system tools using tool groups
=item C<examples/strawberry-server.raku> — The tool LLMs wish they had

=head2 Protocol

Implements MCP protocol version C<2025-11-25> over stdio:

=item JSON-RPC 2.0 message format
=item C<initialize> / C<initialized> handshake
=item C<tools/list>, C<tools/call>
=item C<resources/list>, C<resources/read>
=item C<prompts/list>, C<prompts/get>
=item C<ping>
=item Logging via C<notifications/message>
=item Full error handling with standard JSON-RPC error codes

=head2 Transport

Default transport is stdio (stdin/stdout). Custom transports can be
created by implementing the C<MCP::Server::Transport> role:

=begin code :lang<raku>
use MCP::Server::Transport;

class MyTransport does MCP::Server::Transport {
    method read-message(--> Str) { ... }
    method write-message(Str:D $msg) { ... }
}

$server.run(:transport(MyTransport.new));
=end code

=head2 Author

Matt Doughty

=head2 License

Artistic-2.0

=end pod
