Rand Stats

MCP

zef:wkusnierczyk
logo

Raku MCP SDK: A Raku (Perl 6) implementation of the Model Context Protocol (MCP) SDK.

Build MCP servers and clients in Raku to integrate with LLM applications like Claude Desktop, IDEs, and other AI tools.

Why Raku?
Raku is a expressive, multi-paradigm language with strong concurrency tools, a flexible type system, and modern Unicode support. It is well suited for building protocol-first infrastructure where clarity, safety, and rapid iteration matter.

Status

Raku MCP SDK provides comprehensive coverage of the MCP specification 2025-11-25.
See the Gap Analysis for details.

Implementation progress

FeatureStatusNotes
JSON-RPC 2.0✅ DoneFull message handling
Stdio Transport✅ DoneProduction ready
Tools✅ DoneList, call, builder API, annotations
Resources✅ DoneList, read, builder API, annotations
Prompts✅ DoneList, get, builder API
Pagination✅ DoneCursor-based for all list endpoints
Logging✅ DoneServer-side notifications, logging/setLevel, level filtering
Progress✅ Done_meta.progressToken extraction, server notifications, client Supply
Cancellation✅ DoneRequest cancellation with notifications
Resource subscriptions✅ DoneSubscribe, unsubscribe, update notifications
Roots✅ DoneClient roots, server list-roots
Sampling✅ DoneFull support with tools, toolChoice, includeContext, stopReason
HTTP Transport✅ DoneFull client/server with session management, SSE, resumption
Legacy SSE Transport✅ DoneBackwards-compatible HTTP+SSE transport (spec 2024-11-05)
Elicitation✅ DoneForm and URL modes with handler callbacks
Tasks (experimental)✅ DoneAsync tool execution, status polling, cancellation
Extensions framework✅ Done (experimental)Negotiation via experimental capabilities + extension method routing
Completion✅ DonePrompt and resource autocomplete with handler registration
Tool output schemas✅ DoneoutputSchema and structuredContent for structured results
Resource templates✅ DoneURI templates with pattern matching and builder API
Tool metadata✅ DoneTool name validation (SEP-986), icons and title on Tool/Resource/Prompt/Implementation (SEP-973)
OAuth 2.1✅ DonePKCE, token management, server validation, metadata discovery, dynamic client registration, M2M client credentials, enterprise IdP (SEP-990)

Table of contents

Introduction

MCP

Model Context Protocol (MCP) is an open standard that standardizes how AI models interact with external data and tools. Consider it a "USB-C port for AI applications", a universal interface that replaces the need to build custom connectors for every new data source or tool.

What is it for?

It solves the "m-by-n" integration problem. Instead of every AI application (Claude, ChatGPT, IDEs) needing a specific adapter for every data source (Postgres, Google Drive, Slack), they all speak one protocol.

Design principles

Current status

Raku

Raku (formerly Perl 6) is a high-level, multi-paradigm programming language optimized for expressiveness, modularity, and consistency. It is a specification-driven language (with Rakudo being the primary implementation) that encourages "programming as a human language"—allowing code to be written in a way that feels natural and context-aware rather than rigid and machine-like.

Design principles

Raku preserves the spirit of Perl ("There Is More Than One Way To Do It") but rebuilds the foundation entirely. Its core principles include:

What is outstanding? (Unique features)

Raku offers features that are often libraries or "hacks" in other languages as first-class citizens:

Raku for AI and MCP

Raku is a "sleeper" choice for specific AI domains, particularly those involving language and orchestration.

Suitability for AI:

Interfacing through MCP (Model Context Protocol): Raku is surprisingly well-architected for the Model Context Protocol (MCP), which connects LLMs to external tools and data.

Installation

# From zef
zef install MCP

# From source
git clone https://github.com/wkusnierczyk/raku-mcp-sdk
cd raku-mcp-sdk
zef install .

Quick start

Creating a server

use MCP;
use MCP::Server;
use MCP::Transport::Stdio;
use MCP::Types;

# Create server
my $server = MCP::Server::Server.new(
    info => MCP::Types::Implementation.new(
        name => 'my-server',
        version => '1.0.0'
    ),
    transport => MCP::Transport::Stdio::StdioTransport.new,
);

# Add a tool
$server.add-tool(
    name => 'greet',
    description => 'Greet someone by name',
    schema => {
        type => 'object',
        properties => {
            name => { type => 'string', description => 'Name to greet' }
        },
        required => ['name'],
    },
    handler => -> :%params {
        "Hello, %params<name>!"
    }
);

# Add a resource
$server.add-resource(
    uri => 'info://about',
    name => 'About',
    description => 'About this server',
    mimeType => 'text/plain',
    reader => { 'This is my MCP server!' }
);

# Start serving
await $server.serve;

Using the fluent builder API

use MCP::Server::Tool;

