ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/AnyEvent-MCP/lib/AnyEvent/MCP.pm
Revision: 1.22
Committed: Wed Oct 14 08:27:45 2026 UTC (4 weeks, 3 days ago) by root
Branch: MAIN
Changes since 1.21: +4 -26 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     AnyEvent::MCP - Model Context Protocol client/server support
4    
5     =head1 SYNOPSIS
6    
7 root 1.15 use AnyEvent::MCP::Server::Simple;
8 root 1.1
9 root 1.21 my %options = mcp_getoptions ...;
10     mcp_tool ...;
11     mcp_start %options;
12 root 1.1
13     or use the object interface directly:
14    
15 root 1.6 use AnyEvent::MCP::Server ();
16 root 1.1
17     my $server = AnyEvent::MCP::Server->new (...);
18    
19     =head1 DESCRIPTION
20    
21 root 1.21 AnyEvent::MCP provides event-driven Model Context Protocol client and server
22     support. There are three starting points:
23    
24     =over
25    
26     =item Use the included servers without writing Perl
27    
28     L<AnyEvent::MCP::Intro> walks through installation, a first filesystem
29     request, client connections, configuration, permissions and combining
30     servers. Start there if you want the tools to work on your projects.
31    
32     =item Write a small MCP server
33 root 1.17
34 root 1.21 L<AnyEvent::MCP::Server::Simple/TUTORIAL> builds a complete server,
35     calls it, and adds options, replies, permission checks and asynchronous
36     work. Simple handles transport startup and the event loop.
37    
38     =item Embed MCP in an application
39    
40     The modules below expose tool registries, protocol dispatch, requests and
41     transports separately, for applications that need to control their own
42     connections or lifetime.
43    
44     =back
45 root 1.1
46     The usual way to implement a small MCP server is
47     L<AnyEvent::MCP::Server::Simple>, which provides a function-based interface and
48     creates the underlying server objects when a transport such as stdio or HTTP is
49     started.
50    
51     The lower-level object interfaces are intended for applications that need to
52     manage MCP connections, servers or reusable tool registries directly.
53    
54     =head2 MODULES
55    
56 root 1.7 =over
57 root 1.1
58     =item L<AnyEvent::MCP::Server::Simple>
59    
60     Function-based interface for implementing ordinary MCP servers.
61    
62     =item L<AnyEvent::MCP::Server>
63    
64     Object-oriented MCP server interface.
65    
66     =item L<AnyEvent::MCP::Client>
67    
68     Object-oriented MCP client interface.
69    
70     =item L<AnyEvent::MCP::Server::Toolset>
71    
72     Reusable registry of MCP tool declarations.
73    
74     =item L<AnyEvent::MCP::Server::Permissions>
75    
76 root 1.21 Action authorization and sandbox-grant handling for MCP servers.
77 root 1.1
78     =back
79    
80 root 1.4 C<mcp-multibroker> combines downstream HTTP and stdio servers under tool-name
81     prefixes. It uses the same client, server and permission helper facilities,
82     including modern input forwarding and local permission prompts for legacy
83     callers. Run C<mcp-multibroker --help> for command-line syntax; its manual
84     describes authentication and configuration.
85    
86 root 1.16 The interfaces are still being designed. L<AnyEvent::MCP::Intro> explains
87     the installed programs; module-specific POD describes their Perl APIs.
88 root 1.9
89 root 1.16 =head1 TUTORIAL
90 root 1.9
91 root 1.21 For using the servers, see L<AnyEvent::MCP::Intro>. It includes a working
92     first connection, a complete configuration example, saved permissions,
93     multibroker setup and troubleshooting.
94    
95     For writing a server, see L<AnyEvent::MCP::Server::Simple/TUTORIAL>.
96     The tutorial starts with a complete echo server and a client that prints
97     its reply, then develops argument validation, command-line options,
98     result representations, permission retries and cancellable asynchronous
99     work. The Simple function reference follows it.
100    
101     For custom dispatch and reusable tool registries, see
102     L<AnyEvent::MCP::Server::Toolset/REQUEST AND TOOL WORKFLOW> and
103     L<AnyEvent::MCP::Server>. The client modules describe calling servers
104     from your own application.
105 root 1.9
106 root 1.1
107 root 1.2 =head1 GLOBAL CONFIGURATION
108    
109     The shared per-user configuration is C<$XDG_CONFIG_HOME/anyevent-mcp/global.ini>,
110     or C<$HOME/.config/anyevent-mcp/global.ini> when XDG_CONFIG_HOME is unset.
111     L<AnyEvent::MCP::Config> loads and holds it; loading this module does not access
112     configuration files.
113    
114     The file is UTF-8 and uses sections with single-line C<key = value> pairs.
115     Values are unquoted strings. Blank lines and whole-line comments beginning
116     with C<#> or C<;> are ignored; inline comment characters remain part of the
117     value. Later occurrences of a key replace earlier ones in the same section.
118    
119     [global]
120     secret = ...128 hexadecimal digits...
121    
122 root 1.18 [instance example]
123 root 1.11 bind = 127.0.0.1:31740
124 root 1.13 tokens = first-token,second-token
125 root 1.2
126 root 1.21 [program mcp-fs]
127     mount.opt = ro:/opt
128 root 1.2
129     Section names are unrestricted. Application-specific sections, including
130     exports, utility paths and user settings, are documented by their applications.
131    
132     C<[global]> holds C<secret>, a 64-byte master key encoded as 128 hexadecimal
133     digits. The file must be owned by the current user, with no group or other
134     permissions. A missing secret is generated automatically on first configuration
135     access and saved when possible. Existing secrets are never replaced implicitly.
136     An invalid secret is an error.
137    
138 root 1.22 Applications can derive their own purpose-specific secrets with
139     L<AnyEvent::MCP::Config/derive_secret>, using the cached global configuration
140     or an explicitly loaded configuration object.
141    
142 root 1.2 If saving fails, Config warns and retains the secret in memory. Credentials
143     then remain stable only for that process; other processes cannot derive the
144     same credentials. If randomness is unavailable, supply a securely generated
145     secret in the file.
146    
147 root 1.18 C<[instance NAME]> holds named-server settings.
148 root 1.11 HTTP-capable executables use C<--name NAME> to select these settings, with
149     the executable basename as the default name. Names consist of an ASCII
150     letter or underscore followed by ASCII letters, digits, underscores or
151     hyphens. Their C<--bind ADDRESS[:PORT]> overrides the configured C<bind>.
152     The default bind address is IPv4 loopback with a port derived from the name
153     and master secret in the range 16384..32767; C<:0> requests an ephemeral
154 root 1.12 port. C<--print-url> prints the URL using the first accepted token without
155     listening. Named server settings also include C<path>, C<url_host>,
156     C<url_scheme> and C<tokens>. C<tokens> is a comma-separated list of
157     URL-safe strings. Repeated C<--token TOKEN> options override this list.
158     All accepted tokens work in the URL path or Authorization bearer header.
159     Without configured tokens, one server-specific token is derived.
160 root 1.11
161 root 1.12 C<mcp_start> resolves named settings and requires the selected token for
162     HTTP requests. Explicit tokens replace the derived default.
163 root 1.11 The low-level HTTP transport does not resolve configuration itself.
164 root 1.21 Simple's stdio startup does not access global HTTP configuration.
165     Applications can still load it for their own settings; C<mcp-fs>, for
166     example, reads trusted mount and executable settings even in stdio mode.
167     Browser origins are permitted by default, but must still pass MCP authentication.
168 root 1.3
169 root 1.12 C<[client NAME]> supplies HTTP client defaults: C<url>, C<bearer> and
170     C<timeout>. Clients consult it only when explicitly given a C<name>.
171 root 1.21 An unnamed HTTP client and the low-level HTTP server do not read global
172     configuration. C<mcp_start> resolves a default name and does read it.
173 root 1.3 Explicit constructor arguments take precedence over named defaults.
174    
175     =head1 ENVIRONMENT
176    
177     Project-specific environment variables use the C<PERL_ANYEVENT_MCP_> prefix.
178     C<PERL_ANYEVENT_MCP_MULTIBROKER_PROTOCOL=stdio> selects stdio in Simple.
179 root 1.11 C<PERL_ANYEVENT_MCP_MULTIBROKER_ALIAS> names a multibroker routing prefix;
180     it does not change the service-instance name.
181 root 1.3 C<PERL_ANYEVENT_MCP_PERMISSION_HELPER> selects the external asking executable.
182 root 1.21 C<PERL_ANYEVENT_MCP_PERMISSION_ASK> selects the permission engine's
183     asking default: C<helper>, C<auto> or C<mcp>. See
184     L<AnyEvent::MCP::Intro/Choose where to ask> for the behavior and client
185     requirements.
186 root 1.3 Standard C<HOME> and C<XDG_CONFIG_HOME> keep their standard names.
187 root 1.2
188     =head1 FUNCTIONS
189    
190 root 1.6 =over
191    
192 root 1.10 =item $class = AnyEvent::MCP::json_class
193    
194     Returns C<JSON::XS> if available, otherwise C<JSON::PP>. The caller
195     configures a new coder instance as needed.
196    
197 root 1.8 =item $bytes = AnyEvent::MCP::utf8enc $text
198    
199     Return a UTF-8 byte string for C<$text>, leaving the argument unchanged.
200    
201     =item $text = AnyEvent::MCP::utf8dec $bytes
202    
203     Decode a UTF-8 byte string into characters, dying on invalid UTF-8.
204     The argument is left unchanged. For intentional in-place conversions,
205     use Perl's C<utf8::encode> or C<utf8::decode> directly.
206    
207 root 1.6 =item $bytes = AnyEvent::MCP::random_bytes $length
208 root 1.2
209     Returns exactly C<$length> random bytes. Tries C</dev/urandom>, then an external
210     C<openssl rand> command. No additional random-number module is required.
211     Dies if neither source supplies the requested bytes successfully.
212     C<$length> must be a nonnegative integer; zero returns an empty string.
213    
214 root 1.6 =back
215    
216     =head2 PATCH UTILITIES
217    
218 root 1.7 Since applying patches to files is a common task for MCP servers, the
219 root 1.6 following utility functions can be used to ease that job. Together, they
220 root 1.7 define a common patch format (unified diff or a simplified version of it,
221     with fewer requirements to make it easier for the LLM to generate), helpers
222 root 1.6 to apply the patch and a helper to verify if the patch was applied
223     with sufficient precision.
224    
225 root 1.7 Use C<apply_udiff> to normalize, apply and validate a patch in one operation.
226     It writes a separate output file; the caller checks freshness and metadata
227     and commits the result.
228    
229     For an application-specific applier, the workflow is:
230 root 1.6
231     =over
232    
233     =item 1. AnyEvent::MCP::normalize_udiff
234    
235 root 1.7 Returns a unified diff for GNU patch and the permitted hunk offsets.
236    
237     =item 2. Apply the normalized diff
238 root 1.6
239 root 1.7 Run GNU patch with fuzz zero and capture its verbose output.
240 root 1.6
241 root 1.7 =item 3. AnyEvent::MCP::check_patch_output
242    
243     Checks the process status and output against the permitted offsets.
244     C<parse_patch_output> is available separately when only parsing is needed.
245 root 1.5
246 root 1.6 =back
247    
248 root 1.5 =head3 Input format
249    
250 root 1.6 The patch format accepted by normalize_udiff is an extension to the
251     (essentially undocumented) unified diff format.
252    
253 root 1.5 In a hunk header, only the old start C<-N> matters. It is the old-file line
254     of the first hunk body line, including context, not necessarily the first
255     changed line. Counts and the new-file position are ignored and recomputed.
256     Both ordinary headers and the abbreviated form are accepted:
257    
258     @@ -42,99 +123,99 @@
259     @@ -42 @@
260    
261     Optional text after the closing C<@@> is preserved. File headers and other
262     text before the first hunk are preserved; a diff must describe one file.
263    
264     Hunk body lines use the usual prefixes: a space for unchanged context,
265     C<-> for removed lines and C<+> for added lines. The
266     C<\ No newline at end of file> marker is preserved and counts neither side.
267     Trailing empty lines outside the final hunk are discarded.
268    
269 root 1.7 Pure insertion is permitted only at the beginning, using old start C<0>.
270     For example, C<@@ -0 @@> with one added line becomes C<@@ -0,0 +1,1 @@>.
271     An empty old-side range names the line before the insertion: C<-0,0>
272     means before line 1, whereas C<-1,0> would mean after line 1 and is rejected
273     here. Nonempty ranges use one-based line numbers.
274    
275 root 1.5 An insertion elsewhere needs old-side context. Empty hunks are rejected.
276     New positions include the cumulative line-count changes of preceding hunks.
277    
278     =head3 Applying the result
279    
280 root 1.6 The C<normalize_udiff> function does not apply the patch, check file
281     contents or guard against concurrent edits. The caller must supply those
282     checks, if required.
283 root 1.5
284     Context must match exactly: apply with fuzz zero. For each hunk,
285     C<$max_offset> permits relocation by at most the number of distinct old-side
286     lines minus one, or zero for a pure insertion. Repeated old-side lines do
287     not increase this allowance. The applier must enforce these limits; an
288     unrestricted search for matching context can select the wrong occurrence.
289    
290     For live Vim buffers, also require the expected changedtick to match.
291     File-based users need an equivalent check before replacing the file.
292    
293 root 1.6 To work around GNU patch's handling of asymmetric context, the
294     first context line of each hunk is rewritten as an identity
295     removal/addition. The line contents and old/new line counts are
296     unchanged. This is normalization for the patch applier, not a relaxation
297     of context matching.
298 root 1.5
299 root 1.6 =over
300    
301     =item ($diff, $max_offset) = AnyEvent::MCP::normalize_udiff $diff
302    
303     Normalizes a relaxed, single-file unified diff into ordinary unified diff
304     syntax. Returns the normalized C<$diff> and an array reference C<$max_offset>
305     containing the permitted whole-hunk relocation for each hunk, in order.
306     Dies on malformed hunks or when no hunks are found.
307    
308     Here is a suitable description for the diff format, e.g. in a tool
309     definition:
310    
311     Apply a unified diff to one regular file. Hunk counts are recomputed,
312     fuzz is disabled, whole-hunk relocation is limited per hunk by distinct
313     old-side lines. Pure insertion is allowed only at the beginning;
314     elsewhere include old-side context.
315    
316     =item $cv = AnyEvent::MCP::apply_udiff $input, $output, $diff
317    
318     Asynchronously applies the relaxed C<$diff> to the input file C<$input>,
319     writing a separate output file C<$output>. C<$diff> must contain bytes;
320     callers working with characters must encode them for the file themselves.
321    
322     Normalizes the diff, runs GNU patch using C<AnyEvent::Util::run_cmd>, and
323     validates its verbose output with C<check_patch_output>. The command uses
324     batch/forward mode, binary mode and fuzz zero. No shell is involved.
325    
326     C<$output> must not exist. It is reserved with mode 0600; patch runs in its
327     directory so incidental temporary files stay there. Input and output paths
328     are made absolute before starting the command. These must be trusted paths
329     in a directory controlled by the caller.
330    
331     Returns a condition variable. Its C<recv> returns the validated hunk array
332     reference, or throws on command/application failure. Setup failures throw
333     synchronously. Failed or partial output is removed. C<$input> is unchanged.
334    
335     The caller owns committing the successful output and removing it when no
336     longer needed. Freshness checks, metadata preservation, atomic replacement
337     and application-specific permissions also belong to the caller.
338    
339     For your reference, the exact options used are:
340    
341     --verbose --batch --forward --binary --fuzz=0
342     --no-backup-if-mismatch
343     --reject-file=- --output=... -- <inputfile>
344    
345     =item $parsed = AnyEvent::MCP::parse_patch_output $output
346 root 1.5
347     Parses C<LC_ALL=C> GNU patch C<--verbose> output without judging whether
348     the application is acceptable. C<$parsed> has two fields:
349    
350 root 1.6 =over
351 root 1.5
352     =item hunks => [$hunk...]
353    
354     Hunk records in output order. Each record contains C<hunk> (the one-based
355     number), C<success>, C<line> (the reported application line), C<fuzz> and
356     the signed C<offset>. Omitted fuzz and offset are zero. Failed and ignored
357     hunks have false C<success>.
358    
359     =item unparsed => [$line...]
360    
361     Lines not recognized as hunk results or GNU patch's verbose framing.
362     The parser preserves them for callers to interpret or reject.
363    
364     =back
365    
366     Use verbose output: ordinary patch output omits exact-position successes,
367     so it cannot account for every hunk.
368    
369 root 1.6 =item $hunks = AnyEvent::MCP::check_patch_output $status, $output, [$max_offset...]
370 root 1.5
371     Parses C<$output> and validates a complete application. C<$status> is the
372     raw process wait status, as returned by C<waitpid> or
373     C<AnyEvent::Util::run_cmd>, not just the exit code.
374    
375     Requires status zero, no unrecognized output, exactly the expected hunks
376     in numbered order, successful application, fuzz zero and each signed offset
377     within its corresponding absolute limit. C<[$max_offset...]> is the array
378     reference returned by C<normalize_udiff>. At least one hunk is required.
379    
380     Returns the parsed hunk array reference on success; otherwise dies with a
381     diagnostic. This is also suitable for callers running patch inside their
382     own sandbox.
383    
384 root 1.6 =back
385 root 1.5
386 root 1.1 =cut
387    
388     package AnyEvent::MCP;
389    
390     use common::sense;
391    
392     our $VERSION = '0.0';
393    
394 root 1.10 sub json_class {
395     our $JSON_CLASS ||= eval { require JSON::XS; "JSON::XS" }
396     || do { require JSON::PP; "JSON::PP" };
397     }
398    
399 root 1.8 sub utf8enc($) {
400     my $text = shift;
401    
402     utf8::encode $text;
403     $text
404     }
405    
406     sub utf8dec($) {
407     my $bytes = shift;
408    
409     utf8::decode $bytes
410     or die "invalid UTF-8\n";
411     $bytes
412     }
413    
414 root 1.2 sub random_bytes($) {
415     my ($length) = @_;
416    
417 root 1.19 defined $length && $length =~ /\A[0-9]+\z/
418     or die "Invalid random byte count\n";
419 root 1.2 return "" unless $length;
420    
421     my $bytes = "";
422    
423     if (open my $fh, "<", "/dev/urandom") {
424     binmode $fh;
425     while (length $bytes < $length) {
426     read ($fh, $bytes, $length - length ($bytes), length $bytes)
427     or last;
428     }
429     close $fh;
430    
431     return $bytes if length $bytes == $length;
432     }
433    
434     if (open my $fh, "-|", "openssl", "rand", $length) {
435     binmode $fh;
436     local $/;
437     $bytes = <$fh>;
438     my $success = close $fh;
439    
440     return $bytes
441     if $success && defined $bytes && length $bytes == $length;
442     }
443    
444     die "Cannot obtain $length random bytes from /dev/urandom or openssl rand\n";
445     }
446    
447 root 1.5 sub normalize_udiff {
448     my $diff = shift;
449     my @line = split /\n/, $diff, -1;
450     my $delta = 0;
451     my $hunks = 0;
452     my @max_offset;
453    
454     pop @line while @line and !length $line[-1];
455    
456     for (my $i = 0; $i < @line; ++$i) {
457     next unless $line[$i] =~ /^@@ /;
458    
459     $line[$i] =~ /^@@ -(\d+)(?:,\d+)?(?: \+\d+(?:,\d+)?)? @@(.*)$/
460     or die "normalize_udiff: bad hunk header '$line[$i]'\n";
461    
462     my ($old_start, $suffix) = ($1, $2);
463     my ($old_count, $new_count) = (0, 0);
464     my %old_line;
465     my $j = $i + 1;
466    
467     # GNU patch mishandles hunks with more prefix than suffix context.
468     # Make the first context line an identity change without losing it.
469     # see https://unix.stackexchange.com/questions/414542/does-patch-work-with-asymmetric-context
470     splice @line, $i + 1, 1, "-$1", "+$1"
471     if $line[$i + 1] =~ /^ (.*)\z/;
472    
473     for (; $j < @line; ++$j) {
474     last if $line[$j] =~ /^@@ /;
475    
476     my $tag = substr $line[$j], 0, 1;
477    
478     if ($tag eq " ") {
479     ++$old_count;
480     ++$new_count;
481     $old_line{substr $line[$j], 1} = 1;
482     } elsif ($tag eq "-") {
483     ++$old_count;
484     $old_line{substr $line[$j], 1} = 1;
485     } elsif ($tag eq "+") {
486     ++$new_count;
487     } elsif ($line[$j] eq "\\ No newline at end of file") {
488     # Marker belongs to the preceding body line and counts neither side.
489     } else {
490     die "normalize_udiff: bad hunk line '$line[$j]'\n";
491     }
492     }
493    
494     $old_count or $new_count
495     or die "normalize_udiff: empty hunk at old line $old_start\n";
496    
497     $old_count or !$old_start
498     or die "normalize_udiff: hunk " . ($hunks + 1)
499     . " needs old-side context for insertion after old line $old_start\n";
500    
501     my $new_start = $old_start + $delta;
502     ++$new_start unless $old_count;
503     --$new_start unless $new_count;
504    
505     $new_start >= 0
506     or die "normalize_udiff: invalid new line $new_start\n";
507    
508     $line[$i] = sprintf "@@ -%d,%d +%d,%d @@%s",
509     $old_start, $old_count, $new_start, $new_count, $suffix;
510    
511     $delta += $new_count - $old_count;
512     ++$hunks;
513     my $max_offset = keys %old_line;
514     --$max_offset
515     if $max_offset;
516     push @max_offset, $max_offset;
517     $i = $j - 1;
518     }
519    
520     $hunks
521     or die "normalize_udiff: no hunks\n";
522    
523     (join ("\n", @line, ""), \@max_offset)
524     }
525    
526     sub parse_patch_output {
527     my ($output) = @_;
528     my (@hunks, @unparsed);
529    
530     for (split /\n/, $output) {
531     if (/^Hunk #(\d+) (succeeded|FAILED|ignored) at (-?\d+)(?: with fuzz (\d+))?(?: \(offset (-?\d+) lines?\))?\.\z/) {
532     push @hunks, {
533     hunk => 0 + $1,
534     success => $2 eq "succeeded",
535     line => 0 + $3,
536     fuzz => 0 + $4,
537     offset => 0 + $5,
538     };
539     } elsif (!length
540     || /^Hmm\.\.\. +Looks like a unified diff to me\.\.\.\z/
541     || /^The text leading up to this was:\z/
542     || /^-+\z/ || /^\|/
543     || /^patching file / || /^checking file /
544     || /^Using Plan [AB]\.\.\.\z/ || /^done\z/) {
545     # GNU patch --verbose framing, not hunk results.
546     } else {
547     push @unparsed, $_;
548     }
549     }
550    
551     { hunks => \@hunks, unparsed => \@unparsed }
552     }
553    
554     sub check_patch_output {
555     my ($status, $output, $max_offset) = @_;
556     my $parsed = parse_patch_output $output;
557     my $hunks = $parsed->{hunks};
558    
559     die "patch failed (wait status $status):\n$output"
560     if $status;
561     die "patch: unrecognized output:\n" . (join "\n", @{$parsed->{unparsed}}) . "\n"
562     if @{$parsed->{unparsed}};
563     die "patch: expected " . @$max_offset . " hunks, got " . @$hunks . "\n"
564     unless @$hunks == @$max_offset && @$hunks;
565    
566     for my $i (0 .. $#$hunks) {
567     my $hunk = $hunks->[$i];
568    
569     die "patch: unexpected hunk number $hunk->{hunk}\n"
570     unless $hunk->{hunk} == $i + 1;
571     die "patch: hunk $hunk->{hunk} failed\n"
572     unless $hunk->{success};
573     die "patch: hunk $hunk->{hunk} used fuzz $hunk->{fuzz}\n"
574     if $hunk->{fuzz};
575     die "patch: hunk $hunk->{hunk} offset $hunk->{offset} exceeds $max_offset->[$i]\n"
576     if abs $hunk->{offset} > $max_offset->[$i];
577     }
578    
579     $hunks
580     }
581    
582     sub apply_udiff {
583     my ($input, $output, $diff) = @_;
584     my ($normalized, $max_offset) = normalize_udiff $diff;
585    
586     require AnyEvent::Util;
587     require File::Spec;
588     require File::Basename;
589     require Fcntl;
590    
591     $input = File::Spec->rel2abs ($input);
592     $output = File::Spec->rel2abs ($output);
593     my $directory = File::Basename::dirname ($output);
594    
595     sysopen my $fh, $output, Fcntl::O_WRONLY () | Fcntl::O_CREAT () | Fcntl::O_EXCL (), 0600
596     or die "$output: $!\n";
597     close $fh or do { my $error = $!; unlink $output; die "$output: $error\n" };
598    
599     my $cv = AE::cv ();
600     my $log;
601     my $process = eval {
602     AnyEvent::Util::run_cmd ([
603     "patch", "--verbose", "--batch", "--forward", "--binary", "--fuzz=0",
604     "--no-backup-if-mismatch", "--reject-file=-", "--output=$output", "--", $input,
605     ],
606     "<" => \$normalized,
607     ">" => \$log,
608     close_all => 1,
609     on_prepare => sub {
610     $ENV{LC_ALL} = "C";
611     chdir $directory or die "$directory: $!\n";
612     defined POSIX::dup2 (1, 2) or die "dup2: $!\n";
613     },
614     );
615     };
616    
617     if ($@) {
618     my $error = $@;
619     unlink $output;
620     die $error;
621     }
622    
623     $process->cb (sub {
624     my $hunks;
625     eval { $hunks = check_patch_output $_[0]->recv, $log, $max_offset; 1 } or do {
626     my $error = $@;
627     unlink $output;
628     $cv->croak ($error);
629     return;
630     };
631    
632     $cv->send ($hunks);
633     });
634    
635     $cv
636     }
637    
638 root 1.1 sub _clean_text($) {
639     my ($text) = @_;
640    
641     my ($prefix) = (join "\n", grep /\S/, split /\n/, $text)
642     =~ /\A([ \t]*)[^\n]*(?:\n\1[^\n]*)*\z/;
643    
644     $text =~ s/^\Q$prefix\E//mg;
645     $text =~ s/\A\s+//;
646     $text =~ s/\s+\z//;
647     $text
648     }
649    
650     package AnyEvent::MCP::Base;
651    
652     sub new {
653     my ($class, %kv) = @_;
654 root 1.19
655 root 1.1 bless \%kv, $class
656     }
657    
658     sub event {
659     my ($self, $event, @args) = @_;
660 root 1.19
661 root 1.1 ($self->{"on_$event"} or return)->($self, @args)
662     }
663    
664     =head1 SEE ALSO
665    
666 root 1.16 L<AnyEvent::MCP::Intro>, L<AnyEvent::MCP::Server::Simple>,
667 root 1.1 L<AnyEvent>, L<https://modelcontextprotocol.io/>
668    
669     =head1 AUTHOR
670    
671     The main author of this module is OpenAI ChatGPT.
672    
673     Additional guidance received by:
674    
675     Marc Lehmann <schmorp@schmorp.de>
676     http://home.schmorp.de/
677    
678     =cut
679    
680     1
681