mirror of
https://github.com/aaif-goose/goose.git
synced 2026-07-03 14:10:03 +02:00
Move resource and more types to mcp-core (#470)
Co-authored-by: Bradley Axen <baxen@squareup.com>
This commit is contained in:
+24
-13
@@ -8,16 +8,18 @@ use crate::errors::{AgentError, AgentResult};
|
||||
use crate::message::{Message, ToolRequest};
|
||||
use crate::prompt_template::load_prompt_file;
|
||||
use crate::providers::base::Provider;
|
||||
use crate::systems::{Resource, System};
|
||||
use crate::systems::System;
|
||||
use crate::token_counter::TokenCounter;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::{Content, Resource, Tool, ToolCall};
|
||||
use serde::Serialize;
|
||||
|
||||
const CONTEXT_LIMIT: usize = 200_000; // TODO: model's context limit should be in provider config
|
||||
const ESTIMATE_FACTOR: f32 = 0.8;
|
||||
const ESTIMATED_TOKEN_LIMIT: usize = (CONTEXT_LIMIT as f32 * ESTIMATE_FACTOR) as usize;
|
||||
|
||||
// used to sort resources by priority within error margin
|
||||
const PRIORITY_EPSILON: f32 = 0.001;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
struct SystemInfo {
|
||||
name: String,
|
||||
@@ -239,12 +241,20 @@ impl Agent {
|
||||
}
|
||||
|
||||
// Sort by priority (high to low) and timestamp (newest to oldest)
|
||||
// since priority is float, we need to sort by priority within error margin - PRIORITY_EPSILON
|
||||
all_resources.sort_by(|a, b| {
|
||||
let priority_cmp = b.2.priority.cmp(&a.2.priority);
|
||||
if priority_cmp == std::cmp::Ordering::Equal {
|
||||
b.2.timestamp.cmp(&a.2.timestamp)
|
||||
// Compare priorities with epsilon
|
||||
// Compare priorities with Option handling - default to 0.0 if None
|
||||
let a_priority = a.2.priority().unwrap_or(0.0);
|
||||
let b_priority = b.2.priority().unwrap_or(0.0);
|
||||
if (b_priority - a_priority).abs() < PRIORITY_EPSILON {
|
||||
// Priorities are "equal" within epsilon, use timestamp as tiebreaker
|
||||
b.2.timestamp().cmp(&a.2.timestamp())
|
||||
} else {
|
||||
priority_cmp
|
||||
// Priorities are different enough, use priority ordering
|
||||
b.2.priority()
|
||||
.partial_cmp(&a.2.priority())
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -392,9 +402,11 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::message::MessageContent;
|
||||
use crate::providers::mock::MockProvider;
|
||||
use crate::systems::Resource;
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use futures::TryStreamExt;
|
||||
use mcp_core::resource::Resource;
|
||||
use mcp_core::Annotations;
|
||||
use serde_json::json;
|
||||
|
||||
// Mock system for testing
|
||||
@@ -419,13 +431,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_resource(&mut self, name: &str, content: &str, priority: i32) {
|
||||
fn add_resource(&mut self, name: &str, content: &str, priority: f32) {
|
||||
let uri = format!("file://{}", name);
|
||||
let resource = Resource {
|
||||
name: name.to_string(),
|
||||
uri: uri.clone(),
|
||||
priority,
|
||||
timestamp: chrono::Utc::now(),
|
||||
annotations: Some(Annotations::for_resource(priority, Utc::now())),
|
||||
description: Some("A mock resource".to_string()),
|
||||
mime_type: "text/plain".to_string(),
|
||||
};
|
||||
@@ -602,8 +613,8 @@ mod tests {
|
||||
|
||||
// Add two resources with different priorities
|
||||
let string_10toks = "hello ".repeat(10);
|
||||
system.add_resource("high_priority", &string_10toks, 4);
|
||||
system.add_resource("low_priority", &string_10toks, 1);
|
||||
system.add_resource("high_priority", &string_10toks, 0.8);
|
||||
system.add_resource("low_priority", &string_10toks, 0.1);
|
||||
|
||||
agent.add_system(Box::new(system));
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
mod lang;
|
||||
|
||||
use crate::systems::Resource;
|
||||
use anyhow::Result as AnyhowResult;
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine;
|
||||
@@ -17,9 +16,7 @@ use xcap::{Monitor, Window};
|
||||
|
||||
use crate::errors::{AgentError, AgentResult};
|
||||
use crate::systems::System;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::role::Role;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::{Content, Resource, Role, Tool, ToolCall};
|
||||
|
||||
pub struct DeveloperSystem {
|
||||
tools: Vec<Tool>,
|
||||
@@ -172,7 +169,7 @@ impl DeveloperSystem {
|
||||
You can capture either:
|
||||
1. A full display (monitor) using the display parameter
|
||||
2. A specific window by its title using the window_title parameter
|
||||
|
||||
|
||||
Only one of display or window_title should be specified.
|
||||
"#},
|
||||
json!({
|
||||
@@ -276,7 +273,7 @@ impl DeveloperSystem {
|
||||
uri.clone(),
|
||||
Resource::new(uri, Some("text".to_string()), Some("cwd".to_string()))
|
||||
.unwrap()
|
||||
.with_priority(1000), // Set highest priority
|
||||
.with_priority(1.0), // Set highest priority
|
||||
);
|
||||
Mutex::new(resources)
|
||||
},
|
||||
|
||||
@@ -3,16 +3,13 @@ use crate::systems::System;
|
||||
use anyhow::Result as AnyhowResult;
|
||||
use async_trait::async_trait;
|
||||
use indoc::formatdoc;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::{Content, Resource, Tool, ToolCall};
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::systems::Resource;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MemoryManager {
|
||||
global_memory_dir: PathBuf,
|
||||
@@ -406,7 +403,7 @@ impl System for MemorySystem {
|
||||
Resource::with_uri(
|
||||
format!("str:///{}.txt", memories.join(" ")),
|
||||
format!("{}.txt", category),
|
||||
0,
|
||||
0.0,
|
||||
Some("text".to_string()),
|
||||
)
|
||||
.ok()
|
||||
|
||||
@@ -2,12 +2,9 @@ use anyhow::Result as AnyhowResult;
|
||||
use async_trait::async_trait;
|
||||
use std::fs;
|
||||
|
||||
use super::Resource;
|
||||
use crate::errors::{AgentError, AgentResult};
|
||||
use crate::systems::System;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::tool::Tool;
|
||||
use mcp_core::tool::ToolCall;
|
||||
use mcp_core::{Content, Resource, Tool, ToolCall};
|
||||
|
||||
pub struct GooseHintsSystem {
|
||||
instructions: String,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
mod system;
|
||||
pub use system::System;
|
||||
|
||||
mod resource;
|
||||
pub use resource::Resource;
|
||||
|
||||
pub mod goose_hints;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use anyhow::Result as AnyhowResult;
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::Resource;
|
||||
use crate::errors::AgentResult;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::{Content, Resource, Tool, ToolCall};
|
||||
|
||||
/// Core trait that defines a system that can be operated by an AI agent
|
||||
#[async_trait]
|
||||
|
||||
@@ -3,9 +3,8 @@ use async_trait::async_trait;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use goose::errors::{AgentError, AgentResult};
|
||||
use goose::systems::{Resource, System};
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use goose::systems::System;
|
||||
use mcp_core::{Content, Resource, Tool, ToolCall};
|
||||
|
||||
/// A simple system that echoes input back to the caller
|
||||
pub struct EchoSystem {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
[package]
|
||||
name = "mcp-client"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
description.workspace = true
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
mcp-core = { path = "../mcp-core" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "rustls-tls"] }
|
||||
reqwest-eventsource = "0.5.0"
|
||||
|
||||
@@ -2,4 +2,3 @@ pub mod session;
|
||||
pub mod sse_transport;
|
||||
pub mod stdio_transport;
|
||||
pub mod transport;
|
||||
pub mod types;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::transport::{ReadStream, WriteStream};
|
||||
use crate::types::*;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use mcp_core::types::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::{json, Value};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
@@ -373,9 +373,9 @@ mod tests {
|
||||
|
||||
// Initialize the session
|
||||
let init_result = session.initialize().await?;
|
||||
assert_eq!(init_result.protocolVersion, "2024-11-05");
|
||||
assert_eq!(init_result.protocol_version, "2024-11-05");
|
||||
assert_eq!(
|
||||
init_result.capabilities.resources.unwrap().listChanged,
|
||||
init_result.capabilities.resources.unwrap().list_changed,
|
||||
Some(false)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::transport::{ReadStream, Transport, WriteStream};
|
||||
use crate::types::JsonRpcMessage;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures_util::StreamExt;
|
||||
use mcp_core::types::JsonRpcMessage;
|
||||
use reqwest::{Client, Url};
|
||||
use reqwest_eventsource::{Event, EventSource};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::transport::{ReadStream, Transport, WriteStream};
|
||||
use crate::types::*;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use mcp_core::types::*;
|
||||
use std::process::Stdio;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::process::{Child, Command};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::types::JsonRpcMessage;
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use mcp_core::types::JsonRpcMessage;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
// Stream types for consistent interface
|
||||
|
||||
@@ -8,4 +8,10 @@ async-trait = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
schemars = "0.8"
|
||||
schemars = "0.8"
|
||||
anyhow = "1.0"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
url = "2.5"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.8"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::role::Role;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -8,6 +9,24 @@ pub struct Annotations {
|
||||
pub audience: Option<Vec<Role>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub priority: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub timestamp: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl Annotations {
|
||||
/// Creates a new Annotations instance specifically for resources
|
||||
/// optional priority, and a timestamp (defaults to now if None)
|
||||
pub fn for_resource(priority: f32, timestamp: DateTime<Utc>) -> Self {
|
||||
assert!(
|
||||
(0.0..=1.0).contains(&priority),
|
||||
"Priority {priority} must be between 0.0 and 1.0"
|
||||
);
|
||||
Annotations {
|
||||
priority: Some(priority),
|
||||
timestamp: Some(timestamp),
|
||||
audience: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -81,6 +100,7 @@ impl Content {
|
||||
None => Annotations {
|
||||
audience: Some(audience),
|
||||
priority: None,
|
||||
timestamp: None,
|
||||
},
|
||||
});
|
||||
self
|
||||
@@ -105,6 +125,7 @@ impl Content {
|
||||
None => Annotations {
|
||||
audience: None,
|
||||
priority: Some(priority),
|
||||
timestamp: None,
|
||||
},
|
||||
});
|
||||
self
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
pub mod content;
|
||||
pub use content::{Annotations, Content, ImageContent, TextContent};
|
||||
pub mod handler;
|
||||
pub mod role;
|
||||
pub use role::Role;
|
||||
pub mod tool;
|
||||
pub use tool::{Tool, ToolCall};
|
||||
pub mod resource;
|
||||
pub use resource::{Resource, ResourceContents};
|
||||
pub mod types;
|
||||
pub use types::*;
|
||||
|
||||
@@ -3,23 +3,41 @@ use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
use crate::content::Annotations;
|
||||
|
||||
/// Represents a resource in the system with metadata
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Resource {
|
||||
/// URI representing the resource location (e.g., "file:///path/to/file" or "str:///content")
|
||||
pub uri: String,
|
||||
/// Name of the resource
|
||||
pub name: String,
|
||||
/// Last modified timestamp
|
||||
pub timestamp: DateTime<Utc>,
|
||||
/// Priority of the resource (higher number means higher priority)
|
||||
pub priority: i32,
|
||||
/// Optional description of the resource
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
/// MIME type of the resource content ("text" or "blob")
|
||||
#[serde(default = "default_mime_type")]
|
||||
pub mime_type: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub annotations: Option<Annotations>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
pub enum ResourceContents {
|
||||
TextResourceContents {
|
||||
uri: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
mime_type: Option<String>,
|
||||
text: String,
|
||||
},
|
||||
BlobResourceContents {
|
||||
uri: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
mime_type: Option<String>,
|
||||
blob: String,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_mime_type() -> String {
|
||||
@@ -56,10 +74,9 @@ impl Resource {
|
||||
Ok(Self {
|
||||
uri: uri.to_string(),
|
||||
name,
|
||||
timestamp: Utc::now(),
|
||||
priority: 0,
|
||||
description: None,
|
||||
mime_type,
|
||||
annotations: Some(Annotations::for_resource(0.0, Utc::now())),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -67,7 +84,7 @@ impl Resource {
|
||||
pub fn with_uri<S: Into<String>>(
|
||||
uri: S,
|
||||
name: S,
|
||||
priority: i32,
|
||||
priority: f32,
|
||||
mime_type: Option<String>,
|
||||
) -> Result<Self> {
|
||||
let uri_string = uri.into();
|
||||
@@ -82,24 +99,33 @@ impl Resource {
|
||||
Ok(Self {
|
||||
uri: uri_string,
|
||||
name: name.into(),
|
||||
timestamp: Utc::now(),
|
||||
priority,
|
||||
description: None,
|
||||
mime_type,
|
||||
annotations: Some(Annotations::for_resource(priority, Utc::now())),
|
||||
})
|
||||
}
|
||||
|
||||
/// Updates the resource's timestamp to the current time
|
||||
pub fn update_timestamp(&mut self) {
|
||||
self.timestamp = Utc::now();
|
||||
self.annotations.as_mut().unwrap().timestamp = Some(Utc::now());
|
||||
}
|
||||
|
||||
/// Sets the priority of the resource and returns self for method chaining
|
||||
pub fn with_priority(mut self, priority: i32) -> Self {
|
||||
self.priority = priority;
|
||||
pub fn with_priority(mut self, priority: f32) -> Self {
|
||||
self.annotations.as_mut().unwrap().priority = Some(priority);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the priority of the resource, if set
|
||||
pub fn priority(&self) -> Option<f32> {
|
||||
self.annotations.as_ref().and_then(|a| a.priority)
|
||||
}
|
||||
|
||||
/// Returns the timestamp of the resource, if set
|
||||
pub fn timestamp(&self) -> Option<DateTime<Utc>> {
|
||||
self.annotations.as_ref().and_then(|a| a.timestamp)
|
||||
}
|
||||
|
||||
/// Returns the scheme of the URI
|
||||
pub fn scheme(&self) -> Result<String> {
|
||||
let url = Url::parse(&self.uri)?;
|
||||
@@ -140,7 +166,7 @@ mod tests {
|
||||
|
||||
let resource = Resource::new(&uri, Some("text".to_string()), None)?;
|
||||
assert!(resource.uri.starts_with("file:///"));
|
||||
assert_eq!(resource.priority, 0);
|
||||
assert_eq!(resource.priority(), Some(0.0));
|
||||
assert_eq!(resource.mime_type, "text");
|
||||
assert_eq!(resource.scheme()?, "file");
|
||||
|
||||
@@ -154,13 +180,13 @@ mod tests {
|
||||
let resource = Resource::with_uri(
|
||||
uri.clone(),
|
||||
"test.txt".to_string(),
|
||||
5,
|
||||
0.5,
|
||||
Some("text".to_string()),
|
||||
)?;
|
||||
|
||||
assert_eq!(resource.uri, uri);
|
||||
assert_eq!(resource.name, "test.txt");
|
||||
assert_eq!(resource.priority, 5);
|
||||
assert_eq!(resource.priority(), Some(0.5));
|
||||
assert_eq!(resource.mime_type, "text");
|
||||
assert_eq!(resource.scheme()?, "str");
|
||||
|
||||
@@ -189,7 +215,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_with_description() -> Result<()> {
|
||||
let resource = Resource::with_uri("file:///test.txt", "test.txt", 0, None)?
|
||||
let resource = Resource::with_uri("file:///test.txt", "test.txt", 0.0, None)?
|
||||
.with_description("A test resource");
|
||||
|
||||
assert_eq!(resource.description, Some("A test resource".to_string()));
|
||||
@@ -199,7 +225,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_with_mime_type() -> Result<()> {
|
||||
let resource =
|
||||
Resource::with_uri("file:///test.txt", "test.txt", 0, None)?.with_mime_type("blob");
|
||||
Resource::with_uri("file:///test.txt", "test.txt", 0.0, None)?.with_mime_type("blob");
|
||||
|
||||
assert_eq!(resource.mime_type, "blob");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::{content::Content, resource::Resource, resource::ResourceContents, tool::Tool};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -64,20 +63,21 @@ pub struct ErrorData {
|
||||
pub data: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InitializeResult {
|
||||
pub protocolVersion: String,
|
||||
pub protocol_version: String,
|
||||
pub capabilities: ServerCapabilities,
|
||||
pub serverInfo: Implementation,
|
||||
pub server_info: Implementation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct Implementation {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ServerCapabilities {
|
||||
pub prompts: Option<PromptsCapability>,
|
||||
pub resources: Option<ResourcesCapability>,
|
||||
@@ -85,85 +85,45 @@ pub struct ServerCapabilities {
|
||||
// Add other capabilities as needed
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PromptsCapability {
|
||||
pub listChanged: Option<bool>,
|
||||
pub list_changed: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ResourcesCapability {
|
||||
pub subscribe: Option<bool>,
|
||||
pub listChanged: Option<bool>,
|
||||
pub list_changed: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ToolsCapability {
|
||||
pub listChanged: Option<bool>,
|
||||
pub list_changed: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ListResourcesResult {
|
||||
pub resources: Vec<Resource>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Resource {
|
||||
pub uri: String,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub mimeType: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ReadResourceResult {
|
||||
pub contents: Vec<ResourceContents>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ResourceContents {
|
||||
pub uri: String,
|
||||
pub mimeType: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub content: ResourceContent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ResourceContent {
|
||||
Text { text: String },
|
||||
Blob { blob: String }, // Base64-encoded
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct ListToolsResult {
|
||||
pub tools: Vec<Tool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Tool {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub inputSchema: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CallToolResult {
|
||||
pub content: Vec<Content>,
|
||||
pub isError: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Content {
|
||||
#[serde(rename = "text")]
|
||||
Text { text: String },
|
||||
#[serde(rename = "image")]
|
||||
Image {
|
||||
data: String, // Base64-encoded image data
|
||||
mimeType: String,
|
||||
},
|
||||
#[serde(rename = "resource")]
|
||||
EmbeddedResource { resource: ResourceContents },
|
||||
pub is_error: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
Reference in New Issue
Block a user