Skip to content

loadSkillsWith — data skills, providers, filters

Rust · crate toolnexus · SPEC §3

pub fn load_skills_with(opts: LoadSkillsOptions) -> SkillSource
pub struct LoadSkillsOptions {
pub dirs: Vec<PathBuf>,
pub skills: Vec<SkillDef>,
pub filter: HashMap<String, bool>,
pub sample_limit: i64,
}

Supply skills as data or from a lazy provider, and narrow them per agent with an allowlist.

When skills come from somewhere other than disk (a database, a bundle), or you need an allowlist or a different <skill_files> sample cap.

Use this rather than writing temp files: SkillDef skills behave exactly like on-disk ones, including the byte-exact tool output.

1. Skills supplied as data, no filesystem involved

Section titled “1. Skills supplied as data, no filesystem involved”
use serde_json::json;
use toolnexus::ToolContext;
use toolnexus::skills::{LoadSkillsOptions, SkillDef, load_skills_with};
#[tokio::main]
async fn main() {
let src = load_skills_with(LoadSkillsOptions {
skills: vec![SkillDef {
name: "deploy".into(),
description: "Ship a release".into(),
content: "Run the release checklist.".into(),
..Default::default()
}],
..Default::default()
});
assert_eq!(src.skills.len(), 1);
assert!(src.skills.contains_key("deploy"));
let res = src.tool.call(json!({"name": "deploy"}), ToolContext::default()).await;
assert!(!res.is_error, "{}", res.output);
assert!(res.output.contains("Run the release checklist."));
println!("ok: {}", src.skills["deploy"].name);
}

2. The shared fixture, filtered down for one agent

Section titled “2. The shared fixture, filtered down for one agent”

Loaded from examples/skills, then narrowed via filter — the allowlist is per call, not global, so a second load_skills_with over the same directory can see a different set.

use std::collections::HashMap;
use std::path::Path;
use toolnexus::skills::{LoadSkillsOptions, load_skills_with};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let dir = Path::new(&std::env::var("TOOLNEXUS_REPO")?).join("examples/skills");
let all = load_skills_with(LoadSkillsOptions { dirs: vec![dir.clone()], ..Default::default() });
assert!(all.skills.contains_key("hello-world"));
// An allowlist naming a skill that doesn't exist filters everything out —
// it does not fall back to "all".
let narrowed = load_skills_with(LoadSkillsOptions {
dirs: vec![dir],
filter: HashMap::from([("some-other-skill".to_string(), true)]),
..Default::default()
});
assert!(narrowed.skills.is_empty());
println!("ok: unfiltered={} filtered={}", all.skills.len(), narrowed.skills.len());
Ok(())
}

3. Directories and data merged, with a sample cap

Section titled “3. Directories and data merged, with a sample cap”

dirs and skills combine into one catalog; sample_limit bounds how many sibling files the loader tool reports per skill (0 ⇒ default 10, -1 ⇒ omit the file list entirely).

use std::path::Path;
use serde_json::json;
use toolnexus::ToolContext;
use toolnexus::skills::{LoadSkillsOptions, SkillDef, load_skills_with};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let dir = Path::new(&std::env::var("TOOLNEXUS_REPO")?).join("examples/skills");
let src = load_skills_with(LoadSkillsOptions {
dirs: vec![dir],
skills: vec![SkillDef {
name: "from-data".into(),
description: "Supplied directly, not from disk".into(),
content: "Body.".into(),
..Default::default()
}],
sample_limit: -1, // omit <skill_files> entirely
..Default::default()
});
assert_eq!(src.skills.len(), 2, "fs + data");
let res = src.tool.call(json!({"name": "hello-world"}), ToolContext::default()).await;
assert!(!res.is_error, "{}", res.output);
assert!(!res.output.contains("<skill_files>"), "sample_limit -1 omits the file list");
println!("ok: {} skills, no file sampling", src.skills.len());
Ok(())
}
Field Type What it does
dirs Vec<PathBuf> Skill roots to glob for **/SKILL.md.
skills Vec<SkillDef> Skills supplied directly as data — no filesystem involved.
filter HashMap<String, bool> Per-agent allowlist. Empty ⇒ all; ≥1 true ⇒ allowlist; only-false ⇒ drop-list.
sample_limit i64 0 ⇒ default 10 sibling files sampled, n>0 ⇒ that cap, -1 ⇒ omit <skill_files>.

A lazy skill provider is a toolkit option, not a field here: ToolkitOptions.skill_provider.

  • load_skills — Glob a skills directory and expose one skill tool with progressive disclosure.
  • list_skills — Enumerate discovered skills and, crucially, the ones that were skipped and why.