Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
084452abf6 | ||
|
|
8b4bd59572 |
No files matched your search
@@ -3,6 +3,15 @@
|
||||
All notable changes to CodeCraft. Versions follow [Semantic Versioning](https://semver.org/);
|
||||
until 1.0, minor versions may contain breaking changes.
|
||||
|
||||
## [v202611-00025] – 2026-11-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Models did not find built-in tools via `mcp_search`** (e.g. `generate_3d` in a chat that
|
||||
had just been switched from plan to build): the search only looked at MCP servers. It now also
|
||||
lists matching built-in tools and says whether the current mode has them ("available – call it
|
||||
directly", or "not in the read-only mode plan – ask the user to switch to build").
|
||||
|
||||
## [v202611-00024] – 2026-11-12
|
||||
|
||||
### Added
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img alt="Version v202611-00024" src="https://img.shields.io/badge/version-v202611--00024-d69e2e?style=flat-square">
|
||||
<img alt="Version v202611-00025" src="https://img.shields.io/badge/version-v202611--00025-d69e2e?style=flat-square">
|
||||
<img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-689d6a?style=flat-square">
|
||||
<img alt="Rust backend" src="https://img.shields.io/badge/backend-Rust-b7410e?style=flat-square&logo=rust">
|
||||
<img alt="React PWA" src="https://img.shields.io/badge/frontend-React%20PWA-2d81ff?style=flat-square&logo=react">
|
||||
|
||||
+41
-2
@@ -558,6 +558,42 @@ impl McpManager {
|
||||
/// Search the on-demand MCP tools by keywords.
|
||||
pub struct McpSearch;
|
||||
|
||||
/// Built-in tools matching the search words, with their availability in the current mode.
|
||||
fn builtin_matches(ctx: &ToolCtx, words: &[&str]) -> String {
|
||||
if words.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let app = &ctx.app;
|
||||
let owner_id = app.session(&ctx.session_id).ok().and_then(|s| s.owner_id);
|
||||
let owner_user = owner_id.as_deref().and_then(|id| app.store.user(id).ok().flatten());
|
||||
let root = app.root_for(owner_id.as_deref(), &ctx.cwd);
|
||||
let agent = crate::prompts::find_agent_for(app, &root, &ctx.cwd, &ctx.agent, owner_id.as_deref());
|
||||
let available: std::collections::HashSet<String> = crate::runner::available_tools(app, &agent, owner_user.as_ref()).iter().map(|t| t.name().to_string()).collect();
|
||||
let mut hits: Vec<(usize, String)> = crate::tools::builtin_tools()
|
||||
.iter()
|
||||
.map(|t| {
|
||||
let name = t.name().to_lowercase();
|
||||
let desc = t.description().to_lowercase();
|
||||
let score: usize = words.iter().map(|w| if name.contains(w) { 3 } else if desc.contains(w) { 1 } else { 0 }).sum();
|
||||
let state = if available.contains(t.name()) {
|
||||
"available – call it directly".to_string()
|
||||
} else if agent.read_only && !t.read_only() {
|
||||
format!("exists, but not in the read-only mode \"{}\" – ask the user to switch to build mode", agent.name)
|
||||
} else {
|
||||
format!("exists, but not available in mode \"{}\" (or disabled in the settings)", agent.name)
|
||||
};
|
||||
(score, format!("- {}: {state}", t.name()))
|
||||
})
|
||||
.filter(|(s, _)| *s >= 2 || words.len() == 1 && *s >= 1)
|
||||
.collect();
|
||||
hits.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
if hits.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let list: Vec<String> = hits.into_iter().take(4).map(|(_, l)| l).collect();
|
||||
format!("Built-in tools (not MCP – call them directly by name, not via mcp_call):\n{}\n\n", list.join("\n"))
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Tool for McpSearch {
|
||||
fn name(&self) -> &str {
|
||||
@@ -602,10 +638,13 @@ impl Tool for McpSearch {
|
||||
.filter(|(s, _)| *s > 0 || words.is_empty())
|
||||
.collect();
|
||||
scored.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
// Models also look here for built-in tools (e.g. generate_3d): say that they exist,
|
||||
// whether this mode has them, and that they are called directly.
|
||||
let builtin = if server.is_none() { builtin_matches(ctx, &words) } else { String::new() };
|
||||
if scored.is_empty() {
|
||||
return Ok(ToolOutput::text("No matching tools. Try other keywords or another server."));
|
||||
return Ok(ToolOutput::text(if builtin.is_empty() { "No matching tools. Try other keywords or another server.".to_string() } else { builtin }));
|
||||
}
|
||||
let mut out = String::new();
|
||||
let mut out = builtin;
|
||||
for (_, t) in scored.iter().take(8) {
|
||||
let desc: String = t.info.description.chars().take(300).collect();
|
||||
out.push_str(&format!("## {}\n{}\nInput schema: {}\n\n", t.full_name, desc, t.info.input_schema));
|
||||
|
||||
Reference in new issue
Block a user