Replace mcp_core::content types with rmcp::model types (#3500)

This commit is contained in:
Jack Amadeo
2025-07-18 17:23:25 -04:00
committed by GitHub
parent a2309d9436
commit d5291461ca
66 changed files with 867 additions and 856 deletions
@@ -1,13 +1,18 @@
use goose_server::openapi;
use std::env;
use std::fs;
use std::path::PathBuf;
fn main() {
let schema = openapi::generate_schema();
// Get the current working directory
let current_dir = env::current_dir().unwrap();
let output_path = current_dir.join("ui").join("desktop").join("openapi.json");
let package_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let output_path = PathBuf::from(package_dir)
.join("..")
.join("..")
.join("ui")
.join("desktop")
.join("openapi.json");
// Ensure parent directory exists
if let Some(parent) = output_path.parent() {
+267 -25
View File
@@ -11,40 +11,282 @@ use goose::permission::permission_confirmation::PrincipalType;
use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata};
use goose::session::info::SessionInfo;
use goose::session::SessionMetadata;
use mcp_core::content::{Annotations, Content, EmbeddedResource, ImageContent, TextContent};
use mcp_core::handler::ToolResultSchema;
use mcp_core::resource::ResourceContents;
use mcp_core::tool::{Tool, ToolAnnotations};
use rmcp::model::{Annotations, Content, EmbeddedResource, ImageContent, Role, TextContent};
use utoipa::{OpenApi, ToSchema};
/// A wrapper around rmcp::model::Role that implements ToSchema for utoipa
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub enum Role {
/// A human user or client making a request
User,
/// An AI assistant or server providing a response
Assistant,
use rmcp::schemars::schema::{InstanceType, SchemaObject, SingleOrVec};
use utoipa::openapi::schema::{
AdditionalProperties, AnyOfBuilder, ArrayBuilder, ObjectBuilder, OneOfBuilder, Schema,
SchemaFormat, SchemaType,
};
use utoipa::openapi::{AllOfBuilder, Ref, RefOr};
macro_rules! derive_utoipa {
($inner_type:ident as $schema_name:ident) => {
struct $schema_name {}
impl<'__s> ToSchema<'__s> for $schema_name {
fn schema() -> (&'__s str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
let settings = rmcp::schemars::gen::SchemaSettings::openapi3();
let generator = settings.into_generator();
let schema = generator.into_root_schema_for::<$inner_type>();
let schema = convert_schemars_to_utoipa(schema);
(stringify!($inner_type), schema)
}
fn aliases() -> Vec<(&'__s str, utoipa::openapi::schema::Schema)> {
Vec::new()
}
}
};
}
impl From<rmcp::model::Role> for Role {
fn from(role: rmcp::model::Role) -> Self {
match role {
rmcp::model::Role::User => Role::User,
rmcp::model::Role::Assistant => Role::Assistant,
fn convert_schemars_to_utoipa(schema: rmcp::schemars::schema::RootSchema) -> RefOr<Schema> {
convert_schema_object(&rmcp::schemars::schema::Schema::Object(
schema.schema.clone(),
))
}
fn convert_schema_object(schema: &rmcp::schemars::schema::Schema) -> RefOr<Schema> {
match schema {
rmcp::schemars::schema::Schema::Object(schema_object) => {
convert_schema_object_inner(schema_object)
}
rmcp::schemars::schema::Schema::Bool(true) => {
RefOr::T(Schema::Object(ObjectBuilder::new().build()))
}
rmcp::schemars::schema::Schema::Bool(false) => {
RefOr::T(Schema::Object(ObjectBuilder::new().build()))
}
}
}
impl From<Role> for rmcp::model::Role {
fn from(role: Role) -> Self {
match role {
Role::User => rmcp::model::Role::User,
Role::Assistant => rmcp::model::Role::Assistant,
fn convert_schema_object_inner(schema: &SchemaObject) -> RefOr<Schema> {
// Handle references first
if let Some(reference) = &schema.reference {
return RefOr::Ref(Ref::new(reference.clone()));
}
// Handle subschemas (oneOf, allOf, anyOf)
if let Some(subschemas) = &schema.subschemas {
if let Some(one_of) = &subschemas.one_of {
let schemas: Vec<RefOr<Schema>> = one_of.iter().map(convert_schema_object).collect();
let mut builder = OneOfBuilder::new();
for schema in schemas {
builder = builder.item(schema);
}
return RefOr::T(Schema::OneOf(builder.build()));
}
if let Some(all_of) = &subschemas.all_of {
let schemas: Vec<RefOr<Schema>> = all_of.iter().map(convert_schema_object).collect();
let mut all_of = AllOfBuilder::new();
for schema in schemas {
all_of = all_of.item(schema);
}
return RefOr::T(Schema::AllOf(all_of.build()));
}
if let Some(any_of) = &subschemas.any_of {
let schemas: Vec<RefOr<Schema>> = any_of.iter().map(convert_schema_object).collect();
let mut any_of = AnyOfBuilder::new();
for schema in schemas {
any_of = any_of.item(schema);
}
return RefOr::T(Schema::AnyOf(any_of.build()));
}
}
// Handle based on instance type
match &schema.instance_type {
Some(SingleOrVec::Single(instance_type)) => {
convert_single_instance_type(instance_type, schema)
}
Some(SingleOrVec::Vec(instance_types)) => {
// Multiple types - use AnyOf
let schemas: Vec<RefOr<Schema>> = instance_types
.iter()
.map(|instance_type| convert_single_instance_type(instance_type, schema))
.collect();
let mut any_of = AnyOfBuilder::new();
for schema in schemas {
any_of = any_of.item(schema);
}
RefOr::T(Schema::AnyOf(any_of.build()))
}
None => {
// No type specified - create a generic schema
RefOr::T(Schema::Object(ObjectBuilder::new().build()))
}
}
}
fn convert_single_instance_type(
instance_type: &InstanceType,
schema: &SchemaObject,
) -> RefOr<Schema> {
match instance_type {
InstanceType::Object => {
let mut object_builder = ObjectBuilder::new();
if let Some(object_validation) = &schema.object {
// Add properties
for (name, prop_schema) in &object_validation.properties {
let prop = convert_schema_object(prop_schema);
object_builder = object_builder.property(name, prop);
}
// Add required fields
for required_field in &object_validation.required {
object_builder = object_builder.required(required_field);
}
// Handle additional properties
if let Some(additional) = &object_validation.additional_properties {
match &**additional {
rmcp::schemars::schema::Schema::Bool(false) => {
object_builder = object_builder
.additional_properties(Some(AdditionalProperties::FreeForm(false)));
}
rmcp::schemars::schema::Schema::Bool(true) => {
object_builder = object_builder
.additional_properties(Some(AdditionalProperties::FreeForm(true)));
}
rmcp::schemars::schema::Schema::Object(obj) => {
let schema = convert_schema_object(
&rmcp::schemars::schema::Schema::Object(obj.clone()),
);
object_builder = object_builder
.additional_properties(Some(AdditionalProperties::RefOr(schema)));
}
}
}
}
RefOr::T(Schema::Object(object_builder.build()))
}
InstanceType::Array => {
let mut array_builder = ArrayBuilder::new();
if let Some(array_validation) = &schema.array {
// Add items schema
if let Some(items) = &array_validation.items {
match items {
rmcp::schemars::schema::SingleOrVec::Single(item_schema) => {
let item_schema = convert_schema_object(item_schema);
array_builder = array_builder.items(item_schema);
}
rmcp::schemars::schema::SingleOrVec::Vec(item_schemas) => {
// Multiple item types - use AnyOf
let schemas: Vec<RefOr<Schema>> =
item_schemas.iter().map(convert_schema_object).collect();
let mut any_of = AnyOfBuilder::new();
for schema in schemas {
any_of = any_of.item(schema);
}
let any_of_schema = RefOr::T(Schema::AnyOf(any_of.build()));
array_builder = array_builder.items(any_of_schema);
}
}
}
// Add constraints
if let Some(min_items) = array_validation.min_items {
array_builder = array_builder.min_items(Some(min_items as usize));
}
if let Some(max_items) = array_validation.max_items {
array_builder = array_builder.max_items(Some(max_items as usize));
}
}
RefOr::T(Schema::Array(array_builder.build()))
}
InstanceType::String => {
let mut object_builder = ObjectBuilder::new().schema_type(SchemaType::String);
if let Some(string_validation) = &schema.string {
if let Some(min_length) = string_validation.min_length {
object_builder = object_builder.min_length(Some(min_length as usize));
}
if let Some(max_length) = string_validation.max_length {
object_builder = object_builder.max_length(Some(max_length as usize));
}
if let Some(pattern) = &string_validation.pattern {
object_builder = object_builder.pattern(Some(pattern.clone()));
}
}
if let Some(format) = &schema.format {
object_builder = object_builder.format(Some(SchemaFormat::Custom(format.clone())));
}
RefOr::T(Schema::Object(object_builder.build()))
}
InstanceType::Number => {
let mut object_builder = ObjectBuilder::new().schema_type(SchemaType::Number);
if let Some(number_validation) = &schema.number {
if let Some(minimum) = number_validation.minimum {
object_builder = object_builder.minimum(Some(minimum));
}
if let Some(maximum) = number_validation.maximum {
object_builder = object_builder.maximum(Some(maximum));
}
if let Some(exclusive_minimum) = number_validation.exclusive_minimum {
object_builder = object_builder.exclusive_minimum(Some(exclusive_minimum));
}
if let Some(exclusive_maximum) = number_validation.exclusive_maximum {
object_builder = object_builder.exclusive_maximum(Some(exclusive_maximum));
}
if let Some(multiple_of) = number_validation.multiple_of {
object_builder = object_builder.multiple_of(Some(multiple_of));
}
}
RefOr::T(Schema::Object(object_builder.build()))
}
InstanceType::Integer => {
let mut object_builder = ObjectBuilder::new().schema_type(SchemaType::Integer);
if let Some(number_validation) = &schema.number {
if let Some(minimum) = number_validation.minimum {
object_builder = object_builder.minimum(Some(minimum));
}
if let Some(maximum) = number_validation.maximum {
object_builder = object_builder.maximum(Some(maximum));
}
if let Some(exclusive_minimum) = number_validation.exclusive_minimum {
object_builder = object_builder.exclusive_minimum(Some(exclusive_minimum));
}
if let Some(exclusive_maximum) = number_validation.exclusive_maximum {
object_builder = object_builder.exclusive_maximum(Some(exclusive_maximum));
}
if let Some(multiple_of) = number_validation.multiple_of {
object_builder = object_builder.multiple_of(Some(multiple_of));
}
}
RefOr::T(Schema::Object(object_builder.build()))
}
InstanceType::Boolean => RefOr::T(Schema::Object(
ObjectBuilder::new()
.schema_type(SchemaType::Boolean)
.build(),
)),
InstanceType::Null => RefOr::T(Schema::Object(
ObjectBuilder::new().schema_type(SchemaType::String).build(),
)),
}
}
derive_utoipa!(Role as RoleSchema);
derive_utoipa!(Content as ContentSchema);
derive_utoipa!(EmbeddedResource as EmbeddedResourceSchema);
derive_utoipa!(ImageContent as ImageContentSchema);
derive_utoipa!(TextContent as TextContentSchema);
derive_utoipa!(Annotations as AnnotationsSchema);
#[allow(dead_code)] // Used by utoipa for OpenAPI generation
#[derive(OpenApi)]
#[openapi(
@@ -95,11 +337,11 @@ impl From<Role> for rmcp::model::Role {
super::routes::session::SessionHistoryResponse,
Message,
MessageContent,
Content,
EmbeddedResource,
ImageContent,
Annotations,
TextContent,
ContentSchema,
EmbeddedResourceSchema,
ImageContentSchema,
AnnotationsSchema,
TextContentSchema,
ToolResponse,
ToolRequest,
ToolResultSchema,
@@ -110,7 +352,7 @@ impl From<Role> for rmcp::model::Role {
ResourceContents,
ContextLengthExceeded,
SummarizationRequested,
Role,
RoleSchema,
ProviderMetadata,
ExtensionEntry,
ExtensionConfig,
+2 -1
View File
@@ -18,7 +18,8 @@ use goose::{
permission::{Permission, PermissionConfirmation},
session,
};
use mcp_core::{protocol::JsonRpcMessage, role::Role, Content, ToolResult};
use mcp_core::{protocol::JsonRpcMessage, role::Role, ToolResult};
use rmcp::model::Content;
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;