Is your feature request related to a problem? Please describe.
The trait implementations on IntoCallToolResult over constrain error results, making use of the tool macro more difficult. The implementations on IntoCallToolResult only permit the error to be converted to either unstructured contents via IntoContents, or protocol level errors via manual conversion into ErrorData.
When using the tool macro, it is not possible to have a return signature of Result<T, E> where:
E could become either a protocol error or an application error, depending on its variantE becomes an application error that bears both structured content and unstructured content. Eg display a user friendly message via an unstructured message with user audience, while giving the LLM a detailed error in the structured content.
Describe the solution you'd like
The trait implementation below would work
impl<T:IntoCallToolResult,E:IntoCallToolResult>IntoCallToolResultforResult<T,E>{fninto_call_tool_result(self) -> Result<CallToolResult,crate::ErrorData>{matchself{Ok(value) => value.into_call_tool_result(),Err(error) => error.into_call_tool_result(),}}}This would require removing some of the other implementations. It would allow errors to produce arbitrary outputs.
One drawback is that this would be over-permissive. Error variants would be able to produce CallToolResult objects that are not errors. Avoiding that would probably require new types and traits, along the lines of:
structSuccessResult{/// All fields in `CallToolResult`, except `is_error`}implFrom<SuccessResult>forCallToolResult{fnfrom(value:SuccessResult) -> Self{todo!()}}structErrorResult{/// All fields in `CallToolResult`, except `is_error`}enumMcpError{ProtocolError(ErrorData),ApplicationError(ErrorResult)}implIntoCallToolResultforMcpError{fninto_call_tool_result(self) -> Result<CallToolResult,crate::ErrorData>{matchself{ProtocolError(protocol_error) => Err(protocol_error),ApplicationError(application_error) => todo!(),}}}implIntoCallToolResult<T:Into<SuccessResult>,E:Into<McpError>>forResult<T,E>{fninto_call_tool_result(self) -> Result<CallToolResult,crate::ErrorData>{matchself{Ok(value) => {// Convert into SuccessResultlet res:SuccessResult = value.into();// Convert into CallToolResultOk(res.into())},Err(error) => {// Convert into McpErrorlet err:McpError = error.into();// this is constrained to produce error results, as rmcp defines the into_call_tool_result// implementation on McpError
error.into_call_tool_result()}}}}This would ensure the Ok variant only ever produces happy path results, and the Err variant only ever produces error results.
Describe alternatives you've considered
Right now I'm using an approach that defines a Result newtype
pubstructUnstructuredContent<T>(T);pubstructToolResult<T,E>(pubResult<T,E>);impl<T,E>From<Result<T,E>>forToolResult<T,E>{fnfrom(result:Result<T,E>) -> Self{Self(result)}}impl<T,E>IntoCallToolResultforToolResult<UnstructuredContent<T>,E>whereT:IntoContents,E:IntoCallToolResult,{fninto_call_tool_result(self) -> Result<CallToolResult,McpError>{matchself.0{Ok(content) => Ok(CallToolResult::success(content.0.into_contents())),Err(err) => err.into_call_tool_result(),}}}impl<T,E>IntoCallToolResultforToolResult<Json<T>,E>whereT:Serialize + 'static,E:IntoCallToolResult,{fninto_call_tool_result(self) -> Result<CallToolResult,McpError>{matchself.0{Ok(structured_content) => serde_json::to_value(structured_content.0).map_err(|serialize_err| {McpError::internal_error(format!("failed to serialize response: {serialize_err}"),None,)}).map(CallToolResult::structured),Err(err) => err.into_call_tool_result(),}}}
Is your feature request related to a problem? Please describe.
The trait implementations on
IntoCallToolResultover constrain error results, making use of thetoolmacro more difficult. The implementations onIntoCallToolResultonly permit the error to be converted to either unstructured contents viaIntoContents, or protocol level errors via manual conversion intoErrorData.When using the
toolmacro, it is not possible to have a return signature ofResult<T, E>where:Ecould become either a protocol error or an application error, depending on its variantEbecomes an application error that bears both structured content and unstructured content. Eg display a user friendly message via an unstructured message with useraudience, while giving the LLM a detailed error in the structured content.Describe the solution you'd like
The trait implementation below would work
This would require removing some of the other implementations. It would allow errors to produce arbitrary outputs.
One drawback is that this would be over-permissive. Error variants would be able to produce
CallToolResultobjects that are not errors. Avoiding that would probably require new types and traits, along the lines of:This would ensure the
Okvariant only ever produces happy path results, and theErrvariant only ever produces error results.Describe alternatives you've considered
Right now I'm using an approach that defines a Result newtype