# Build tools with fluent API
my $calculator = tool()
    .name('add')
    .description('Add two numbers')
    .number-param('a', description => 'First number', :required)
    .number-param('b', description => 'Second number', :required)
    .annotations(title => 'Calculator', :readOnly, :idempotent)
    .handler(-> :%params { %params<a> + %params<b> })
    .build;

$server.add-tool($calculator);

Creating a client

use MCP;
use MCP::Client;
use MCP::Transport::Stdio;
use MCP::Types;

# Connect to an MCP server process
my $proc = Proc::Async.new('path/to/mcp-server');
my $client = MCP::Client::Client.new(
    info => MCP::Types::Implementation.new(
        name => 'my-client',
        version => '1.0.0'
    ),
    transport => MCP::Transport::Stdio::StdioTransport.new(
        input => $proc.stdout,
        output => $proc.stdin,
    ),
);

await $client.connect;

# List and call tools (with pagination support)
my $tools-result = await $client.list-tools;
for $tools-result<tools> -> $tool {
    say "Tool: $tool.name() - $tool.description()";
}
# Use $tools-result<nextCursor> for pagination if present

my $call-result = await $client.call-tool('greet', arguments => { name => 'World' });
say $call-result.content[0].text;  # "Hello, World!"

# Read resources
my @contents = await $client.read-resource('info://about');
say @contents[0].text;

Examples

The examples/ directory contains runnable, minimal reference implementations that demonstrate common MCP SDK patterns. Use them to quickly validate your environment, see how transports and handlers fit together, and copy a solid starting point for your own servers and clients.

To list available examples:

# execute without naming any example
make run-examples

Available examples:
  • advanced-client      # In-process loopback covering pagination, completions, roots, and elicitation
  • advanced-server      # Pagination, resource subscriptions, and cancellation
  • http-server          # Streamable HTTP server transport
  • oauth-server         # HTTP server with OAuth 2.1 token validation
  • sampling-client      # Client-side sampling handler
  • extensions           # Extension registration, negotiation, and method dispatch
  • simple-server        # Stdio server with tools, resources, and prompts

Run any example with:

# execute naming an example
make run-example EXAMPLE=advanced-server

→ Running example: advanced-server...

== Pagination ==
Page 1 tools: tool-beta, tool-epsilon
Page 1 nextCursor present: yes
Page 2 tools: tool-alpha, tool-delta
Page 2 nextCursor present: yes

== Resource Subscriptions ==
Subscribe result keys: 
Update notifications sent: 1
Last notification method: notifications/resources/updated

== Cancellation ==
Marked cancelled: yes
Responses sent after cancellation: 0

Done.

Features

Tools

Tools are functions that the LLM can call.

$server.add-tool(
    name => 'search',
    description => 'Search for something',
    schema => {
        type => 'object',
        properties => {
            query => { type => 'string' },
            limit => { type => 'integer', default => 10 },
        },
        required => ['query'],
    },
    handler => -> :%params {
        # Return string, Content object, or CallToolResult
        my @results = do-search(%params<query>, %params<limit>);
        @results.join("\n")
    }
);

Resources

Resources provide read-only data.

# Static resource
$server.add-resource(
    uri => 'config://app',
    name => 'App Config',
    mimeType => 'application/json',
    reader => { to-json(%config) }
);

# File-based resource
use MCP::Server::Resource;
$server.add-resource(file-resource('data.txt'.IO));

Prompts

Prompts are templated message workflows.

use MCP::Server::Prompt;

$server.add-prompt(
    name => 'summarize',
    description => 'Summarize content',
    arguments => [
        { name => 'content', required => True },
        { name => 'length', required => False },
    ],
    generator => -> :%params {
        my $length = %params<length> // 'medium';
        user-message("Please provide a $length summary of: %params<content>")
    }
);

Development

The project uses a comprehensive Makefile for development tasks:

make about       # Show project information
make all         # Full build: dependencies → build → test
make test        # Run test suite

Makefile Targets

