EXPERIMENTAL: This project is under active development. Features may be unstable and are subject to change.
"In Linux, everything is a file. In RYE, everything is data."
The last MCP your agent will ever need.
Built on Lilux, a microkernel providing pure execution primitives, RYE (RYE Your Execution) solves the core interoperability failure in AI: workflows, tools, and context are trapped inside individual projects and agents. Every environment understands what to do (“scrape a website”), but the how — the steps, tools, and learned knowledge — is locked away, forcing you to point your agent to other projects, copy over workflows and context manually or relearn and rebuild everything from scratch. This doesn't scale.
RYE breaks that loop. Agents can search, load, and execute across tools, workflows and knowledge from a shared local or online registry. Enabling full reuseability without manual setup and clean operations across project contexts.
Your agent becomes self-sufficient.
AI agents are powerful, but they're trapped.
- No self-service: Your agent can't pull the web scraper it used yesterday.
- No portability: Workflows that exist in Project A are invisible to Project B.
- No consistency: Every project reinvents the same tools because agents can't share.
- No discoverability: Your agent rebuilds what already exists because it can't search.
RYE empowers your agent to solve this itself through MCP tooling.
RYE exposes just four primary tools that your agent uses to interact with the entire system:
| Tool | Purpose | Example |
|---|---|---|
search |
Discover items across your project, user space, and system | search(item_type="directive", query="scraping") |
load |
Pull content from the registry or local stores | load(item_type="tool", item_id="acme/web/scraper") |
sign |
Cryptographically sign items with Ed25519 | sign(item_type="directive", item_id="web/scraper") |
execute |
Run directives, tools, or knowledge items | execute(item_type="tool", item_id="web/scraper", url="...") |
These four primitives compose everything your agent does. Search finds what's available, load pulls it in, sign establishes trust, and execute runs it.
"Once you understand the physics, then you can play the game. RYE aims to be the maintainer of those physics."
Every AI system has the same underlying mechanics:
- Prompts need to be understood
- Tools need to be discovered and executed
- Workflows need to be orchestrated
- Permissions need to be enforced
- Costs need to be tracked
- State needs to be managed
RYE encodes these fundamentals as data, not implementation. The physics are consistent. Only the execution environment changes.
RYE treats three types of items as structured data:
Declarative workflows stored as XML-embedded markdown:
<directive name="web_scraper" version="1.0.0">
<metadata>
<description>Extract data from websites</description>
<category>automation</category>
</metadata>
<inputs>
<param name="target_url" type="string" required="true" />
<param name="selector" type="string" required="true" />
</inputs>
<process>
<step name="load_knowledge">
<execute item_type="knowledge" item_id="patterns/scraping/web-scraping-best-practices" />
</step>
<step name="fetch">
<execute item_type="tool" item_id="web/scraper">
<param name="url" value="${inputs.target_url}" />
<param name="selector" value="${inputs.selector}" />
</execute>
</step>
</process>
<outputs>
<param name="data" type="array" />
</outputs>
</directive>XML provides:
- Schema validation for workflow structure
- Clear parameter typing and explicit nesting
- Standardized parsing across languages
- Explicit declarations that LLMs can generate reliably
While more verbose than YAML, XML's rigidity ensures directives are machine-readable without ambiguity. This matters when your agent is generating and modifying workflows programmatically.
Python, JavaScript, YAML, or Bash scripts with metadata headers:
__version__ = "1.0.0"
__executor_id__ = "python_runtime"
__catergory__ = "web"
__tool_type__ = "automation"
async def main(**kwargs):
url = kwargs.get('url')
selector = kwargs.get('selector')
# Scraping logic
return {"data": []}Structured learnings with YAML frontmatter:
---
id: web-scraping-best-practices
category: patterns/scraping
tags: [scraping, css-selectors, rate-limiting]
---
# Web Scraping Best Practices
## CSS Selector Tips
- Use specific class names over generic tags
- Avoid brittle XPath expressions
- Handle dynamic content with wait strategies
## Rate Limiting
- Respect robots.txt
- Add delays between requests
- Use exponential backoff on 429 responsesAll three live in your project's .ai/ directory:
.ai/
├── directives/ # XML workflows
├── tools/ # Executable scripts
└── knowledge/ # Patterns & learnings
Every directive, tool, and knowledge item carries an Ed25519 signature. Unsigned items are rejected — no execution, no loading, no bypass.
<!-- rye:signed:2026-02-11T00:00:00Z:a1b2c3d4...:base64url_sig:0a3f9b2c1d4e5f67 -->
# Directive NameSignature format: rye:signed:TIMESTAMP:CONTENT_HASH:ED25519_SIG:PUBKEY_FP
What each field provides:
- Tamper detection: SHA256 content hash recomputed and compared on every use
- Provenance: Ed25519 signature binds content to a specific keypair
- Trust: Signing key must exist in the local trust store (
{USER_SPACE}/.ai/trusted_keys/) - Registry attestation: Registry re-signs on push, appending
|provider@username
How it works:
- Keypair auto-generated on first sign (
{USER_SPACE}/.ai/keys/) - Own public key auto-trusted — self-signed items verify locally with no setup
- Registry public key pinned on first pull (Trust On First Use)
- Every tool in the execution chain is verified before running
Every tool in RYE ultimately runs through two execution primitives provided by Lilux:
Executes shell commands in isolated environments:
- Process isolation
- Environment variable injection
- Timeout and signal handling
- Output capture (stdout/stderr)
Makes HTTP requests with retry logic:
- Authentication header management
- Timeout and retry configuration
- Response streaming
- Error handling
That's it. All tool execution reduces to these two primitives.
RYE builds a recursive chain for every tool execution:
Your Tool (e.g., web/scraper.py)
↓ __executor_id__ = "python_runtime"
Python Runtime
↓ __executor_id__ = "subprocess"
Subprocess Primitive
↓ executor_id = None (is primitive)
Execute via Lilux
The chain resolves recursively: each layer's __executor_id__ points to the next layer until reaching a primitive (where executor_id = None). Common chain lengths are 2-4 layers, but there's no fixed limit.
Each layer validates before passing down. The chain ensures compatibility between tool requirements and execution environment.
Every resolved chain generates a lockfile:
{
"lockfile_version": 1,
"generated_at": "2026-02-11T00:00:00+00:00",
"root": {
"tool_id": "web/scraper",
"version": "1.0.0",
"integrity": "a1b2c3d4e5f6...64 hex chars"
},
"resolved_chain": [
{
"item_id": "web/scraper",
"space": "project",
"tool_type": "python",
"executor_id": "rye/core/runtimes/python_runtime",
"integrity": "a1b2c3d4...64 hex chars"
},
{
"item_id": "rye/core/runtimes/python_runtime",
"space": "system",
"tool_type": "runtime",
"executor_id": "rye/core/primitives/subprocess",
"integrity": "e5f6a7b8...64 hex chars"
},
{
"item_id": "rye/core/primitives/subprocess",
"space": "system",
"tool_type": "primitive",
"executor_id": null,
"integrity": "c9d0e1f2...64 hex chars"
}
]
}Why lockfiles matter:
- Reproducibility: Same chain every time
- Security: Verify each layer hasn't changed
- Caching: Skip resolution if lockfile matches
- Audit: Complete execution trace
RYE doesn't just provide tools—it absorbs other MCP servers and turns them into data-driven tools that your agent can discover and use on its own.
Your agent connects to MCP servers itself. No manual configuration. No copying endpoints. Your agent discovers tools and starts using them immediately:
Your agent says:
"discover mcp server https://api.context7.com/mcp"
"list mcp servers"
"execute mcp context7 search for authentication patterns"
Your agent executes these as tool calls:
| You Say | RYE Tool Call |
|---|---|
discover mcp server X |
execute(item_type="tool", item_id="rye/mcp/manager", action="add", url=X) |
list mcp servers |
execute(item_type="tool", item_id="rye/mcp/manager", action="list") |
execute mcp X search for Y |
execute(item_type="tool", item_id="mcp/X/search", query=Y) |
Example: Mapping "rye discover mcp server https://api.context7.com/mcp"
The LLM translates this to:
{
"item_type": "tool",
"item_id": "rye/mcp/manager",
"action": "add",
"name": "context7",
"transport": "http",
"url": "https://api.context7.com/mcp"
}Execution Chain for MCP Tools
When you execute mcp/context7/search, the chain resolves based on transport:
HTTP Transport:
mcp/context7/search (MCP tool YAML config)
↓ __executor_id__ = "rye/core/runtimes/mcp_http_runtime"
rye/core/runtimes/mcp_http_runtime (Layer 2 runtime)
↓ __executor_id__ = "rye/core/primitives/subprocess"
rye/core/primitives/subprocess (Layer 1 primitive)
↓ executor_id = None
Execute via Lilux
stdio Transport:
mcp/local-script/analyze (MCP tool YAML config)
↓ __executor_id__ = "rye/core/runtimes/mcp_stdio_runtime"
rye/core/runtimes/mcp_stdio_runtime (Layer 2 runtime)
↓ __executor_id__ = "rye/core/primitives/subprocess"
rye/core/primitives/subprocess (Layer 1 primitive)
↓ executor_id = None
Execute via Lilux
The MCP runtimes handle the transport-specific connection logic, then delegate to the subprocess primitive for actual execution.
- Discovery: RYE connects to external MCP servers via stdio, HTTP (Streamable), or SSE
- Conversion: Each discovered tool becomes a YAML configuration in
.ai/tools/mcp/ - Data-Driven Execution: External tools are executed through RYE's chain resolution
- Environment Integration: Auto-loads
.envfiles for API keys and configuration
.ai/tools/mcp/
├── servers/
│ └── context7.yaml # Server configuration
└── context7/
├── search.yaml # Discovered tool configs
├── resolve.yaml
└── get-library.yaml
Want your agent to understand your own phrasing? Add a command dispatch table to your AGENTS.md:
## COMMAND DISPATCH TABLE
| User Says | Run Directive | With Inputs |
| ---------------------------- | ---------------------- | ----------- |
| `connect to X mcp` | `rye/mcp/discover` | `url=X` |
| `what mcp servers do i have` | `rye/mcp/list_servers` | none |
| `search X using mcp Y` | `mcp/Y/search` | `query=X` |Now your agent understands your language while still executing RYE directives.
- stdio: Local CLI tools (e.g., custom scripts)
- HTTP: Remote services with Streamable HTTP transport
- SSE: Legacy SSE transport support
Environment variables are automatically resolved from:
- User space:
USER_SPACE/.env(default:~/.ai/.env) - Project:
./.ai/.env,./.env,./.env.local
This means RYE becomes a universal MCP client. One connection point. Every MCP server accessible as data-driven tools.
The registry is a centralized, cryptographically-signed store that your agent can access directly:
- Self-service discovery: Your agent finds solutions itself
- On-demand pulling: Your agent downloads workflows it needs, when it needs them
- Validation: Items are Ed25519-signed and verified against a local trust store
- Sharing: Push your workflows, pull others—programmatically
Identity model: namespace/category/name
The registry is just another data-driven tool your agent can invoke:
Your agent says:
"Search the registry for web scraper directive"
"Pull acme/web/scraper from registry"
"Push my web/scraper tool to registry"
These become tool calls your agent executes:
| You Prompt | RYE Tool Call |
|---|---|
search registry for X |
execute(item_type="tool", item_id="rye/registry/registry", action="search", query=X) |
pull X from registry |
execute(item_type="tool", item_id="rye/registry/registry", action="pull", item_id=X) |
push X to registry |
execute(item_type="tool", item_id="rye/registry/registry", action="push", item_path=X) |
Example: Your agent searching for a scraper
{
"item_type": "tool",
"item_id": "rye/registry/registry",
"action": "search",
"query": "web scraper",
"item_type": "directive"
}No more waiting for you to set up tools. Your agent helps itself.
Ed25519 signatures for provenance. Server-side validation. Local integrity verification via trust store.
RYE doesn't just orchestrate—it runs agents inside the MCP.
Spawn isolated LLM threads with scoped permissions:
<directive name="parallel_scraping" version="1.0.0">
<metadata>
<description>Run web scraping in parallel using spawned threads</description>
<category>automation</category>
<author>rye</author>
<model tier="haiku" id="claude-3-5-haiku-20241022">Fast scraping with fallback</model>
<limits max_turns="8" max_tokens="4096" />
<permissions>
<execute>
<tool>rye.agent.threads.spawn_thread</tool>
<tool>rye.file-system.fs_read</tool>
</execute>
<search>
<directive>web/*</directive>
</search>
<load>
<directive>*</directive>
</load>
</permissions>
<cost>
<context estimated_usage="medium" turns="8" spawn_threshold="3">
4096
</context>
<duration>300</duration>
<spend currency="USD">5.00</spend>
</cost>
<hooks>
<hook>
<when>cost.current > cost.limit * 0.8</when>
<execute item_type="directive">notify-cost-threshold</execute>
</hook>
<hook>
<when>error.type == "permission_denied"</when>
<execute item_type="directive">request-elevated-permissions</execute>
<inputs>
<requested_resource>${error.resource}</requested_resource>
</inputs>
</hook>
</hooks>
</metadata>
<process>
<step name="spawn_scraper">
<execute item_type="tool" item_id="rye.agent.threads.spawn_thread">
<param name="thread_id" value="scraping-worker" />
<param name="directive_name" value="web/scraper" />
</execute>
</step>
</process>
</directive>Every thread runs with:
- Cost tracking: tokens, turns, duration, spend
- Permission enforcement: CapabilityToken validation
- Hook-based error handling: retry, skip, fail, abort
- Checkpoint control: pause, resume, inspect
Hooks let you respond to events during execution:
<hooks>
<hook>
<when>cost.current > cost.limit * 0.8</when>
<execute item_type="directive">notify-cost-threshold</execute>
</hook>
<hook>
<when>error.type == "permission_denied"</when>
<execute item_type="directive">request-elevated-permissions</execute>
</hook>
</hooks>Available context: cost.current, cost.limit, error.type, loop_count, directive.name
RYE uses a unified capability system where permissions declared in directives become runtime capability tokens.
Permissions are declared hierarchically in directive XML:
<permissions>
<execute>
<tool>rye.file-system.fs_read</tool>
<tool>web/*</tool>
</execute>
<search>
<directive>automation/*</directive>
</search>
<load>
<tool>rye.shell.*</tool>
</load>
</permissions>When a directive runs, its permissions are converted to capability strings:
rye.execute.tool.rye.file-system.fs_readrye.execute.tool.web.*rye.search.directive.automation.*rye.load.tool.rye.shell.*
These capabilities become a CapabilityToken for the thread:
# Token created from directive permissions
cap_token = CapabilityToken(
capabilities={
"rye.execute.tool.rye.file-system.fs_read",
"rye.execute.tool.web.*",
"rye.search.directive.automation.*"
},
thread_id="scraping-worker"
)
# Every tool call validates against the token
# Violations raise PermissionDeniedEach spawned agent gets its own capability token derived from its directive's permissions, plus:
- Cost budget (tokens, turns, duration, spend)
- Resource limits
- Scoped capability access
Ed25519 signatures are enforced at every boundary:
- Directive verified before thread execution
- Every tool in the execution chain verified before running
- Registry items verified against TOFU-pinned registry key on pull
- Unsigned or tampered items rejected with
IntegrityError
Important: RYE is not a replacement for LangChain, OpenAI Assistants, or CrewAI (although it can and I believe it will). It's a complementary layer that solves a specific problem those SDKs aren't designed for.
| Aspect | Traditional SDKs | RYE | Trade-off |
|---|---|---|---|
| Maturity | Battle-tested, 2+ years production | Experimental, active development | Stability vs Innovation |
| Ecosystem | 2000+ integrations, rich community | MCP-based, emerging | Breadth vs Protocol focus |
| Discovery Model | Package registries (human searches) | Agent-searchable registry | Manual import vs Autonomous discovery |
| Portability | Python/JS runtime | Any MCP client | Language lock-in vs Protocol lock-in |
| Security Model | Runtime policies + package signing | Ed25519 + capabilities | Implicit vs Explicit |
| Agent Autonomy | Human configures tools | Agent discovers & loads | Manual vs Autonomous |
| Observability | LangSmith, W&B, mature tooling | Deterministic tool calls, traceable chains | Rich dashboards vs Data transparency |
Traditional SDKs excel at:
- Building agent applications with mature frameworks
- Integration breadth - thousands of pre-built connectors
- Developer experience - debugging tools, IDE support, type safety
- Production hardening - retry logic, rate limiting, error recovery
- Community support - Stack Overflow, tutorials, consultants
RYE excels at:
- Sharing agent capabilities across projects and environments
- Agent-native discovery - workflows searchable and loadable by agents
- Cross-environment portability - same directives in Claude Desktop, Cursor, or any MCP client
- Explicit provenance - cryptographic signatures for trust without central authority
- Agent autonomy - your agent pulls what it needs without manual setup
Traditional SDKs are great when:
- Building a chatbot with LangSmith observability and production monitoring
- Integrating with 50+ data sources using pre-built LangChain connectors
- You need TypeScript type safety for your React frontend
- You're deploying to production and need battle-tested reliability
RYE is great when:
- You're building workflows across Opencode, Amp, Claude Code, Cursor, etc.
- Your agent needs to self-serve tools without you manually copying them between projects
- You want cryptographic proof of workflow provenance
- You're working in MCP-native environments
- You need the same capabilities available regardless of which AI tool you're using
Real scenario: You build a web scraper in Opencode for Project A. Next week, you start Project B in Claude Code. Your agent can't access the scraper from Project A—it's trapped in that project's code. You copy-paste the code, adjust imports, debug environment differences. A month later, you update the scraper in Project A. Now Project B is out of sync.
Traditional SDKs handle this with package managers, which work great—if a human is doing the importing. But your agent can't pip install the scraper it wrote last week. It can't search npm for "that authentication pattern we used before." It rebuilds from scratch every time.
RYE treats workflows as discoverable, cryptographically-signed data that agents can search, pull, and execute without human intervention.
Both traditional SDKs and RYE support stateless HTTP deployment:
LangChain with LangServe:
from fastapi import FastAPI
from langserve import add_routes
app = FastAPI()
add_routes(app, chain, path="/scrape") # Stateless HTTP endpointRYE with MCP wrapper:
from fastapi import FastAPI
from rye.server import rye_mcp_server
app = FastAPI()
@app.post("/api/scrape")
async def scrape(url: str, selector: str):
# Create an LLM thread to execute the directive
return await rye_mcp_server.execute(
item_type="tool",
item_id="rye/agent/threads/thread_directive",
directive_name="web/scraper",
inputs={"target_url": url, "selector": selector}
)Both are:
- Stateless ✓
- Scalable ✓
- Observable ✓
- Hot-swappable: LangChain (with dynamic loading), RYE (via registry) ✓
The difference is in workflow management: LangChain workflows live in code repositories. RYE workflows live in a searchable, agent-accessible registry.
Example: Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: rye-api
spec:
replicas: 3
template:
spec:
containers:
- name: rye-api
image: myapp/rye-api:latest
volumeMounts:
- name: directives
mountPath: /app/.ai/directives
volumes:
- name: directives
configMap:
name: workflow-directives
---
apiVersion: v1
kind: ConfigMap
metadata:
name: workflow-directives
data:
web/scraper.md: |
<!-- directive content -->
deployment/pipeline.md: |
<!-- directive content -->Update workflows by updating the ConfigMap. No container rebuilds. No downtime.
Deployment Difference: Traditional SDKs package workflows with the application (update via redeployment). RYE keeps workflows as external data (update via ConfigMap/registry without rebuilding containers).
Note: RYE is not yet published to PyPI. Install from source:
git clone https://github.com/leolilley/rye-os.git
cd rye-os
pip install -e lilux
pip install -e ryeOpencode (.opencode/mcp.json):
{
"mcpServers": {
"rye": {
"type": "local",
"command": ["/path/to/rye"],
"environment": {
"USER_SPACE": "~/.ai"
},
"enabled": true
}
}
}Amp, Claude Code, Cursor, or any MCP client: Configure MCP server path to rye.
"Give me a lever long enough and a fulcrum upon which to place it and I shall move the earth." — Archimedes
If AI is the lever, this is the fulcrum.
RYE doesn't just run tools—it captures the fundamental physics of AI execution as data. Once you encode workflows, tools, and knowledge as structured data, they become:
- Autonomous: Agents self-serve workflows from the registry
- Portable: Works in any MCP environment
- Composable: Chain directives together
- Verifiable: Ed25519 signatures with trust store
- Shareable: Registry-based distribution
- Secure: Capability-based permissions
This is the future of AI architecture. Once you see it, you can't go back.
MIT