|
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.
Raku MCP SDK provides comprehensive coverage of the MCP specification 2025-11-25.
See Gap Analysis for details.
| Feature | Status | Notes |
|---|---|---|
| JSON-RPC 2.0 | ✅ Done | Full message handling |
| Stdio Transport | ✅ Done | Production ready |
| Tools | ✅ Done | List, call, builder API, annotations |
| Resources | ✅ Done | List, read, builder API, annotations |
| Prompts | ✅ Done | List, get, builder API |
| Pagination | ✅ Done | Cursor-based for all list endpoints |
| Logging | ✅ Done | Server-side notifications, logging/setLevel, level filtering |
| Progress | ✅ Done | _meta.progressToken extraction, server notifications, client Supply |
| Cancellation | ✅ Done | Request cancellation with notifications |
| Resource subscriptions | ✅ Done | Subscribe, unsubscribe, update notifications |
| Roots | ✅ Done | Client roots, server list-roots |
| Sampling | ✅ Done | Full support with tools, toolChoice, includeContext, stopReason |
| HTTP Transport | ✅ Done | Full client/server with session management, SSE, resumption |
| Legacy SSE Transport | ✅ Done | Backwards-compatible HTTP+SSE transport (spec 2024-11-05) |
| Elicitation | ✅ Done | Form and URL modes with handler callbacks |
| Tasks (experimental) | ✅ Done | Async tool execution, status polling, cancellation |
| Extensions framework | ✅ Done (experimental) | Negotiation via experimental capabilities + extension method routing |
| Completion | ✅ Done | Prompt and resource autocomplete with handler registration |
| Tool output schemas | ✅ Done | outputSchema and structuredContent for structured results |
| Resource templates | ✅ Done | URI templates with pattern matching and builder API |
| Tool metadata | ✅ Done | Tool name validation (SEP-986), icons and title on Tool/Resource/Prompt/Implementation (SEP-973) |
| OAuth 2.1 | ✅ Done | PKCE, token management, server validation, metadata discovery, dynamic client registration, M2M client credentials, enterprise IdP (SEP-990) |
The Raku MCP SDK is currently in review by the community.
Planned and currently ongoing efforts:
make coverage or make coverage-report (see Makefile targets and Coverage below).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.
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.
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.
Raku preserves the spirit of Perl (“There Is More Than One Way To Do It”) but rebuilds the foundation entirely. Its core principles include:
Raku offers features that are often libraries or “hacks” in other languages as first-class citizens:
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.
# From zef
zef install MCP
# From source
git clone https://github.com/wkusnierczyk/raku-mcp-sdk
cd raku-mcp-sdk
zef install .
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;
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);
use MCP;
use MCP::Transport::StreamableHTTP;
# Connect to a remote MCP server over HTTP
my $client = Client.new(
info => Implementation.new(
name => 'my-client',
version => '1.0.0'
),
transport => StreamableHTTPClientTransport.new(
endpoint => 'http://localhost:8080/mcp'
),
);
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;
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.
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 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 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>")
}
);
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
| Target | Description | Notes |
|---|---|---|
| Primary targets | ||
all | Install deps, build, and test | Runs dependencies → build → test |
build | Validate and precompile modules | Runs validate then build-precompile |
build-precompile | Precompile the main module | Uses raku -Ilib -c lib/MCP.rakumod fallback |
test | Build and run tests | Depends on build |
install | Install module globally | Uses zef install . --/test |
| Validation and metadata | ||
validate | Validate META6.json and provides entries | Runs validate-meta and validate-provides |
validate-meta | Check required META6.json fields | Ensures name, version, description, provides |
validate-provides | Verify provides paths exist | Prints each resolved entry |
| Dependencies | ||
dependencies | Install runtime dependencies | zef install --deps-only . |
dependencies-dev | Install dev dependencies | Includes Prove6, Test::META, Mi6, Racoco |
dependencies-update | Update dependencies | Runs zef update and reinstalls project deps |
| Lint and formatting | ||
lint | Run syntax + META checks | Runs lint-syntax and lint-meta |
lint-syntax | Compile-check source files | Uses raku -Ilib -c |
lint-meta | Validate META6.json | Requires JSON::Fast |
format | Format guidance and whitespace scan | Non-destructive |
format-fix | Remove trailing whitespace | Applies to source + tests |
check | Run lint + tests | Equivalent to lint test |
| Testing and coverage | ||
test-verbose | Run tests with verbose output | Uses prove6 with --verbose |
test-file | Run a specific test file | FILE=t/01-types.rakutest |
test-quick | Run tests without build | Skips build |
coverage | Check coverage threshold (CI) | Uses Test::Coverage; fails if below threshold |
coverage-report | Generate coverage report (local) | Uses RaCoCo; produces HTML, JSON, and Markdown reports |
benchmark | Run performance benchmarks | Parsing, dispatch, concurrency |
| Documentation | ||
docs | Generate text docs into docs/ | Uses raku --doc=Text per module |
docs-serve | Serve docs (placeholder) | Not implemented |
architecture-diagram | Build architecture PNG | Renders architecture/architecture.mmd to architecture/architecture.png |
| Distribution and release | ||
dist | Create source tarball | Writes to dist/ |
release | Interactive release helper | Prompts for fez upload |
| Utilities and examples | ||
about | Show project info | Prints metadata from Makefile |
repl | Start REPL with project loaded | raku -Ilib -MMCP |
run-example | Run example by name | EXAMPLE=simple-server |
info | Show toolchain + stats | Raku/Zef/Prove versions |
list-modules | List module files | From lib/ |
list-tests | List test files | From t/ |
| Install/uninstall | ||
install-local | Install to home | Uses zef install . --to=home |
install-force | Force install | Uses zef install . --force-install |
uninstall | Uninstall module | zef uninstall MCP |
| CI helpers | ||
ci | CI pipeline | dependencies → lint → test |
ci-full | Full CI pipeline | dependencies-dev → lint → test → coverage |
| Version management | ||
version | Show or update project version | make TAG=1.2.3 version updates Makefile + META6.json + README (no tag) |
bump-patch | Patch bump | make bump-patch bumps to the next patch version (no tag) |
bump-minor | Minor bump | make bump-minor bumps to the next minor version (no tag) |
bump-major | Major bump | make bump-major bumps to the next major version (no tag) |
| Benchmarks and stress tests | ||
benchmark | Run performance benchmarks | Writes timestamped reports to bench/ |
stress | Run stress tests | Writes timestamped reports to bench/ |
| Cleaning | ||
clean | Remove build/coverage/dist | Runs clean-build/clean-coverage/clean-dist |
clean-build | Remove precomp/build dirs | Removes .precomp and .build |
clean-coverage | Remove coverage output | Removes *.rakucov, coverage-report/, and .racoco/ |
clean-dist | Remove tarballs/dist dir | Removes dist/ and *.tar.gz |
clean-all | Deep clean | Also removes docs build output |
| Variable | Description |
|---|---|
V=1 |
Enable verbose output |
NO_COLOR=1 |
Disable colored output |
FILE=<path> |
Specify file for test-file target |
EXAMPLE=<name> |
Specify example for run-example target |
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
The project uses two complementary coverage tools with separate make targets:
| Target | Tool | Purpose | Output |
|---|---|---|---|
make coverage |
Test::Coverage | Threshold check (CI) | Pass/fail TAP output |
make coverage-report |
RaCoCo | Detailed report (local) | HTML, JSON, and Markdown in coverage-report/ |
Important: The two tools measure coverage differently, so their reported percentages will not match.
Test::Coverage instruments at the statement level and is used in CI to enforce a minimum threshold.
RaCoCo instruments at the line level and produces detailed per-module reports for local analysis.
The CI workflow uses only make coverage; make coverage-report is for local use.
Install both with:
make dependencies-dev
Or individually:
zef install Test::Coverage # for make coverage
zef install App::RaCoCo # for make coverage-report
The project runs automated checks on every push and pull request via GitHub Actions.
CI workflow (ci.yml):
latest only.Release workflow (release.yml), triggered on version tags (v*.*.*):
META6.json version.fez.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
Contributions are welcome. You can:
Note
- Pushing directly to
mainis disallowed.- Merging into
mainwithout positive review and passing checks is disallowed.- Merging into
mainof stalled pull requests is disallowed.
You need to mergemaininto your branch, or rebase your branch ontomainbefore being able to merge intomain.- Merging into
mainwith 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.
A security review was conducted on the codebase. No critical vulnerabilities were found. The following defense-in-depth measures are implemented:
Additional areas reviewed with no issues found:
JSON::Fast (no code execution risk)User responsibility (by design for an SDK):
To report a security issue, please open a confidential issue or email the maintainer directly.
The SDK includes benchmarks and stress tests (make benchmark, make stress) that measure:
Reports are written as timestamped Markdown and JSON files to bench/.
MIT License - see the LICENSE file and the MIT License site.
Building this repository was supported by:
$ make about
Raku MCP SDK: Raku Implementation of the Model Context Protocol
├─ version: 0.33.6
├─ developer: mailto:waclaw.kusnierczyk@gmail.com
├─ source: https://github.com/wkusnierczyk/raku-mcp-sdk
└─ licence: MIT https://opensource.org/licenses/MIT