Skip to content

Repository files navigation

RustySkills

RustySkills is a Rust CLI and MCP server for managing agent skills. It stores skills in a local SQLite database, returns SKILL.md content during activation, and recreates support files in a per-session load root when a skill is used.

The same tool service backs the CLI, stdio MCP server, and Streamable HTTP MCP server:

  • list searches the local SQLite database.
  • search queries skills.sh without writing local state.
  • get ingests one skill from a package spec, GitHub repository, generic HTTPS git repository, or local path.
  • activate returns the primary SKILL.md and prepares support files.
  • remove deletes an exact local skill record and can optionally purge loaded support files.

Quick Start

Build and run the local stdio MCP server:

cargo build --release
./target/release/rustyskills serve

Install the binary from the working tree:

cargo install --path .
rustyskills --help

Run the shared Rust checks:

cargo fmt --check
cargo test -- --test-threads=1
cargo clippy --all-targets -- -D warnings

For a local HTTP smoke test, run the Streamable HTTP server on loopback:

rustyskills serve-http \
  --bind 127.0.0.1:8000 \
  --path /mcp \
  --allowed-host 127.0.0.1:8000

Then verify the health endpoint:

curl -fsS http://127.0.0.1:8000/healthz

Runtime Modes

CLI

Use the CLI for direct local administration:

rustyskills list "docx"
rustyskills search "docx"
rustyskills get owner/repo@skill
rustyskills get --github-url https://github.com/owner/repo --skill-name skill
rustyskills get --git-url https://git.example.org/owner/repo --skill-name skill
rustyskills get --local-path ./path/to/repo-or-skill
rustyskills activate skill
rustyskills remove skill --dry-run
rustyskills remove skill --purge-loaded

get accepts exactly one source mode:

Source CLI form Notes
Package spec owner/repo@skill Clones https://github.com/owner/repo.git and derives the selected skill from the spec.
GitHub URL --github-url https://github.com/owner/repo Use --skill-name when the repository has more than one SKILL.md.
Generic git URL --git-url https://git.example.org/owner/repo HTTPS only, without credentials, query, or fragment.
Local path --local-path ./path The path can be a single skill directory or a repository containing skills.

Stdio

Use stdio mode when an MCP client launches RustySkills as a local process. It opens no network port and must have writable, persistent paths for both the SQLite database and activated support files (RUSTYSKILLS_DB and RUSTYSKILLS_LOAD_ROOT).

cargo build --release
RUSTYSKILLS_DB="$PWD/data/skills.db" \
RUSTYSKILLS_LOAD_ROOT="$PWD/data/agents" \
  ./target/release/rustyskills serve

Register that exact release binary with Codex:

codex mcp add rustyskills \
  --env "RUSTYSKILLS_DB=$PWD/data/skills.db" \
  --env "RUSTYSKILLS_LOAD_ROOT=$PWD/data/agents" \
  -- "$(pwd)/target/release/rustyskills" serve
codex mcp list --json

Run the container in stdio mode with stdin kept open (-i); the named volumes retain the required database and load-root state between client launches:

docker run --rm -i \
  -v rustyskills-db-volume:/data/rustyskills \
  -v rustyskills-agents-volume:/data/agents \
  rustyskills:local serve

The command uses the container defaults of /data/rustyskills/skills.db and /data/agents. Mount equivalent writable paths and set the two environment variables explicitly when you use different locations.

MCP Over Streamable HTTP

Use HTTP mode for long-running processes, containers, and reverse proxy deployments:

rustyskills serve-http

Default HTTP behavior:

Setting Default
Bind address 0.0.0.0:8000
MCP endpoint /mcp
Health endpoint /healthz
Session mode stateful
Response framing Streamable HTTP/SSE framing unless JSON response mode is enabled

Useful flags:

rustyskills serve-http \
  --bind 127.0.0.1:8000 \
  --path /mcp \
  --allowed-host 127.0.0.1:8000 \
  --allowed-origin https://mcp.example.org

HTTP options can also come from environment variables. Use explicit allowed hosts in deployed HTTP mode; --allow-any-host and RUSTYSKILLS_HTTP_ALLOW_ANY_HOST=true are intended only for trusted proxy setups that already enforce the host policy.

Skill Ingestion

RustySkills discovers SKILL.md files in the source root, skills/, .agents/skills/, and .codex/skills/. If a source contains multiple skills, the caller must provide an exact skill_name unless the package spec already selects it.

During ingestion, RustySkills:

  • requires YAML frontmatter with non-empty name and description
  • validates skill names and support-file paths
  • stores the primary SKILL.md separately from support files
  • stores support-file content in SQLite with zstd compression
  • excludes VCS and common build/cache directories such as .git, target, node_modules, .cache, and __pycache__
  • rewrites known hardcoded skill-root paths in text support files to ${RUSTYSKILLS_SKILL_DIR} so they can be rebound at activation time

