MCP client: spawn external tool servers, dispatch via JSON-RPC

New mcp_client.rs: McpRegistry manages MCP server connections.
Spawns child processes, speaks JSON-RPC 2.0 over stdio. Discovers
tools via tools/list, dispatches calls via tools/call.

dispatch_with_agent falls through to MCP after checking internal
tools. McpRegistry lives on Agent (shared across forks).

Still needs: config-driven server startup, system prompt integration.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-09 11:45:39 -04:00
parent ec7e11db56
commit 8b5614ba99
4 changed files with 241 additions and 3 deletions

View file

@ -138,8 +138,17 @@ pub struct Agent {
}
/// Mutable agent state — behind its own mutex.
/// Which external MCP tools an agent can access.
#[derive(Clone)]
pub enum McpToolAccess {
None,
All,
Some(Vec<String>),
}
pub struct AgentState {
pub tools: Vec<tools::Tool>,
pub mcp_tools: McpToolAccess,
pub last_prompt_tokens: u32,
pub reasoning_effort: String,
pub temperature: f32,
@ -174,8 +183,7 @@ impl Agent {
context.conversation_log = conversation_log;
context.push_no_log(Section::System, AstNode::system_msg(&system_prompt));
let tool_defs: Vec<String> = tools::tools().iter()
.map(|t| t.to_json()).collect();
let tool_defs = tools::all_tool_definitions().await;
if !tool_defs.is_empty() {
let tools_text = format!(
"# Tools\n\nYou have access to the following functions:\n\n<tools>\n{}\n</tools>\n\n\
@ -202,6 +210,7 @@ impl Agent {
context: tokio::sync::Mutex::new(context),
state: tokio::sync::Mutex::new(AgentState {
tools: tools::tools(),
mcp_tools: McpToolAccess::All,
last_prompt_tokens: 0,
reasoning_effort: "none".to_string(),
temperature: 0.6,
@ -237,6 +246,7 @@ impl Agent {
context: tokio::sync::Mutex::new(ctx),
state: tokio::sync::Mutex::new(AgentState {
tools,
mcp_tools: McpToolAccess::None,
last_prompt_tokens: 0,
reasoning_effort: "none".to_string(),
temperature: st.temperature,