[MCP] Add support for resources (#5239)
This PR adds support for [MCP resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources) by adding three new tools for the model: 1. `list_resources` 2. `list_resource_templates` 3. `read_resource` These 3 tools correspond to the [three primary MCP resource protocol messages](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#protocol-messages). Example of listing and reading a GitHub resource tempalte <img width="2984" height="804" alt="CleanShot 2025-10-15 at 17 31 10" src="https://github.com/user-attachments/assets/89b7f215-2e2a-41c5-90dd-b932ac84a585" /> `/mcp` with Figma configured <img width="2984" height="442" alt="CleanShot 2025-10-15 at 18 29 35" src="https://github.com/user-attachments/assets/a7578080-2ed2-4c59-b9b4-d8461f90d8ee" /> Fixes #4956
This commit is contained in:
@@ -2071,6 +2071,8 @@ impl ChatWidget {
|
||||
self.add_to_history(history_cell::new_mcp_tools_output(
|
||||
&self.config,
|
||||
ev.tools,
|
||||
ev.resources,
|
||||
ev.resource_templates,
|
||||
&ev.auth_statuses,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ use codex_protocol::plan_tool::UpdatePlanArgs;
|
||||
use image::DynamicImage;
|
||||
use image::ImageReader;
|
||||
use mcp_types::EmbeddedResourceResource;
|
||||
use mcp_types::Resource;
|
||||
use mcp_types::ResourceLink;
|
||||
use mcp_types::ResourceTemplate;
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::style::Modifier;
|
||||
use ratatui::style::Style;
|
||||
@@ -976,6 +978,8 @@ pub(crate) fn empty_mcp_output() -> PlainHistoryCell {
|
||||
pub(crate) fn new_mcp_tools_output(
|
||||
config: &Config,
|
||||
tools: HashMap<String, mcp_types::Tool>,
|
||||
resources: HashMap<String, Vec<Resource>>,
|
||||
resource_templates: HashMap<String, Vec<ResourceTemplate>>,
|
||||
auth_statuses: &HashMap<String, McpAuthStatus>,
|
||||
) -> PlainHistoryCell {
|
||||
let mut lines: Vec<Line<'static>> = vec![
|
||||
@@ -1073,12 +1077,64 @@ pub(crate) fn new_mcp_tools_output(
|
||||
}
|
||||
|
||||
if !cfg.enabled {
|
||||
lines.push(vec![" • Tools: ".into(), "(disabled)".red()].into());
|
||||
} else if names.is_empty() {
|
||||
let disabled = "(disabled)".red();
|
||||
lines.push(vec![" • Tools: ".into(), disabled.clone()].into());
|
||||
lines.push(vec![" • Resources: ".into(), disabled.clone()].into());
|
||||
lines.push(vec![" • Resource templates: ".into(), disabled].into());
|
||||
lines.push(Line::from(""));
|
||||
continue;
|
||||
}
|
||||
|
||||
if names.is_empty() {
|
||||
lines.push(" • Tools: (none)".into());
|
||||
} else {
|
||||
lines.push(vec![" • Tools: ".into(), names.join(", ").into()].into());
|
||||
}
|
||||
|
||||
let server_resources: Vec<Resource> =
|
||||
resources.get(server.as_str()).cloned().unwrap_or_default();
|
||||
if server_resources.is_empty() {
|
||||
lines.push(" • Resources: (none)".into());
|
||||
} else {
|
||||
let mut spans: Vec<Span<'static>> = vec![" • Resources: ".into()];
|
||||
|
||||
for (idx, resource) in server_resources.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
spans.push(", ".into());
|
||||
}
|
||||
|
||||
let label = resource.title.as_ref().unwrap_or(&resource.name);
|
||||
spans.push(label.clone().into());
|
||||
spans.push(" ".into());
|
||||
spans.push(format!("({})", resource.uri).dim());
|
||||
}
|
||||
|
||||
lines.push(spans.into());
|
||||
}
|
||||
|
||||
let server_templates: Vec<ResourceTemplate> = resource_templates
|
||||
.get(server.as_str())
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
if server_templates.is_empty() {
|
||||
lines.push(" • Resource templates: (none)".into());
|
||||
} else {
|
||||
let mut spans: Vec<Span<'static>> = vec![" • Resource templates: ".into()];
|
||||
|
||||
for (idx, template) in server_templates.iter().enumerate() {
|
||||
if idx > 0 {
|
||||
spans.push(", ".into());
|
||||
}
|
||||
|
||||
let label = template.title.as_ref().unwrap_or(&template.name);
|
||||
spans.push(label.clone().into());
|
||||
spans.push(" ".into());
|
||||
spans.push(format!("({})", template.uri_template).dim());
|
||||
}
|
||||
|
||||
lines.push(spans.into());
|
||||
}
|
||||
|
||||
lines.push(Line::from(""));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user