Skip to content
Closed
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
10 changes: 5 additions & 5 deletions crates/mcpls-core/src/bridge/translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn diagnostic_request_params(text_document: TextDocumentIdentifier) -> Diagnosti
}

/// Position in a document (1-based for MCP).
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Position2D {
/// Line number (1-based).
pub line: u32,
Expand All @@ -150,7 +150,7 @@ pub struct Position2D {
}

/// Range in a document (1-based for MCP).
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Range {
/// Start position.
pub start: Position2D,
Expand Down Expand Up @@ -191,7 +191,7 @@ pub struct ReferencesResult {
}

/// Diagnostic severity.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum DiagnosticSeverity {
/// Error diagnostic.
Expand All @@ -205,7 +205,7 @@ pub enum DiagnosticSeverity {
}

/// A single diagnostic.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Diagnostic {
/// Range where the diagnostic applies.
pub range: Range,
Expand All @@ -218,7 +218,7 @@ pub struct Diagnostic {
}

/// Result of a diagnostics request.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct DiagnosticsResult {
/// List of diagnostics for the document.
pub diagnostics: Vec<Diagnostic>,
Expand Down
42 changes: 37 additions & 5 deletions crates/mcpls-core/src/mcp/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use rmcp::model::{
ReadResourceResult, ResourceContents, ServerCapabilities, ServerInfo, SubscribeRequestParams,
UnsubscribeRequestParams,
};
use rmcp::{ErrorData as McpError, RoleServer, ServerHandler, tool, tool_handler, tool_router};
use rmcp::{
ErrorData as McpError, Json, RoleServer, ServerHandler, tool, tool_handler, tool_router,
};
use tokio::sync::Mutex;

use super::handlers::HandlerContext;
Expand All @@ -23,7 +25,7 @@ use super::tools::{
ServerLogsParams, ServerMessagesParams, SignatureHelpParams, WorkspaceSymbolParams,
};
use crate::bridge::resources::{make_uri, parse_uri};
use crate::bridge::{ResourceSubscriptions, Translator};
use crate::bridge::{DiagnosticsResult, ResourceSubscriptions, Translator};

/// MCP server that exposes LSP capabilities as tools.
#[derive(Clone)]
Expand Down Expand Up @@ -127,15 +129,14 @@ impl McplsServer {
async fn get_diagnostics(
&self,
Parameters(DiagnosticsParams { file_path }): Parameters<DiagnosticsParams>,
) -> Result<String, McpError> {
) -> Result<Json<DiagnosticsResult>, McpError> {
let result = {
let mut translator = self.context.translator.lock().await;
translator.handle_diagnostics(file_path).await
};

match result {
Ok(value) => serde_json::to_string(&value)
.map_err(|e| McpError::internal_error(format!("Serialization error: {e}"), None)),
Ok(value) => Ok(Json(value)),
Err(e) => Err(McpError::internal_error(e.to_string(), None)),
}
}
Expand Down Expand Up @@ -706,6 +707,37 @@ mod tests {
assert!(info.instructions.is_some());
}

#[test]
fn test_get_diagnostics_tool_has_output_schema() {
let tool = McplsServer::get_diagnostics_tool_attr();
let Some(schema) = tool.output_schema.as_ref() else {
panic!("get_diagnostics should declare an output schema");
};
let schema_json = serde_json::to_string(schema).unwrap();

assert!(schema_json.contains("diagnostics"));
}

#[test]
fn test_diagnostics_result_converts_to_structured_content() {
use rmcp::handler::server::tool::IntoCallToolResult;

let result = Json(DiagnosticsResult {
diagnostics: Vec::new(),
})
.into_call_tool_result()
.unwrap();

let Some(structured) = result.structured_content.as_ref() else {
panic!("diagnostics result should include structured content");
};
assert_eq!(structured["diagnostics"].as_array().unwrap().len(), 0);

let text = result.content[0].as_text().unwrap();
let content_json: serde_json::Value = serde_json::from_str(&text.text).unwrap();
assert_eq!(&content_json, structured);
}

#[tokio::test]
async fn test_hover_tool_with_params() {
let server = create_test_server();
Expand Down