Improve claude tools (#36538)

- Return unified diff from `Edit` tool so model can see the final state
- Format on save if enabled
- Provide `Write` tool
- Disable `MultiEdit` tool
- Better prompting

Release Notes:

- N/A
This commit is contained in:
Agus Zubiaga 2025-08-19 22:42:11 -03:00 committed by GitHub
parent 714c36fa7b
commit 7c7043947b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 606 additions and 302 deletions

View file

@ -14,6 +14,7 @@ use serde::de::DeserializeOwned;
use serde_json::{json, value::RawValue};
use smol::stream::StreamExt;
use std::{
any::TypeId,
cell::RefCell,
path::{Path, PathBuf},
rc::Rc,
@ -87,18 +88,26 @@ impl McpServer {
settings.inline_subschemas = true;
let mut generator = settings.into_generator();
let output_schema = generator.root_schema_for::<T::Output>();
let unit_schema = generator.root_schema_for::<T::Output>();
let input_schema = generator.root_schema_for::<T::Input>();
let description = input_schema
.get("description")
.and_then(|desc| desc.as_str())
.map(|desc| desc.to_string());
debug_assert!(
description.is_some(),
"Input schema struct must include a doc comment for the tool description"
);
let registered_tool = RegisteredTool {
tool: Tool {
name: T::NAME.into(),
description: Some(tool.description().into()),
input_schema: generator.root_schema_for::<T::Input>().into(),
output_schema: if output_schema == unit_schema {
description,
input_schema: input_schema.into(),
output_schema: if TypeId::of::<T::Output>() == TypeId::of::<()>() {
None
} else {
Some(output_schema.into())
Some(generator.root_schema_for::<T::Output>().into())
},
annotations: Some(tool.annotations()),
},
@ -399,8 +408,6 @@ pub trait McpServerTool {
const NAME: &'static str;
fn description(&self) -> &'static str;
fn annotations(&self) -> ToolAnnotations {
ToolAnnotations {
title: None,
@ -418,6 +425,7 @@ pub trait McpServerTool {
) -> impl Future<Output = Result<ToolResponse<Self::Output>>>;
}
#[derive(Debug)]
pub struct ToolResponse<T> {
pub content: Vec<ToolResponseContent>,
pub structured_content: T,

View file

@ -711,6 +711,16 @@ pub enum ToolResponseContent {
Resource { resource: ResourceContents },
}
impl ToolResponseContent {
pub fn text(&self) -> Option<&str> {
if let ToolResponseContent::Text { text } = self {
Some(text)
} else {
None
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListToolsResponse {