26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
55 lines
2.2 KiB
Rust
55 lines
2.2 KiB
Rust
//! End-to-end proof for manifest-declared tools (EPIC 12.11): a plugin that
|
|
//! ships a `[[tools]]` entry is discovered, registered as a native MCP tool
|
|
//! **without forking** `build_registry()`, and invocable as a sandboxed
|
|
//! subprocess. Own integration-test binary so the global plugin registry
|
|
//! (`OnceLock`) is fresh. Unix-only: the tool is the `cat` echo binary.
|
|
|
|
#![cfg(unix)]
|
|
|
|
use std::fs;
|
|
|
|
use lean_ctx::core::plugins::PluginManager;
|
|
use lean_ctx::core::plugins::tools::invoke;
|
|
use lean_ctx::server::registry::build_registry;
|
|
|
|
#[test]
|
|
fn manifest_tool_is_discovered_registered_and_invocable() {
|
|
let root = tempfile::tempdir().expect("tempdir");
|
|
let plugin_dir = root.path().join("weather-plugin");
|
|
fs::create_dir_all(&plugin_dir).expect("plugin dir");
|
|
|
|
// `cat` echoes the JSON args we pass on stdin straight back as the result —
|
|
// a real subprocess round-trip with no mock layer.
|
|
fs::write(
|
|
plugin_dir.join("plugin.toml"),
|
|
"[plugin]\nname = \"weather-plugin\"\nversion = \"1.0.0\"\n\n\
|
|
[[tools]]\nname = \"weather_lookup\"\n\
|
|
description = \"Look up the weather\"\ncommand = \"cat\"\ntimeout_ms = 5000\n\
|
|
input_schema = { type = \"object\", properties = { city = { type = \"string\" } } }\n",
|
|
)
|
|
.expect("manifest");
|
|
|
|
// TODO: Audit that the environment access only happens in single-threaded code.
|
|
unsafe { std::env::set_var("LEAN_CTX_PLUGINS_DIR", root.path()) };
|
|
PluginManager::init();
|
|
|
|
// 1) Discovered from the manifest (no fork).
|
|
let specs = PluginManager::tool_specs();
|
|
let spec = specs
|
|
.iter()
|
|
.find(|s| s.name == "weather_lookup")
|
|
.expect("manifest tool should be discovered");
|
|
assert_eq!(spec.plugin_name, "weather-plugin");
|
|
|
|
// 2) Appears in the native tool surface (gateway / list_tools).
|
|
let registry = build_registry();
|
|
assert!(
|
|
registry.contains("weather_lookup"),
|
|
"manifest tool must register as a native MCP tool without a code edit"
|
|
);
|
|
|
|
// 3) Runs sandboxed: the subprocess echoes our arguments back.
|
|
let out = invoke(spec, "{\"city\":\"Bern\"}").expect("tool invocation");
|
|
assert!(out.contains("Bern"), "tool output should echo args: {out}");
|
|
}
|