1use std::path::PathBuf;
2
3use async_lsp::LanguageServer;
4use async_lsp::lsp_types::{
5 DocumentDiagnosticParams, DocumentDiagnosticReportResult, PartialResultParams,
6 TextDocumentIdentifier, Url, WorkDoneProgressParams,
7};
8use rmcp::ErrorData as MCPError;
9use rmcp::{
10 handler::server::wrapper::Parameters, model::CallToolResult, schemars, serde_json,
11};
12
13use crate::mcp::*;
14
15#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
16pub struct ReadToolArgs {
17 pub path: PathBuf,
18 pub range_start: usize,
19 pub range_end: usize,
20}
21
22#[derive(Debug, serde::Serialize)]
23pub struct ReadToolOutput {
24 pub content: Option<String>,
25 pub diagnostics: DocumentDiagnosticReportResult,
26}
27
28pub async fn call(
29 server: &MCPServer,
30 Parameters(args): Parameters<ReadToolArgs>,
31) -> Result<CallToolResult, MCPError> {
32 let file_path = server.config.project_root.join(&args.path);
33 let file_path = tokio::fs::canonicalize(file_path).await.unwrap();
34 let content = tokio::fs::read_to_string(&file_path)
35 .await
36 .map_err(|e| MCPError::invalid_request(format!("Failed to read file: {e}"), None))?;
37
38 let mut lsp_server = server.lsp_server.clone();
39
40 let diagnostic_report = lsp_server
41 .document_diagnostic(DocumentDiagnosticParams {
42 text_document: TextDocumentIdentifier::new(Url::from_file_path(file_path).unwrap()),
43 identifier: None,
44 previous_result_id: None,
45 work_done_progress_params: WorkDoneProgressParams {
46 work_done_token: None,
47 },
48 partial_result_params: PartialResultParams {
49 partial_result_token: None,
50 },
51 })
52 .await
53 .unwrap();
54
55 Ok(CallToolResult {
56 content: vec![],
57 structured_content: Some(serde_json::json!(ReadToolOutput {
58 content: Some(content),
59 diagnostics: diagnostic_report
60 })),
61 is_error: None,
62 meta: None,
63 })
64}