feat(cli): add mcp prompt support via slash commands (#1323)

This commit is contained in:
Kalvin C
2025-02-27 15:47:29 -08:00
committed by GitHub
parent 5bf05d545e
commit d0ca46983e
24 changed files with 958 additions and 82 deletions
+19 -1
View File
@@ -9,7 +9,8 @@ use std::{
use tokio::process::Command;
use mcp_core::{
handler::{ResourceError, ToolError},
handler::{PromptError, ResourceError, ToolError},
prompt::Prompt,
protocol::ServerCapabilities,
resource::Resource,
tool::Tool,
@@ -819,4 +820,21 @@ impl Router for ComputerControllerRouter {
}
})
}
fn list_prompts(&self) -> Vec<Prompt> {
vec![]
}
fn get_prompt(
&self,
prompt_name: &str,
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
let prompt_name = prompt_name.to_string();
Box::pin(async move {
Err(PromptError::NotFound(format!(
"Prompt {} not found",
prompt_name
)))
})
}
}
+10 -22
View File
@@ -72,9 +72,9 @@ pub fn load_prompt_files() -> HashMap<String, Prompt> {
description: arg.description,
required: arg.required,
})
.collect();
.collect::<Vec<PromptArgument>>();
let prompt = Prompt::new(&template.id, &template.template, arguments);
let prompt = Prompt::new(&template.id, Some(&template.template), Some(arguments));
if prompts.contains_key(&prompt.name) {
eprintln!("Duplicate prompt name '{}' found. Skipping.", prompt.name);
@@ -905,47 +905,35 @@ impl Router for DeveloperRouter {
Box::pin(async move { Ok("".to_string()) })
}
fn list_prompts(&self) -> Option<Vec<Prompt>> {
if self.prompts.is_empty() {
None
} else {
Some(self.prompts.values().cloned().collect())
}
fn list_prompts(&self) -> Vec<Prompt> {
self.prompts.values().cloned().collect()
}
fn get_prompt(
&self,
prompt_name: &str,
) -> Option<Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>>> {
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
let prompt_name = prompt_name.trim().to_owned();
// Validate prompt name is not empty
if prompt_name.is_empty() {
return Some(Box::pin(async move {
return Box::pin(async move {
Err(PromptError::InvalidParameters(
"Prompt name cannot be empty".to_string(),
))
}));
});
}
let prompts = Arc::clone(&self.prompts);
Some(Box::pin(async move {
Box::pin(async move {
match prompts.get(&prompt_name) {
Some(prompt) => {
if prompt.description.trim().is_empty() {
Err(PromptError::InternalError(format!(
"Prompt '{prompt_name}' has an empty description"
)))
} else {
Ok(prompt.description.clone())
}
}
Some(prompt) => Ok(prompt.description.clone().unwrap_or_default()),
None => Err(PromptError::NotFound(format!(
"Prompt '{prompt_name}' not found"
))),
}
}))
})
}
}
+19 -1
View File
@@ -5,7 +5,8 @@ use serde_json::{json, Value};
use std::{env, fs, future::Future, io::Write, path::Path, pin::Pin};
use mcp_core::{
handler::{ResourceError, ToolError},
handler::{PromptError, ResourceError, ToolError},
prompt::Prompt,
protocol::ServerCapabilities,
resource::Resource,
tool::Tool,
@@ -618,6 +619,23 @@ impl Router for GoogleDriveRouter {
let uri_clone = uri.to_string();
Box::pin(async move { this.read_google_resource(uri_clone).await })
}
fn list_prompts(&self) -> Vec<Prompt> {
vec![]
}
fn get_prompt(
&self,
prompt_name: &str,
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
let prompt_name = prompt_name.to_string();
Box::pin(async move {
Err(PromptError::NotFound(format!(
"Prompt {} not found",
prompt_name
)))
})
}
}
impl Clone for GoogleDriveRouter {
+19 -1
View File
@@ -3,7 +3,8 @@ mod proxy;
use anyhow::Result;
use mcp_core::{
content::Content,
handler::{ResourceError, ToolError},
handler::{PromptError, ResourceError, ToolError},
prompt::Prompt,
protocol::ServerCapabilities,
resource::Resource,
role::Role,
@@ -176,6 +177,23 @@ impl Router for JetBrainsRouter {
) -> Pin<Box<dyn Future<Output = Result<String, ResourceError>> + Send + 'static>> {
Box::pin(async { Err(ResourceError::NotFound("Resource not found".into())) })
}
fn list_prompts(&self) -> Vec<Prompt> {
vec![]
}
fn get_prompt(
&self,
prompt_name: &str,
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
let prompt_name = prompt_name.to_string();
Box::pin(async move {
Err(PromptError::NotFound(format!(
"Prompt {} not found",
prompt_name
)))
})
}
}
impl Clone for JetBrainsRouter {
+18 -1
View File
@@ -12,7 +12,8 @@ use std::{
};
use mcp_core::{
handler::{ResourceError, ToolError},
handler::{PromptError, ResourceError, ToolError},
prompt::Prompt,
protocol::ServerCapabilities,
resource::Resource,
tool::{Tool, ToolCall},
@@ -493,6 +494,22 @@ impl Router for MemoryRouter {
) -> Pin<Box<dyn Future<Output = Result<String, ResourceError>> + Send + 'static>> {
Box::pin(async move { Ok("".to_string()) })
}
fn list_prompts(&self) -> Vec<Prompt> {
vec![]
}
fn get_prompt(
&self,
prompt_name: &str,
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
let prompt_name = prompt_name.to_string();
Box::pin(async move {
Err(PromptError::NotFound(format!(
"Prompt {} not found",
prompt_name
)))
})
}
}
#[derive(Debug)]
+19 -1
View File
@@ -5,7 +5,8 @@ use serde_json::{json, Value};
use std::{future::Future, pin::Pin};
use mcp_core::{
handler::{ResourceError, ToolError},
handler::{PromptError, ResourceError, ToolError},
prompt::Prompt,
protocol::ServerCapabilities,
resource::Resource,
role::Role,
@@ -156,6 +157,23 @@ impl Router for TutorialRouter {
) -> Pin<Box<dyn Future<Output = Result<String, ResourceError>> + Send + 'static>> {
Box::pin(async move { Ok("".to_string()) })
}
fn list_prompts(&self) -> Vec<Prompt> {
vec![]
}
fn get_prompt(
&self,
prompt_name: &str,
) -> Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>> {
let prompt_name = prompt_name.to_string();
Box::pin(async move {
Err(PromptError::NotFound(format!(
"Prompt {} not found",
prompt_name
)))
})
}
}
impl Clone for TutorialRouter {