The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
An R package that documents its functions properly is already what
MCP is trying to wire together: tools (functions), descriptions
(.Rd files), and an invocation mechanism
(library()). corteza walks the .Rd tree at
session start and turns the exports into JSON-Schema tool definitions —
same shape the Anthropic / OpenAI / Moonshot APIs expect. No server, no
schema by hand, no protocol.
This vignette walks the setup with fortunes, quotes from the R-help archives. It returns a structured S3 object, and the demo is more fun than configuring a JSON parser.
Two steps. Total wiring: one config line.
install.packages("fortunes")Add a skill_packages entry to corteza’s config. Either
project-local at <cwd>/.corteza/config.json or global
at tools::R_user_dir("corteza", "config")/config.json:
{
"skill_packages": ["fortunes"]
}That’s it. The string form registers every export. For larger packages, the object form picks specific functions:
{
"skill_packages": [
{"package": "fortunes", "functions": ["fortune"]}
]
}corteza::chat()The startup banner shows the tool count climbing —
corteza::chat() reports 30 tools instead of
29. Then ask the agent to use it:
> Find a fortune by Brian Ripley about types or coercion.
You’ll see the progress hint fire as the agent invokes the tool:
[fortunes::fortune] author=Brian Ripley (8 lines)
The fortune object comes back with quote,
author, context, source, and
date fields, and the agent paraphrases or quotes from
there. Try variations:
fortune(showMatches = TRUE) to find anything about
NULL, then summarize the consensus.”The R community has 20,000 CRAN packages but not all of them work cleanly as agent skills. The shape that fits:
cat()s status updates or paints with
crayon:: pollutes the tool result.oops() or stop() for non-error
cases. If the function calls stop() because you’re
not in the right directory, it’s designed for a human at the console,
not a tool harness..Rd parameters that match
formals(). CRAN already enforces this with
R CMD check, so any current CRAN package qualifies.Counter-example: gitr wraps
system2("git", ...) cleanly enough but cat()s
colored output and calls oops() when not in a git repo.
Wrapping it as a skill needed utils::capture.output() and
grew the code instead of shrinking it. Built for humans, not for
plumbing.
The test: would you call this function from another function and trust the return value? If yes, it’s a candidate.
A live MCP server ships every tool’s JSON schema into the system
prompt at connect time. Twenty tools at ~400 tokens each is 8,000 tokens
of startup overhead before the agent has done anything. The CLI /
package-as-skill path pays roughly zero startup tax (corteza already has
bash, run_r, read_file baked in),
and lazy lookup via saber::pkg_help() costs ~200 tokens for
a tool the agent actually needs.
Same tool surface, different cost curve. corteza’s MCP server
(corteza::serve()) still exists for clients that need it
(Claude Code, Codex, etc), but it’s no longer the only path — the same
skill registry feeds both.
Rscript --vanilla -e 'saber::pkg_exports("fortunes")' and
Rscript --vanilla -e 'saber::pkg_help("fortune", "fortunes")'..R file in
<project>/.corteza/skills/ calling
register_skill_from_fn("name", my_fn). Loaded on every
session start, no installation required.These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.