MCP Apps Plumbing (#6286)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -41,8 +41,8 @@ use crate::oauth::oauth_flow;
|
||||
use crate::prompt_template;
|
||||
use crate::subprocess::configure_command_no_window;
|
||||
use rmcp::model::{
|
||||
CallToolRequestParam, Content, ErrorCode, ErrorData, GetPromptResult, Prompt, RawContent,
|
||||
Resource, ResourceContents, ServerInfo, Tool,
|
||||
CallToolRequestParam, Content, ErrorCode, ErrorData, GetPromptResult, Prompt, Resource,
|
||||
ResourceContents, ServerInfo, Tool,
|
||||
};
|
||||
use rmcp::transport::auth::AuthClient;
|
||||
use schemars::_private::NoSerialize;
|
||||
@@ -713,14 +713,13 @@ impl ExtensionManager {
|
||||
input_schema: tool.input_schema,
|
||||
annotations: tool.annotations,
|
||||
output_schema: tool.output_schema,
|
||||
icons: None,
|
||||
title: None,
|
||||
meta: None,
|
||||
icons: tool.icons,
|
||||
title: tool.title,
|
||||
meta: tool.meta,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Exit loop when there are no more pages
|
||||
if client_tools.next_cursor.is_none() {
|
||||
break;
|
||||
}
|
||||
@@ -773,7 +772,7 @@ impl ExtensionManager {
|
||||
}
|
||||
|
||||
// Function that gets executed for read_resource tool
|
||||
pub async fn read_resource(
|
||||
pub async fn read_resource_tool(
|
||||
&self,
|
||||
params: Value,
|
||||
cancellation_token: CancellationToken,
|
||||
@@ -784,14 +783,17 @@ impl ExtensionManager {
|
||||
|
||||
// If extension name is provided, we can just look it up
|
||||
if extension_name.is_some() {
|
||||
let result = self
|
||||
.read_resource_from_extension(
|
||||
uri,
|
||||
extension_name.unwrap(),
|
||||
cancellation_token.clone(),
|
||||
true,
|
||||
)
|
||||
let read_result = self
|
||||
.read_resource(uri, extension_name.unwrap(), cancellation_token.clone())
|
||||
.await?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for content in read_result.contents {
|
||||
if let ResourceContents::TextResourceContents { text, .. } = content {
|
||||
let content_str = format!("{}\n\n{}", uri, text);
|
||||
result.push(Content::text(content_str));
|
||||
}
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
@@ -804,16 +806,20 @@ impl ExtensionManager {
|
||||
let extension_names: Vec<String> = self.extensions.lock().await.keys().cloned().collect();
|
||||
|
||||
for extension_name in extension_names {
|
||||
let result = self
|
||||
.read_resource_from_extension(
|
||||
uri,
|
||||
&extension_name,
|
||||
cancellation_token.clone(),
|
||||
true,
|
||||
)
|
||||
let read_result = self
|
||||
.read_resource(uri, &extension_name, cancellation_token.clone())
|
||||
.await;
|
||||
match result {
|
||||
Ok(result) => return Ok(result),
|
||||
match read_result {
|
||||
Ok(read_result) => {
|
||||
let mut result = Vec::new();
|
||||
for content in read_result.contents {
|
||||
if let ResourceContents::TextResourceContents { text, .. } = content {
|
||||
let content_str = format!("{}\n\n{}", uri, text);
|
||||
result.push(Content::text(content_str));
|
||||
}
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
@@ -839,13 +845,12 @@ impl ExtensionManager {
|
||||
))
|
||||
}
|
||||
|
||||
async fn read_resource_from_extension(
|
||||
pub async fn read_resource(
|
||||
&self,
|
||||
uri: &str,
|
||||
extension_name: &str,
|
||||
cancellation_token: CancellationToken,
|
||||
format_with_uri: bool,
|
||||
) -> Result<Vec<Content>, ErrorData> {
|
||||
) -> Result<rmcp::model::ReadResourceResult, ErrorData> {
|
||||
let available_extensions = self
|
||||
.extensions
|
||||
.lock()
|
||||
@@ -865,7 +870,7 @@ impl ExtensionManager {
|
||||
.ok_or(ErrorData::new(ErrorCode::INVALID_PARAMS, error_msg, None))?;
|
||||
|
||||
let client_guard = client.lock().await;
|
||||
let read_result = client_guard
|
||||
client_guard
|
||||
.read_resource(uri, cancellation_token)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
@@ -874,21 +879,7 @@ impl ExtensionManager {
|
||||
format!("Could not read resource with uri: {}", uri),
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for content in read_result.contents {
|
||||
if let ResourceContents::TextResourceContents { text, .. } = content {
|
||||
let content_str = if format_with_uri {
|
||||
format!("{}\n\n{}", uri, text)
|
||||
} else {
|
||||
text
|
||||
};
|
||||
result.push(Content::text(content_str));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_ui_resources(&self) -> Result<Vec<(String, Resource)>, ErrorData> {
|
||||
@@ -925,31 +916,6 @@ impl ExtensionManager {
|
||||
Ok(ui_resources)
|
||||
}
|
||||
|
||||
pub async fn read_ui_resource(
|
||||
&self,
|
||||
uri: &str,
|
||||
extension_name: &str,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Result<String, ErrorData> {
|
||||
let contents = self
|
||||
.read_resource_from_extension(uri, extension_name, cancellation_token, false)
|
||||
.await?;
|
||||
|
||||
contents
|
||||
.into_iter()
|
||||
.find_map(|c| match c.raw {
|
||||
RawContent::Text(text_content) => Some(text_content.text),
|
||||
_ => None,
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
ErrorData::new(
|
||||
ErrorCode::RESOURCE_NOT_FOUND,
|
||||
format!("No text content in resource '{}'", uri),
|
||||
None,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async fn list_resources_from_extension(
|
||||
&self,
|
||||
extension_name: &str,
|
||||
|
||||
Reference in New Issue
Block a user