NAME

Langertha::Raider::MCP::Server - Internal client of one configured MCP server, mounted as an engine MCP server

VERSION

version 0.504

SYNOPSIS

# Internal to Langertha-Raider -- no API promise.
my $server = Langertha::Raider::MCP::Server->new(
  spec => $mcp_config->server('github'),   # an entry of Langertha::Raider::MCP::Config
  loop => $loop,
);
await $server->initialize;                 # never fails: status connected or failed
say $server->status.': '.( $server->reason // '' );
my $tools  = await $server->list_tools;    # names mcp__github__*
my $result = await $server->call_tool( 'mcp__github__create_issue', \%args );
await $server->stop_f;

DESCRIPTION

Internal module. Its interface may change without notice.

One configured MCP server (ADR 0029 §10) as raider mounts it: an object that answers initialize, list_tools and call_tool like Net::Async::MCP, so it is one more entry of the engine's mcp_servers and Langertha::Raider calls it as it calls every other.

  • Start. "start_f" (and "initialize") connects once: the transport (Langertha::Raider::MCP::Stdio or Langertha::Raider::MCP::HTTP), the initialize handshake, the notifications/initialized notification and the tool list, all within "startup_timeout_ms". It never fails: "status" becomes connected, or failed with "reason"; a stdio process of a failed start is stopped.

  • Protocol. Raider speaks the established handshake of the MCP revisions 2024-11-05 to 2025-11-25: it asks for "client_protocol_version" and accepts any revision of "known_protocol_versions" the server answers with. The stateless 2026-07-28 revision (server/discover) is not spoken; a server that knows only that one fails with a reason that says so. The installed Net::Async::MCP speaks only that revision, which is why raider has its own client here (ADR 0029).

  • Tools. The server's tools are listed once, at the start, under the names the model sees: mcp__KEY__TOOL with the tool part normalised ("tool_name" in Langertha::Raider::MCP::Config). Two tools that normalise to the same name: the first is kept. "call_tool" sends the original name.

  • Calls are bounded by the server's timeout_ms (its timeout, else MCP_TOOL_TIMEOUT); a call that runs out is cancelled (notifications/cancelled) and fails. A result keeps what the server sent, except _meta keys under raider/: a foreign server cannot hand the raider instructions through them (Langertha::Raider takes user messages from _meta.raider/messages of its own sources).

spec

The server's entry of "servers" in Langertha::Raider::MCP::Config. Required.

loop

The IO::Async::Loop. Required.

startup_timeout_ms

How long the start may take, in milliseconds. Default 30000.

base_env

The environment a stdio server inherits (before its own env). Default a copy of %ENV.

kill_grace

Seconds between closing a stdio server's stdin, TERM and KILL. Default 2.

on_status

sub { my ( $server ) = @_; ... }, called whenever "status" changes. Optional.

max_output_tokens

How many tokens (about four characters each) of a tool result's text raider passes on; more is cut with a note (ADR 0029 Addendum 1). Default 25000 (MAX_MCP_OUTPUT_TOKENS).

on_tools_changed

sub { my ( $server ) = @_; ... }, called when the server announced notifications/tools/list_changed, at once: the tools are listed again in the background, and "list_tools" waits for that. Optional.

status

configured before the start, starting, then connected or failed; stopped after "stop_f".

reason

Why it failed (or stopped), with the last stderr line of a stdio server.

protocol_version

The revision the server answered with.

server_info

The server's serverInfo (name, version), or {}.

instructions

The server's instructions, or undef.

warnings

What went wrong without stopping the server: a tool dropped because its name collides, ...

name

key

transport

The server's name, key and transport from "spec".

client_protocol_version

The revision raider asks for in initialize: 2025-11-25.

known_protocol_versions

The revisions raider accepts in the answer: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05. They differ in features raider does not use; the handshake, tools/list and tools/call are the same in all of them.

fingerprint

What the server is started with, as one string: the transport, the command, arguments, working and project directory and the whole environment of a stdio server, the URL and headers of an HTTP one, the tool prefix and the call timeout. Two clients with the same fingerprint talk to the same thing (/reload keeps such a client).

pid

The process id of a running stdio server, else undef.

stderr_tail

The last lines a stdio server wrote to stderr.

child_env

The environment a stdio server is started with: "base_env" without inherited CLAUDE_PLUGIN_ROOT / CLAUDE_PLUGIN_DATA, then the entry's env, then CLAUDE_PROJECT_DIR (the project dir, as Claude Code sets it) and, for a plugin's server, its CLAUDE_PLUGIN_ROOT and CLAUDE_PLUGIN_DATA.

start_f

await $server->start_f;

Connects once (see "DESCRIPTION"); every later call returns the same future. Never fails.

initialize

The same as "start_f", under the name Langertha::Raider::Application calls on every engine MCP server before a raid.

tool_names

The names the model sees (mcp__KEY__TOOL), in the server's order.

original_name

my $name = $server->original_name('mcp__github__create_issue');   # create_issue

The tool's name on the server, or undef for a name it does not offer.

list_tools

my $tools = await $server->list_tools;

The tools under their prefixed names (after the start; empty unless connected), each a copy of the server's definition. While the server's answer to a notifications/tools/list_changed is being fetched, it waits for it.

call_tool

my $result = await $server->call_tool( 'mcp__github__create_issue', \%args );

Calls the tool by its original name, bounded by timeout_ms of the "spec". Fails when the server is not connected, the name is unknown, the server answers with a JSON-RPC error, the process is gone or the time is up (MCP tool call timed out after N s).

limit_content

my $content = $server->limit_content( $result->{content}, $is_error );

The content blocks of a tool result with their text held to "max_output_tokens" (four characters count as a token, as a rough estimate): text over the limit is cut and a note block says how much there was and how to raise the limit. A result with an image block is passed on as it is, as Claude Code does not cut it either. An error result whose text is over about 11000 characters keeps its first and last 5000, with a marker between them that says how many characters were removed (Claude Code's rule). Claude Code saves the whole text to a file and names the path; raider keeps no such file and cuts.

has_prompts

has_resources

True when the connected server announced the prompts / resources capability.

list_prompts_f

my $prompts = await $server->list_prompts_f;   # [ { name, description, arguments => [ { name, required } ] } ]

The server's prompts, listed on first use and again after notifications/prompts/list_changed. Empty unless connected; a failing prompts/list is an empty list.

get_prompt_f

my $prompt = await $server->get_prompt_f( 'pr_review', { number => '456' } );
# { description => ..., messages => [ { role => 'user', content => { type => 'text', text => ... } } ] }

prompts/get, with the same call timeout as a tool. Fails when the server is not connected or answers with an error.

list_resources_f

my $resources = await $server->list_resources_f;   # [ { uri, name, mimeType, description } ]

The server's resources (resources/list), cached like the prompts; ui:// resources and those of media type text/html;profile=mcp-app are left out (Claude Code does the same).

read_resource_f

my $contents = await $server->read_resource_f('file:///a');   # [ { uri, mimeType, text | blob } ]

resources/read.

stop_f

await $server->stop_f;

Stops the server: a stdio process as "stop_f" in Langertha::Raider::MCP::Stdio, an HTTP session with DELETE. Status stopped.

kill_now

Kills a stdio process at once.

SEE ALSO

SUPPORT

Issues

Please report bugs and feature requests at https://github.com/Getty/langertha-raider/issues.

IRC

Join #langertha on irc.perl.org or message Getty directly.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

https://raudssus.de/