Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/src/ai/agent/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use itertools::Itertools;
use prost_types::FieldMask;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use warp_core::datetime_ext::DateTimeExt;
use warp_multi_agent_api::{
self as api,
message::{tool_call::subagent::Metadata, Message},
Expand All @@ -24,7 +25,6 @@ use crate::{
agent::comment::CodeReview,
document::ai_document_model::{AIDocumentId, AIDocumentVersion},
},
server::datetime_ext::DateTimeExt,
terminal::model::block::BlockId,
AIAgentTodoList,
};
Expand Down
2 changes: 1 addition & 1 deletion app/src/ai/mcp/gallery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use crate::ai::mcp::templatable::{
GalleryData, JsonTemplate, TemplatableMCPServer, TemplateVariable,
};
use crate::server::cloud_objects::update_manager::{UpdateManager, UpdateManagerEvent};
use crate::server::datetime_ext::DateTimeExt;
use chrono::DateTime;
use uuid::Uuid;
use warp_core::datetime_ext::DateTimeExt;
use warpui::{Entity, ModelContext, SingletonEntity};

#[derive(Clone, Debug)]
Expand Down
29 changes: 0 additions & 29 deletions app/src/ai/mcp/http_client.rs

This file was deleted.

6 changes: 2 additions & 4 deletions app/src/ai/mcp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#[cfg(not(target_family = "wasm"))]
use crate::server::datetime_ext::DateTimeExt;
#[cfg(not(target_family = "wasm"))]
use chrono::DateTime;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[cfg(not(target_family = "wasm"))]
use warp_core::datetime_ext::DateTimeExt;

#[cfg(not(target_family = "wasm"))]
use crate::persistence::model::MCPEnvironmentVariables;
Expand Down Expand Up @@ -67,8 +67,6 @@ pub use templatable_installation::{VariableType, VariableValue};
pub mod parsing;
pub use parsing::ParsedTemplatableMCPServerResult;
#[cfg(not(target_family = "wasm"))]
pub mod http_client;
#[cfg(not(target_family = "wasm"))]
pub mod reconnecting_peer;