Remote search and git-backed get operations can be disabled or password-gated with configuration.

Activation

Activation does not execute support files. It returns the selected skill's SKILL.md to the caller and prepares support files for the current session.

In local mode, activate extracts support files under the configured load root:

~/.agents/<skill_name>/

Local activation is replacement-based: stale files for that skill are removed, the primary SKILL.md is not written into the support directory, and text files containing ${RUSTYSKILLS_SKILL_DIR} are rebound to the absolute support path. Activation uses a lock and journal under .rustyskills/activation/ in the load root so interrupted replacements can be recovered on the next activation for the same skill.

In remote mode, activate does not write support files on the RustySkills host. For skills with support files, it returns the SKILL.md plus a short-lived installer URL and command:

curl -fsSL 'https://mcp.example.org/rustyskills-install/<token>/skill-install.sh' | sh

Run the returned command on the MCP client and treat a non-zero exit status as an incomplete activation. The installer route is token-authenticated and is separate from the MCP route, which lets reverse proxies protect MCP traffic without also requiring the MCP bearer token for support-file downloads.

Legacy bundle output remains available for older automation:

rustyskills activate skill --bundle-output > /tmp/skill.bundle.json
rustyskills install-bundle --bundle /tmp/skill.bundle.json

Configuration

RustySkills loads configuration from:

  1. process environment variables
  2. an optional .env file beside the rustyskills binary
  3. built-in defaults

Process environment values override .env values. Docker -e flags and Compose environment: entries therefore take precedence over a colocated .env file.

Copy .env.example beside the installed binary when you want file-based native configuration:

cp .env.example "$(dirname "$(command -v rustyskills)")/.env"

Storage

Variable Default Purpose
RUSTYSKILLS_DB unset Full SQLite database path. Overrides directory and filename settings.
RUSTYSKILLS_DATABASE_DIRECTORY $HOME/.local/share/rustyskills/ Directory containing the SQLite database.
RUSTYSKILLS_DATABASE_NAME skills.db Database filename inside the database directory.
RUSTYSKILLS_LOAD_ROOT $HOME/.agents Root directory for activated support files.

Tool Controls

Variable Default Purpose
RUSTYSKILLS_DISABLE_DOWNLOADS false Disables search and hides it from MCP tool listings.
RUSTYSKILLS_DISABLE_INSTALLS false Disables get and hides it from MCP tool listings.
RUSTYSKILLS_DISABLE_REMOVAL false Disables remove and hides it from MCP tool listings.
RUSTYSKILLS_DESTRUCTIVE_PASSWORD empty Requires a per-call password for search, get, and remove.
RUSTYSKILLS_REMOVAL_PASSWORD empty Requires a dedicated password for remove; takes precedence over the destructive password for removal.

CLI password example:

rustyskills get owner/repo@skill --password "$RUSTYSKILLS_DESTRUCTIVE_PASSWORD"
rustyskills remove skill --password "$RUSTYSKILLS_REMOVAL_PASSWORD"

MCP callers pass the same values in the tool call password field.

Result Counts

Variable Default Range Purpose
RUSTYSKILLS_LIST_COUNT_DEFAULT 10 2..=30 Default result count for list when the caller omits limit.
RUSTYSKILLS_SEARCH_COUNT_DEFAULT 5 2..=30 Default result count for search when the caller omits limit.

HTTP

Variable Default Purpose
RUSTYSKILLS_HTTP_BIND 0.0.0.0:8000 Listen address for serve-http.
RUSTYSKILLS_HTTP_PATH /mcp MCP Streamable HTTP endpoint path. Must be non-root and begin with /.
RUSTYSKILLS_HTTP_ALLOWED_HOSTS unset Comma-separated allowed Host headers.
RUSTYSKILLS_HTTP_ALLOW_ANY_HOST false Disables host header checks when true.
RUSTYSKILLS_HTTP_ALLOWED_ORIGINS unset Comma-separated allowed browser Origin headers.
RUSTYSKILLS_HTTP_ALLOW_ANY_ORIGIN false Disables origin checks when true.
RUSTYSKILLS_HTTP_STATEFUL true Uses stateful Streamable HTTP sessions when true.
RUSTYSKILLS_HTTP_JSON_RESPONSE false Requests JSON responses where RMCP supports them.

Remote Activation

