mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-17 22:24:21 +01:00
feat(cli): add mcp prompt support via slash commands (#1323)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::handler::ResourceError;
|
||||
use mcp_core::handler::{PromptError, ResourceError};
|
||||
use mcp_core::prompt::{Prompt, PromptArgument};
|
||||
use mcp_core::{handler::ToolError, protocol::ServerCapabilities, resource::Resource, tool::Tool};
|
||||
use mcp_server::router::{CapabilitiesBuilder, RouterService};
|
||||
use mcp_server::{ByteTransport, Router, Server};
|
||||
@@ -61,6 +62,7 @@ impl Router for CounterRouter {
|
||||
CapabilitiesBuilder::new()
|
||||
.with_tools(false)
|
||||
.with_resources(false, false)
|
||||
.with_prompts(false)
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -153,6 +155,37 @@ impl Router for CounterRouter {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn list_prompts(&self) -> Vec<Prompt> {
|
||||
vec![Prompt::new(
|
||||
"example_prompt",
|
||||
Some("This is an example prompt that takes one required agrument, message"),
|
||||
Some(vec![PromptArgument {
|
||||
name: "message".to_string(),
|
||||
description: Some("A message to put in the prompt".to_string()),
|
||||
required: Some(true),
|
||||
}]),
|
||||
)]
|
||||
}
|
||||
|
||||
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 {
|
||||
match prompt_name.as_str() {
|
||||
"example_prompt" => {
|
||||
let prompt = "This is an example prompt with your message here: '{message}'";
|
||||
Ok(prompt.to_string())
|
||||
}
|
||||
_ => Err(PromptError::NotFound(format!(
|
||||
"Prompt {} not found",
|
||||
prompt_name
|
||||
))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -97,12 +97,8 @@ pub trait Router: Send + Sync + 'static {
|
||||
&self,
|
||||
uri: &str,
|
||||
) -> Pin<Box<dyn Future<Output = Result<String, ResourceError>> + Send + 'static>>;
|
||||
fn list_prompts(&self) -> Option<Vec<Prompt>> {
|
||||
None
|
||||
}
|
||||
fn get_prompt(&self, _prompt_name: &str) -> Option<PromptFuture> {
|
||||
None
|
||||
}
|
||||
fn list_prompts(&self) -> Vec<Prompt>;
|
||||
fn get_prompt(&self, prompt_name: &str) -> PromptFuture;
|
||||
|
||||
// Helper method to create base response
|
||||
fn create_response(&self, id: Option<u64>) -> JsonRpcResponse {
|
||||
@@ -257,7 +253,7 @@ pub trait Router: Send + Sync + 'static {
|
||||
req: JsonRpcRequest,
|
||||
) -> impl Future<Output = Result<JsonRpcResponse, RouterError>> + Send {
|
||||
async move {
|
||||
let prompts = self.list_prompts().unwrap_or_default();
|
||||
let prompts = self.list_prompts();
|
||||
|
||||
let result = ListPromptsResult { prompts };
|
||||
|
||||
@@ -294,36 +290,36 @@ pub trait Router: Send + Sync + 'static {
|
||||
.ok_or_else(|| RouterError::InvalidParams("Missing arguments object".into()))?;
|
||||
|
||||
// Fetch the prompt definition first
|
||||
let prompt = match self.list_prompts() {
|
||||
Some(prompts) => prompts
|
||||
.into_iter()
|
||||
.find(|p| p.name == prompt_name)
|
||||
.ok_or_else(|| {
|
||||
RouterError::PromptNotFound(format!("Prompt '{}' not found", prompt_name))
|
||||
})?,
|
||||
None => return Err(RouterError::PromptNotFound("No prompts available".into())),
|
||||
};
|
||||
let prompt = self
|
||||
.list_prompts()
|
||||
.into_iter()
|
||||
.find(|p| p.name == prompt_name)
|
||||
.ok_or_else(|| {
|
||||
RouterError::PromptNotFound(format!("Prompt '{}' not found", prompt_name))
|
||||
})?;
|
||||
|
||||
// Validate required arguments
|
||||
for arg in &prompt.arguments {
|
||||
if arg.required
|
||||
&& (!arguments.contains_key(&arg.name)
|
||||
|| arguments
|
||||
.get(&arg.name)
|
||||
.and_then(Value::as_str)
|
||||
.is_none_or(str::is_empty))
|
||||
{
|
||||
return Err(RouterError::InvalidParams(format!(
|
||||
"Missing required argument: '{}'",
|
||||
arg.name
|
||||
)));
|
||||
if let Some(args) = &prompt.arguments {
|
||||
for arg in args {
|
||||
if arg.required.is_some()
|
||||
&& arg.required.unwrap()
|
||||
&& (!arguments.contains_key(&arg.name)
|
||||
|| arguments
|
||||
.get(&arg.name)
|
||||
.and_then(Value::as_str)
|
||||
.is_none_or(str::is_empty))
|
||||
{
|
||||
return Err(RouterError::InvalidParams(format!(
|
||||
"Missing required argument: '{}'",
|
||||
arg.name
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now get the prompt content
|
||||
let description = self
|
||||
.get_prompt(prompt_name)
|
||||
.ok_or_else(|| RouterError::PromptNotFound("Prompt not found".into()))?
|
||||
.await
|
||||
.map_err(|e| RouterError::Internal(e.to_string()))?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user