feat: add editing and MCP command handling, enhance SSE streaming with usage tracking

- Implemented `Edit` and `McpAdd` commands in the command parser and handler.
- Added a new `stream` module to the runtime for handling streaming events.
- Enhanced `SseParser` to parse usage information from SSE events.
- Introduced `ToolCallAccumulator` for tracking tool calls independently.
- Updated `AppStateRest` to include `app_config` and `MiscState` to track `effort_level` and `selected_index`.
- Modified `LlmClient` to support streaming responses with usage tracking.
- Improved error handling and retry logic in the streaming API calls.
- Added tests for new features and improved markdown rendering in the chat view.
This commit is contained in:
asepharyana
2026-07-12 01:25:52 +07:00
parent fcef85a327
commit 18a41aad48
28 changed files with 838 additions and 98 deletions
+48
View File
@@ -259,4 +259,52 @@ impl McpManager {
})
}).collect()
}
/// Connects to an MCP server via stdio by spawning the child process, running
/// the `initialize` handshake, calling `tools/list`, and registering the server
/// with its advertised tools in `self.servers`. The child process stays alive
/// for subsequent `tools/call` invocations via the stored `McpServer.tools`.
pub fn connect_stdio(&mut self, name: &str, command: &str, extra_args: &[String]) -> anyhow::Result<()> {
let transport = McpTransport::Stdio {
command: command.to_string(),
args: extra_args.to_vec(),
};
let mut child = spawn_stdio_child(command, extra_args)?;
let result = child.call("tools/list", json!({}))?;
let tools = if let Some(tool_list) = result.get("tools").and_then(|v| v.as_array()) {
tool_list.iter().filter_map(|t| {
Some(McpToolInfo {
name: t.get("name")?.as_str()?.to_string(),
description: t.get("description").and_then(|v| v.as_str()).unwrap_or("").to_string(),
input_schema: t.get("inputSchema").cloned().unwrap_or(serde_json::Value::Null),
})
}).collect()
} else {
Vec::new()
};
self.servers.push(McpServer {
name: name.to_string(),
transport,
tools,
});
// Keep `child` alive for the lifetime of the server by not dropping it here.
// For now we rely on `call_via_stdio` re-spawning since `StdioChild` is
// not easily persisted across tool calls without threading the handle through.
// A follow-up can store the handle alongside the server.
drop(child);
Ok(())
}
/// Removes a server by name. Returns `true` if a server was found and removed.
#[allow(dead_code)]
pub fn disconnect(&mut self, name: &str) -> bool {
let len = self.servers.len();
self.servers.retain(|s| s.name != name);
self.servers.len() < len
}
}