Files
cursor-byok/server/src/cursor/tools/dispatch/mod.rs
T
leokun c5d578c5b1 feat(docs): add documentation site and demo features
- Introduced a new documentation site for Cursor BYOK using Next.js and Fumadocs.
- Added a product demo page with a corresponding Vite configuration.
- Implemented a demo API to simulate LLM calls and responses.
- Enhanced the Makefile to include new build and development commands for the documentation.
- Updated package.json scripts for building and running the documentation site.
- Created various components and layouts for the documentation structure, including blog and user documentation sections.
- Added styling for the new components and layouts to ensure a cohesive design.
- Included a README and other necessary files for local development and deployment.
2026-08-27 21:45:28 +08:00

90 lines
2.6 KiB
Rust

mod edit;
mod exec;
mod interaction;
mod local;
mod semble;
use std::collections::BTreeMap;
use crate::{
cursor::proto::agent::v1 as pb,
model::ToolCall,
store::Store,
web::{WebFetch, WebSearch},
Error, Result,
};
use super::{
result::{ToolCompletion, ToolResultSender},
runtime::{CursorToolRuntime, ExecContext, PendingInteraction},
};
pub(super) struct ToolStart {
pub messages: Vec<pb::AgentServerMessage>,
pub completion: Option<ToolCompletion>,
}
pub(super) enum InteractionContinuation {
Completed(Box<ToolCompletion>),
Pending,
}
pub(super) async fn start(
runtime: &CursorToolRuntime,
results: &ToolResultSender,
call: &ToolCall,
message_index: usize,
dynamic_mcp: &BTreeMap<String, pb::McpToolDefinition>,
context: &ExecContext,
store: Option<&Store>,
) -> Result<ToolStart> {
if let Some(definition) = dynamic_mcp.get(&call.name) {
return exec::start_dynamic(runtime, call, definition, context).await;
}
if is_mcp_auth(call) {
return interaction::start(runtime, call).await;
}
if context.task_disabled(call) {
return local::subagents_disabled(call);
}
match normalized(&call.name).as_str() {
"shell" | "read" | "delete" | "grep" | "glob" | "readlints" | "task" | "callmcptool"
| "fetchmcpresource" | "getmcptools" => exec::start(runtime, call, context).await,
"write" | "strreplace" | "editnotebook" => edit::start(runtime, call, context).await,
"askquestion" | "websearch" | "webfetch" | "switchmode" | "createplan"
| "generateimage" => interaction::start(runtime, call).await,
"todowrite" | "updatecurrentstep" => local::start(call, message_index),
"semblesearch" | "semblefindrelated" => semble::start(results, call, store.cloned()),
_ => Err(Error::Protocol(format!("unsupported tool: {}", call.name))),
}
}
fn is_mcp_auth(call: &ToolCall) -> bool {
normalized(&call.name) == "callmcptool"
&& call
.arguments
.get("toolName")
.and_then(serde_json::Value::as_str)
.is_some_and(|tool| normalized(tool) == "mcpauth")
}
pub(super) async fn resume_interaction(
results: &ToolResultSender,
search: &WebSearch,
fetch: &WebFetch,
pending: PendingInteraction,
response: &pb::InteractionResponse,
) -> Result<InteractionContinuation> {
interaction::resume(results, search, fetch, pending, response).await
}
pub(super) fn normalized(name: &str) -> String {
name.chars()
.filter(|character| character.is_ascii_alphanumeric())
.flat_map(char::to_lowercase)
.collect()
}