use rmcp::schemars;
use serde::{Deserialize, Serialize};
use super::cursor::Cursor;
use super::types::default_true;
use crate::path::RelPath;
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct WorkingTreeStatusParams {}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct RecentChangesParams {
#[serde(default)]
pub limit: Option<u32>,
#[serde(default = "default_true")]
pub include_files: bool,
#[serde(default)]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct CommitsTouchingParams {
pub path: RelPath,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct DiffOutlineParams {
pub path: RelPath,
#[serde(default)]
pub rev: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct BlameFileParams {
pub path: RelPath,
#[serde(default)]
pub line_start: Option<u32>,
#[serde(default)]
pub line_end: Option<u32>,
#[serde(default)]
pub rev: Option<String>,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct FindCommitsByPathParams {
#[serde(
alias = "query",
alias = "needle",
alias = "regex",
alias = "q",
alias = "search"
)]
pub pattern: String,
#[serde(default)]
pub window: Option<u32>,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct HotFilesParams {
#[serde(default)]
pub window: Option<u32>,
#[serde(default)]
pub top_k: Option<u32>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct DiffFileParams {
pub rev_old: String,
pub rev_new: String,
pub path: RelPath,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct SymbolHistoryParams {
pub path: RelPath,
#[serde(alias = "symbol", alias = "needle", alias = "query")]
pub name: String,
#[serde(default)]
pub kind: Option<String>,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub hash_mode: Option<String>,
#[serde(default)]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Deserialize, Serialize, schemars::JsonSchema)]
pub struct BlameSymbolParams {
pub path: RelPath,
#[serde(alias = "symbol", alias = "needle", alias = "query")]
pub name: String,
#[serde(default)]
pub kind: Option<String>,
#[serde(default)]
pub rev: Option<String>,
#[serde(default)]
pub limit: Option<u32>,
#[serde(default)]
pub cursor: Option<Cursor>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct CommitView {
pub sha: String,
pub short_sha: String,
pub summary: String,
pub author: String,
pub author_time_unix: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub files: Option<Vec<CommitFileView>>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct CommitFileView {
pub path: RelPath,
pub change: &'static str,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct WorkingTreeStatusView {
pub staged_added: Vec<RelPath>,
pub staged_modified: Vec<RelPath>,
pub staged_deleted: Vec<RelPath>,
pub modified: Vec<RelPath>,
pub untracked: Vec<RelPath>,
pub is_clean: bool,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct RecentChangesResponse {
pub commits: Vec<CommitView>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub truncated_reason: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub cursor_invalidated: bool,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct CommitsTouchingResponse {
pub path: RelPath,
pub commits: Vec<CommitView>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub truncated_reason: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub cursor_invalidated: bool,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct DiffSymbolView {
pub name: String,
pub kind: String,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct DiffOutlineResponse {
pub path: RelPath,
pub rev: String,
pub added: Vec<DiffSymbolView>,
pub removed: Vec<DiffSymbolView>,
pub common: Vec<DiffSymbolView>,
#[serde(skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct BlameHunkView {
pub commit_sha: String,
pub short_sha: String,
pub start_line: u32,
pub len: u32,
pub source_start_line: u32,
pub author: String,
pub author_time_unix: i64,
pub summary: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_path: Option<RelPath>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct BlameResponse {
pub path: RelPath,
pub suspect_sha: String,
pub hunks: Vec<BlameHunkView>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub truncated_reason: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct BlameSymbolResponse {
pub path: RelPath,
pub suspect_sha: String,
pub name: String,
pub kind: String,
pub line_start: u32,
pub line_end: u32,
pub hunks: Vec<BlameHunkView>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub truncated_reason: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct FindCommitsByPathResponse {
pub pattern: String,
pub window_inspected: u32,
pub commits: Vec<CommitView>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub cursor_invalidated: bool,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct HotFileEntry {
pub path: RelPath,
pub commits_touching: u32,
pub added: u32,
pub modified: u32,
pub deleted: u32,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct HotFilesResponse {
pub window_inspected: u32,
pub total_files_changed: u32,
pub files: Vec<HotFileEntry>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct HunkView {
pub kind: &'static str,
pub old_line_start: u32,
pub old_line_count: u32,
pub new_line_start: u32,
pub new_line_count: u32,
pub text: String,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct DiffFileResponse {
pub path: RelPath,
pub rev_old: String,
pub rev_new: String,
pub present_at_old: bool,
pub present_at_new: bool,
pub hunks: Vec<HunkView>,
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct SymbolHistoryEntry {
pub sha: String,
pub short_sha: String,
pub summary: String,
pub author: String,
pub author_time_unix: i64,
pub change: &'static str,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_commits_by_path_accepts_query_alias_for_pattern() {
let params: FindCommitsByPathParams =
serde_json::from_value(serde_json::json!({ "query": "fix:" })).unwrap();
assert_eq!(params.pattern, "fix:");
}
#[test]
fn symbol_history_accepts_symbol_alias_for_name() {
let params: SymbolHistoryParams =
serde_json::from_value(serde_json::json!({ "path": "src/lib.rs", "symbol": "scan" }))
.unwrap();
assert_eq!(params.name, "scan");
}
#[test]
fn blame_symbol_accepts_needle_alias_for_name() {
let params: BlameSymbolParams =
serde_json::from_value(serde_json::json!({ "path": "src/lib.rs", "needle": "scan" }))
.unwrap();
assert_eq!(params.name, "scan");
}
}
#[derive(Debug, Serialize)]
pub(in crate::mcp) struct SymbolHistoryResponse {
pub path: RelPath,
pub name: String,
pub kind: Option<String>,
pub commits_inspected: u32,
pub history: Vec<SymbolHistoryEntry>,
pub hash_mode: &'static str,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub truncated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub truncated_reason: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<Cursor>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub cursor_invalidated: bool,
}