MCPcopy Create free account
hub / github.com/AI45Lab/Code / load_tools

Function load_tools

core/src/config/agent_dir.rs:274–362  ·  view source on GitHub ↗
(dir: &Path)

Source from the content-addressed store, hash-verified

272}
273
274fn load_tools(dir: &Path) -> Result<Vec<ToolSpec>> {
275 let mut out = Vec::new();
276 let mut seen = std::collections::HashSet::new();
277 for path in md_files(dir, &["md"])? {
278 let content = std::fs::read_to_string(&path)
279 .map_err(|e| CodeError::Context(format!("read {}: {e}", path.display())))?;
280 let (front, body) = split_frontmatter(&content);
281 let front = front.ok_or_else(|| {
282 CodeError::Context(format!(
283 "tool {} has no YAML frontmatter (need `kind:`)",
284 path.display()
285 ))
286 })?;
287 let meta: ToolFront = serde_yaml::from_str(&front)
288 .map_err(|e| CodeError::Context(format!("tool {} frontmatter: {e}", path.display())))?;
289 let spec = match meta.kind.as_str() {
290 "mcp" => {
291 // The frontmatter's flat fields (transport/command/args/url/…) plus
292 // `name` deserialize straight into McpServerConfig; the `kind` key is
293 // ignored by its lenient Deserialize.
294 let cfg: McpServerConfig = serde_yaml::from_str(&front).map_err(|e| {
295 CodeError::Context(format!(
296 "tool {} (kind=mcp) is not a valid MCP server config: {e}",
297 path.display()
298 ))
299 })?;
300 ToolSpec::Mcp(cfg)
301 }
302 "script" => {
303 let meta: ScriptFront = serde_yaml::from_str(&front).map_err(|e| {
304 CodeError::Context(format!(
305 "tool {} (kind=script) frontmatter: {e}",
306 path.display()
307 ))
308 })?;
309 // Fail closed at load (not at first call), consistent with the
310 // runtime guards the script runs under: a non-JS source, a path
311 // that escapes the workspace, or an out-of-range sandbox limit are
312 // all directory-load errors rather than first-call surprises.
313 let p = meta.path.to_string_lossy();
314 if !(p.ends_with(".js") || p.ends_with(".mjs")) {
315 return Err(CodeError::Context(format!(
316 "tool {} (kind=script) path `{p}` must point to a .js or .mjs file",
317 path.display()
318 )));
319 }
320 crate::workspace::validate_relative_pattern(&p, "script path").map_err(|e| {
321 CodeError::Context(format!("tool {} (kind=script): {e}", path.display()))
322 })?;
323 let limits =
324 validate_script_limits(meta.limits.unwrap_or_default()).map_err(|e| {
325 CodeError::Context(format!("tool {} (kind=script): {e}", path.display()))
326 })?;
327 let description = meta
328 .description
329 .map(|d| d.trim().to_string())
330 .filter(|d| !d.is_empty())
331 .unwrap_or_else(|| body.trim().to_string());

Callers 1

loadMethod · 0.85

Calls 9

md_filesFunction · 0.85
split_frontmatterFunction · 0.85
validate_script_limitsFunction · 0.85
file_stemFunction · 0.85
as_strMethod · 0.45
is_emptyMethod · 0.45
insertMethod · 0.45
nameMethod · 0.45

Tested by

no test coverage detected