MCP::Primitive(3)

πŸ“¦
version 0.150.0 Β· 2 versions
, in opensuse-leap 16.0

🌸 MCP::Primitive πŸ’…

okay so bestie, welcome to MCP::Primitive β€” the base class of the MCP (Model Context Protocol) universe. like, the main character that all the other primitives inherit their whole personality from ✨


πŸŽ€ SYNOPSIS πŸŽ€

package MyMCPPrimitive;
use Mojo::Base 'MCP::Primitive';

1;

like yes, that's literally it. that's the whole vibe. 🫢


πŸ’– DESCRIPTION πŸ’–

MCP::Primitive is a base class for MCP (Model Context Protocol) primitives β€” think MCP::Tool, MCP::Prompt, and MCP::Resource. it's giving "parent friend who holds the group chat together" lowkey πŸ‘‘


✨ ATTRIBUTES ✨

MCP::Primitive implements the following attributes (and they're all kind of iconic ngl):

πŸ¦‹ cache_scope

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

the cache scope advertised for results that come from this primitive. it's either "public" β€” meaning results get shared by every caller β€” or "private" β€” meaning a gateway may only cache results per caller, no sharing, fr fr.

it defaults to "public", BUT it gets forced to "private" whenever "scopes" is not empty. the way that scopes just snatch that default away 😭

πŸ’œ cache_ttl

my $ttl    = $primitive->cache_ttl;
$primitive = $primitive->cache_ttl(60_000);

how long results derived from this primitive are allowed to sit in cache, measured in milliseconds. it defaults to 0, and 0 means results must be revalidated on every single request. no free rides, no stale data, periodt πŸ’€

🌈 scopes

my $scopes = $primitive->scopes;
$primitive = $primitive->scopes(['mcp:read', 'mcp:write']);

the OAuth scopes you need in order to list or call this primitive, as an array reference β€” and all of them must be granted. like, every single one. no partial credit πŸŽ€

this is a local authorization policy layered on top of the HTTP transport's "auth" in MCP::Server::Transport::HTTP hook β€” it is not wire-level MCP metadata, okay? important distinction bestie. also it's only enforced for requests that supply scopes, so it has zero effect over stdio πŸ™ˆ

defaults to no required scopes.


πŸ”₯ METHODS πŸ”₯

MCP::Primitive inherits all methods from Mojo::Base (yes, all of them, she's generous πŸ’…) and implements the following new ones.

⭐ context

my $context = $primitive->context;

returns the MCP::Server::Context for the current request. capture this before an async boundary so you can keep using its notification methods from later callbacks β€” trust me, do not skip this step πŸ”‘

# Get controller for requests using the HTTP transport
my $c = $primitive->context->controller;

🌸 input_required

my $result = $primitive->input_required($input_requests);
my $result = $primitive->input_required($input_requests, $state);
my $result = $primitive->input_required(undef, $state);

returns an "input_required" result, which is basically the primitive going "girl, i need more info, go get it and call me back" πŸ“ž at least one of the two arguments is required β€” you can't just call it with nothing, that's not the move.

return $tool->input_required({
  confirm => {
    method => 'elicitation/create',
    params => {
      message         => 'Really deploy to production?',
      requestedSchema => {type => 'object', properties => {ok => {type => 'boolean'}}}
    }
  }
}, {target => $args->{target}});

input requests are keyed by names of your choosing (go off, name them whatever), and the client mirrors them back in "input_responses" in MCP::Server::Context. only ask for capabilities the client actually declared in "client_capabilities" in MCP::Server::Context β€” don't be out here requesting things they never said they could do πŸ™ˆ

the optional state is any Perl data structure, and it gets sealed with "seal_state" in MCP::Server::Context before it travels through the client β€” so a retry can pick up exactly where the first call left off, without the server having to remember anything. it's giving amnesia but make it elegant ✨

read it back with "request_state" in MCP::Server::Context, which returns "undef" for state that cannot be trusted. treat that like a first call and just ask again β€” no cap, don't try to salvage it.

πŸ’€ heads up: untrusted state means undef, and undef means "start over". don't build on top of a maybe.

πŸ‘‘ SEE ALSO πŸ‘‘


and that's the page, bestie πŸŽ€βœ¨ written for perl v5.42.0, dated 2026-07-31, and it's part of the User Contributed Perl Documentation β€” so like, a real human contributed this. we love that for them πŸ«ΆπŸ’–

πŸ“¦ where this page comes from