impl CloudObjectUuid for MCPServer {
Expand Down
10 changes: 4 additions & 6 deletions app/src/ai/mcp/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ use std::collections::HashMap;

use chrono::DateTime;
use handlebars::{get_arguments, render_template};
use warp_core::datetime_ext::DateTimeExt;

#[cfg(feature = "local_fs")]
use serde::Deserialize;

#[cfg(feature = "local_fs")]
use crate::ai::mcp::{JSONMCPServer, JSONTransportType};

use crate::{
ai::mcp::{
templatable::{JsonTemplate, TemplatableMCPServer, TemplateVariable},
templatable_installation::{TemplatableMCPServerInstallation, VariableType, VariableValue},
},
server::datetime_ext::DateTimeExt,
use crate::ai::mcp::{
templatable::{JsonTemplate, TemplatableMCPServer, TemplateVariable},
templatable_installation::{TemplatableMCPServerInstallation, VariableType, VariableValue},
};

/// Normalize MCP JSON input to ensure it has a server name wrapper.
Expand Down
88 changes: 13 additions & 75 deletions app/src/ai/mcp/templatable_manager.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
#[cfg(not(target_family = "wasm"))]
mod native;
#[cfg(not(target_family = "wasm"))]
pub use native::McpIntegration;
#[cfg(not(target_family = "wasm"))]
mod oauth;
#[cfg(not(target_family = "wasm"))]
mod utils;
#[cfg(target_family = "wasm")]
mod wasm;

#[cfg(all(test, not(target_family = "wasm")))]
mod utils_tests;

#[cfg(not(target_family = "wasm"))]
use diesel::SqliteConnection;
#[cfg(not(target_family = "wasm"))]
use mcp::oauth;
#[cfg(not(target_family = "wasm"))]
pub use native::McpIntegration;
#[cfg(not(target_family = "wasm"))]
use parking_lot::Mutex;
use std::collections::{HashMap, HashSet};
#[cfg(not(target_family = "wasm"))]
Expand All @@ -25,6 +20,7 @@ use crate::ai::mcp::templatable::CloudTemplatableMCPServer;
use crate::ai::mcp::FileBasedMCPManager;
use crate::ai::mcp::{templatable_installation::TemplatableMCPServerInstallation, MCPServerState};
use futures_util::stream::AbortHandle;
pub use mcp::runtime::TemplatableMCPServerInfo;
use uuid::Uuid;
#[cfg(not(target_family = "wasm"))]
use warpui::ModelSpawner;
Expand Down Expand Up @@ -97,48 +93,6 @@ struct SpawnedServerInfo {
oauth_result_tx: async_channel::Sender<oauth::CallbackResult>,
}

/// Information about a single connected MCP server.
#[cfg_attr(target_family = "wasm", allow(dead_code))]
pub struct TemplatableMCPServerInfo {
name: String,
service: rmcp::service::RunningService<
rmcp::RoleClient,
Box<dyn rmcp::service::DynService<rmcp::RoleClient>>,
>,
resources: Vec<rmcp::model::Resource>,
tools: Vec<rmcp::model::Tool>,
installation_id: Uuid,
description: Option<String>,
/// Whether the underlying transport uses authentication.
///
/// TODO(vorporeal): Use this to display a toast when server authentication and connection is complete, and
/// to provide a "log out" button.
#[allow(dead_code)]
is_authenticated_transport: bool,
}

impl TemplatableMCPServerInfo {
pub fn name(&self) -> &str {
&self.name
}

pub fn resources(&self) -> &Vec<rmcp::model::Resource> {
&self.resources
}

pub fn tools(&self) -> &Vec<rmcp::model::Tool> {
&self.tools
}

pub fn installation_id(&self) -> Uuid {
self.installation_id
}

pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
}

/// The current status of the Figma MCP server.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FigmaMcpStatus {
Expand Down Expand Up @@ -221,13 +175,13 @@ impl TemplatableMCPServerManager {
pub fn resources(&self) -> impl Iterator<Item = &rmcp::model::Resource> {
self.active_servers
.values()
.flat_map(|server| server.resources.iter())
.flat_map(|server| server.resources().iter())
}

pub fn tools(&self) -> impl Iterator<Item = &rmcp::model::Tool> {
self.active_servers
.values()
.flat_map(|server| server.tools.iter())
.flat_map(|server| server.tools().iter())
}

/// Returns a reconnecting peer for a server that has the given resource.
Expand All @@ -241,12 +195,7 @@ impl TemplatableMCPServerManager {
let spawner = self.spawner.as_ref()?;
self.active_servers
.iter()
.find(|(_, server)| {
server
.resources
.iter()
.any(|other_resource| resource.uri == other_resource.uri)
})
.find(|(_, server)| server.has_resource(resource))
.map(|(installation_uuid, _)| {
super::reconnecting_peer::ReconnectingPeer::new(*installation_uuid, spawner.clone())
})
Expand All @@ -255,7 +204,7 @@ impl TemplatableMCPServerManager {
pub fn tools_for_server(&self, uuid: Uuid) -> Vec<rmcp::model::Tool> {
self.active_servers
.get(&uuid)
.map(|server| server.tools.clone())
.map(|server| server.tools().clone())
.unwrap_or_default()
}

Expand All @@ -273,24 +222,21 @@ impl TemplatableMCPServerManager {
installation_id: Option<Uuid>,
tool_name: &str,
) -> Option<std::sync::Arc<rmcp::model::JsonObject>> {
let candidates: Box<dyn Iterator<Item = &TemplatableMCPServerInfo>> =
let mut candidates: Box<dyn Iterator<Item = &TemplatableMCPServerInfo>> =
if let Some(uuid) = installation_id {
Box::new(self.active_servers.get(&uuid).into_iter())
} else {
Box::new(self.active_servers.values())
};

candidates
.flat_map(|server| server.tools.iter())
.find(|t| t.name == tool_name)
.map(|t| t.input_schema.clone())
candidates.find_map(|server| server.tool_input_schema(tool_name))
}

#[cfg(not(target_family = "wasm"))]
pub fn server_from_tool(&self, tool: String) -> Option<&Uuid> {
self.active_servers
.iter()
.find(|(_, server)| server.tools.iter().any(|t| t.name == tool))
.find(|(_, server)| server.has_tool(&tool))
.map(|(uuid, _)| uuid)
}

Expand All @@ -300,15 +246,7 @@ impl TemplatableMCPServerManager {
pub fn server_from_resource(&self, name: &str, uri: Option<&str>) -> Option<&Uuid> {
self.active_servers
.iter()
.find(|(_, server)| {
server.resources.iter().any(|r| {
if let Some(uri) = uri {
r.uri == uri
} else {
r.name == name
}
})
})
.find(|(_, server)| server.has_resource_name_or_uri(name, uri))
.map(|(uuid, _)| uuid)
}

Expand Down
Loading