TargetDescriptionNotes
Primary targets
allInstall deps, build, and testRuns dependencies → build → test
buildValidate and precompile modulesRuns validate then build-precompile
build-precompilePrecompile the main moduleUses raku -Ilib -c lib/MCP.rakumod fallback
testBuild and run testsDepends on build
installInstall module globallyUses zef install . --/test
Validation and metadata
validateValidate META6.json and provides entriesRuns validate-meta and validate-provides
validate-metaCheck required META6.json fieldsEnsures name, version, description, provides
validate-providesVerify provides paths existPrints each resolved entry
Dependencies
dependenciesInstall runtime dependencieszef install --deps-only .
dependencies-devInstall dev dependenciesIncludes Prove6, Test::META, Mi6, Racoco
dependencies-updateUpdate dependenciesRuns zef update and zef upgrade
Lint and formatting
lintRun syntax + META checksRuns lint-syntax and lint-meta
lint-syntaxCompile-check source filesUses raku -Ilib -c
lint-metaValidate META6.jsonRequires JSON::Fast
formatFormat guidance and whitespace scanNon-destructive
format-fixRemove trailing whitespaceApplies to source + tests
checkRun lint + testsEquivalent to lint test
Testing and coverage
test-verboseRun tests with verbose outputUses prove6 with --verbose
test-fileRun a specific test fileFILE=t/01-types.rakutest
test-quickRun tests without buildSkips build
coverageGenerate coverage reportHTML in coverage-report/report.html, raw data in .racoco/
Documentation
docsGenerate text docs into docs/Uses raku --doc=Text per module
docs-serveServe docs (placeholder)Not implemented
architecture-diagramBuild architecture PNGRenders architecture/architecture.mmd to architecture/architecture.png
Distribution and release
distCreate source tarballWrites to dist/
releaseInteractive release helperPrompts for fez upload
Utilities and examples
aboutShow project infoPrints metadata from Makefile
replStart REPL with project loadedraku -Ilib -MMCP
run-exampleRun example by nameEXAMPLE=simple-server
infoShow toolchain + statsRaku/Zef/Prove versions
list-modulesList module filesFrom lib/
list-testsList test filesFrom t/
Install/uninstall
install-localInstall to homeUses zef install . --to=home
install-forceForce installUses zef install . --force-install
uninstallUninstall modulezef uninstall MCP
CI helpers
ciCI pipelinedependencies → lint → test
ci-fullFull CI pipelinedependencies-dev → lint → test → coverage
Version management
versionShow or update project versionmake TAG=1.2.3 version updates Makefile + META6.json + README (no tag)
bump-patchPatch bumpmake bump-patch bumps to the next patch version (no tag)
bump-minorMinor bumpmake bump-minor bumps to the next minor version (no tag)
bump-majorMajor bumpmake bump-major bumps to the next major version (no tag)
Cleaning
cleanRemove build/coverage/distRuns clean-build/clean-coverage/clean-dist
clean-buildRemove precomp/build dirsRemoves .precomp and .build
clean-coverageRemove coverage outputRemoves .racoco and coverage-report
clean-distRemove tarballs/dist dirRemoves dist/ and *.tar.gz
clean-allDeep cleanAlso removes docs build output

Environment Variables

VariableDescription
V=1Enable verbose output
NO_COLOR=1Disable colored output
FILE=<path>Specify file for test-file target
EXAMPLE=<name>Specify example for run-example target

Versioning

Use make version to set an explicit version (no tag):

make TAG=0.10.0 version

Or use a positional argument:

make version 0.10.0

For quick bumps, use:

make bump-patch    # x.y.(z+1)
make bump-minor    # x.(y+1).0
make bump-major    # (x+1).0.0

Coverage Prerequisites

The coverage report uses RaCoCo. If racoco is not on your PATH, add the Raku site bin directory:

export PATH="$(brew --prefix rakudo-star)/share/perl6/site/bin:$PATH"

Then run:

make coverage
# report: coverage-report/report.html

Project structure

MCP/
├── MCP.rakumod                     # Main module, re-exports
├── MCP/
│   ├── Types.rakumod               # Protocol types
│   ├── JSONRPC.rakumod             # JSON-RPC 2.0
│   ├── Transport/
│   │   ├── Base.rakumod            # Transport role
│   │   ├── Stdio.rakumod           # Stdio transport
│   │   ├── StreamableHTTP.rakumod  # HTTP transport with SSE
│   │   └── SSE.rakumod             # Legacy SSE transport (2024-11-05)
│   ├── OAuth.rakumod               # OAuth 2.1 types, PKCE, exceptions
│   ├── OAuth/
│   │   ├── Client.rakumod          # Client-side OAuth flow
│   │   └── Server.rakumod          # Server-side token validation
│   ├── Server.rakumod              # Server implementation
│   ├── Server/
│   │   ├── Tool.rakumod            # Tool helpers
│   │   ├── Resource.rakumod        # Resource helpers
│   │   └── Prompt.rakumod          # Prompt helpers
│   └── Client.rakumod              # Client implementation

Contributing

Contributions are welcome. You can:

Note

  • Pushing directly to main is disallowed.
  • Merging into main without positive review and passing checks is disallowed.
  • Merging into main of stalled pull requests is disallowed.
    You need to merge main into your branch, or rebase your branch onto main before being able to merge into main.
  • Merging into main with the whole multi-commit history of your branch is disallowed.
    You can only squash-merge as one commit, with a detailed description of your changes.

License

MIT License - see the LICENSE file and the MIT License site.

References

Acknowledgments

Building this repository was supported by:

About

$ make about

Raku MCP SDK: Raku Implementation of the Model Context Protocol
├─ version:    0.28.3
├─ developer:  mailto:waclaw.kusnierczyk@gmail.com
├─ source:     https://github.com/wkusnierczyk/raku-mcp-sdk
└─ licence:    MIT https://opensource.org/licenses/MIT