NAME

MCP::Server - MCP server implementation

SYNOPSIS

use MCP::Server;

my $server = MCP::Server->new(name => 'MyServer');

$server->tool(
  name         => 'echo',
  description  => 'Echo the input text',
  input_schema => {type => 'object', properties => {msg => {type => 'string'}}, required => ['msg']},
  code         => sub ($tool, $args) {
    return "Echo: $args->{msg}";
  }
);

$server->prompt(
  name        => 'echo',
  description => 'A prompt to demonstrate the echo tool',
  code        => sub ($prompt, $args) {
    return 'Use the echo tool with the message "Hello, World!"';
  }
);

$server->resource(
  uri         => 'file:///example.txt',
  name        => 'example',
  description => 'A simple text resource',
  mime_type   => 'text/plain',
  code        => sub ($resource) {
    return 'This is an example resource content.';
  }
);

$server->to_stdio;

DESCRIPTION

MCP::Server is an MCP (Model Context Protocol) server.

EVENTS

MCP::Server inherits all events from Mojo::EventEmitter and emits the following new ones.

prompts

$server->on(prompts => sub ($server, $prompts, $context) { ... });

Emitted whenever the list of prompts is accessed.

resources

$server->on(resources => sub ($server, $resources, $context) { ... });

Emitted whenever the list of resources is accessed.

tools

$server->on(tools => sub ($server, $tools, $context) { ... });

Emitted whenever the list of tools is accessed.

ATTRIBUTES

MCP::Server implements the following attributes.

cache_scope

my $scope = $server->cache_scope;
$server   = $server->cache_scope('private');

Cache scope advertised for server/discover results, either public or private. Defaults to public. Cache hints for the other cacheable results are declared per primitive with "cache_scope" in MCP::Primitive.

cache_ttl

my $ttl = $server->cache_ttl;
$server = $server->cache_ttl(3_600_000);

How long server/discover results may be cached, in milliseconds. Defaults to 0, which means the document must be revalidated on every request.

instructions

my $instructions = $server->instructions;
$server          = $server->instructions('Use the echo tool to repeat text.');

Free-form guidance for the model on how to use this server, returned by server/discover. Omitted from the document when undef, which is the default.

log

my $log = $server->log;
$server = $server->log(Mojo::Log->new);

Where exceptions thrown by prompts, resources, and tools are reported, defaults to a Mojo::Log object writing to STDERR, which is where an MCP host collects the output of a stdio server.

A server mounted in a Mojolicious application with "to_action" adopts the log of that application on its first request, so exceptions end up wherever the rest of the application logs, with the same level and format. Set this attribute yourself to opt out of that.

The caller never sees the exception itself, only an InternalError, since it can easily contain file system paths or connection strings.

name

my $name = $server->name;
$server  = $server->name('MyServer');

The name of the server, used for identification.

prompts

my $prompts = $server->prompts;
$server    = $server->prompts([MCP::Prompt->new]);

An array reference containing registered prompts.

resources

my $resources = $server->resources;
$server      = $server->resources([MCP::Resource->new]);

An array reference containing registered resources.

state_secret

my $secret = $server->state_secret;
$server    = $server->state_secret($ENV{MY_MCP_SECRET});

Key used to authenticate the request state of input_required results, which travels through the client and has to be tamper proof. Defaults to 32 random bytes generated once per process.

That default is only correct for a single process. Under a pre-forking web server or behind a load balancer every worker mints state the others reject, so retries loop instead of completing, and you have to configure the same secret everywhere. Use at least 32 bytes from a cryptographically secure source, and keep it out of your code.

state_timeout

my $seconds = $server->state_timeout;
$server     = $server->state_timeout(60);

How long the request state of an input_required result stays valid, in seconds. Defaults to 300, and should be no longer than a client plausibly needs to gather the requested input.

tools

my $tools = $server->tools;
$server   = $server->tools([MCP::Tool->new]);

An array reference containing registered tools.

transport

my $transport = $server->transport;
$server       = $server->transport(MCP::Server::Transport::HTTP->new);

The transport layer used by the server, such as MCP::Server::Transport::HTTP or MCP::Server::Transport::Stdio.

version

my $version = $server->version;
$server     = $server->version('1.0.0');

The version of the server.

METHODS

MCP::Server inherits all methods from Mojo::EventEmitter and implements the following new ones.

handle

my $response = $server->handle($request, $context);

Handle a JSON-RPC request and return a response, which may also be a Mojo::Promise. A subscriptions/listen request yields an MCP::Server::Subscription object instead, which the transport turns into a notification stream. Notifications yield undef.

notify_list_changed

my $bool = $server->notify_list_changed('tools');

Broadcast a notifications/$kind/list_changed JSON-RPC notification to all connected clients. Returns true on success, or undef if no notification could be delivered.

oauth_metadata

my $metadata = $server->oauth_metadata(
  resource              => 'https://example.com/mcp',
  authorization_servers => ['https://auth.example.com']
);

Build an OAuth 2.0 Protected Resource Metadata document from the given fields, to be served from /.well-known/oauth-protected-resource. Unless scopes_supported is provided, it is filled in with the sorted union of all scopes declared by registered tools, prompts, and resources.

prompt

my $prompt = $server->prompt(
  name        => 'my_prompt',
  description => 'A sample prompt',
  arguments   => [{name => 'foo', description => 'Whatever', required => 1}],
  code        => sub ($prompt, $args) { ... }
);

Register a new prompt with the server.

resource

my $resource = $server->resource(
  uri         => 'file://my_resource',
  name        => 'sample_resource',
  description => 'A sample resource',
  mime_type   => 'text/plain',
  code        => sub ($resource) { ... }
);

Register a new resource with the server.

to_action

my $action = $server->to_action;
my $action = $server->to_action({streaming => 1});

Convert the server to a Mojolicious action. Any options are passed through to the constructor of MCP::Server::Transport::HTTP; in particular, streaming => 1 opts in to subscriptions/listen, the long-lived notification stream.

to_stdio

$server->to_stdio;

Handles JSON-RPC requests over standard input/output.

tool

my $tool = $server->tool(
  name         => 'my_tool',
  description  => 'A sample tool',
  input_schema => {type => 'object', properties => {foo => {type => 'string'}}},
  code         => sub ($tool, $args) { ... }
);

Register a new tool with the server.

SEE ALSO

MCP, https://mojolicious.org, https://modelcontextprotocol.io.