Variable Default Purpose
RUSTYSKILLS_REMOTE_MODE false Returns a client-side installer instead of extracting support files on the RustySkills host.
RUSTYSKILLS_REMOTE_MODE_URL unset Public external base URL used in installer commands. Required in remote mode for skills with support files.
RUSTYSKILLS_REMOTE_INSTALL_PATH /rustyskills-install Public HTTP path for installer scripts and support archives.
RUSTYSKILLS_REMOTE_INSTALL_TTL_SECS 900 Installer token lifetime in seconds.

RUSTYSKILLS_REMOTE_MODE_URL must be an HTTPS URL with no credentials, query, or fragment. Loopback http://127.0.0.1:<port> is accepted for local tests.

Docker

The included Dockerfile builds the release binary with Cargo and copies it into a Debian slim runtime image with ca-certificates, curl, and git. The image starts rustyskills serve-http by default.

Build and run the image locally:

docker build -t rustyskills:local .
docker run --rm \
  -p 127.0.0.1:18080:8000 \
  -e RUSTYSKILLS_HTTP_ALLOWED_HOSTS=127.0.0.1:18080,localhost:18080 \
  -v rustyskills-db-volume:/data/rustyskills \
  -v rustyskills-agents-volume:/data/agents \
  rustyskills:local

Container defaults:

Variable Value
RUSTYSKILLS_DATABASE_DIRECTORY /data/rustyskills
RUSTYSKILLS_DATABASE_NAME skills.db
RUSTYSKILLS_LOAD_ROOT /data/agents
RUSTYSKILLS_HTTP_BIND 0.0.0.0:8000
RUSTYSKILLS_HTTP_PATH /mcp
RUSTYSKILLS_REMOTE_INSTALL_PATH /rustyskills-install
RUSTYSKILLS_REMOTE_INSTALL_TTL_SECS 900

Check the container:

curl -fsS http://127.0.0.1:18080/healthz

Docker Compose

The provided compose.yaml builds the local image, publishes it on loopback, and persists the database and activation load root in named volumes:

docker compose up -d --build
docker compose ps
docker compose logs -f rustyskills

Compose defaults:

Setting Default
Host port 18080
Container port 8000
External MCP path /rustyskills
Allowed hosts mcp.phrk.org,localhost,127.0.0.1:18080
Remote mode false
Remote installer path /rustyskills-install

Override the common local settings at launch:

RUSTYSKILLS_HOST_PORT=18111 \
RUSTYSKILLS_HTTP_PATH=/mcp \
RUSTYSKILLS_HTTP_ALLOWED_HOSTS=127.0.0.1:18111,localhost:18111 \
docker compose up -d --build

For reverse proxy deployments, bind the container to loopback, configure RUSTYSKILLS_HTTP_ALLOWED_HOSTS for the proxy behavior, and proxy the installer path separately from the MCP path. If MCP requests are bearer-authenticated at the proxy, do not require that bearer token on the installer path; the generated installer URL carries its own temporary token.

Local Development

Common commands:

cargo check --all-features
cargo test --all-features -- --test-threads=1
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings

Focused test suites:

cargo test --test cli
cargo test --test mcp
cargo test --test http -- --nocapture
cargo test --test storage

The storage and activation tests use SQLite and temporary directories. If tests hit intermittent filesystem or SQLite errors on a busy temporary directory, rerun with single-threaded tests:

cargo test -- --test-threads=1

Docker remote-installer e2e coverage lives in scripts/docker-e2e.sh:

scripts/docker-e2e.sh

The script builds a Docker image, runs HTTP mode in remote activation mode, ingests a fixture skill through MCP, fetches the generated installer script, and verifies support files are installed into a client-side load root.

Release Workflow

The Forgejo workflow in .forgejo/workflows/release.yml runs on pushes to main that touch source, tests, the README, .env.example, Cargo files, or the workflow itself.

The workflow:

  1. installs Rust formatting and linting components in a Rust container
  2. clones the pushed source and fetches tags
  3. runs cargo fmt --check, cargo check --all-features, cargo test --all-features -- --test-threads=1, and cargo clippy --all-targets --all-features -- -D warnings
  4. increments the patch number in VERSION
  5. builds the release binary
  6. packages the binary with VERSION, README.md, and .env.example
  7. commits the new VERSION with [skip ci], tags the release, creates a Forgejo release, and uploads the archive, checksum, and changelog

src/lib.rs exposes the runtime version from VERSION. Cargo.toml remains the Cargo package manifest and currently marks the crate as unpublished.

Do not commit .env files, bearer tokens, API tokens, or deployment-only credentials.

About

RustySkills is a Rust MCP server and CLI for managing agent skills. It stores skills in one SQLite database, compresses SKILL.md and support files with zstd, and activates support files into a scratch directory for the current agent session.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages