chadtmiller.com/knotless
Git hosted in your atproto account, on any stock PDS, fast enough for agents.
git clone knotless://chadtmiller.com/knotlessneeds the knotless git helper. set it up
MCP: edit_item and delete_comment tools
Showing 1 changed file with 51 additions and 1 deletion
crates/knotless-mcp/src/lib.rs+51−1
| @@ -363,6 +363,24 @@ pub struct Remark { | ||
pub verdict: Option<String>, | ||
} | ||
| ||
#[derive(Deserialize, schemars::JsonSchema)] | ||
pub struct Edit { | ||
pub repo: String, | ||
/// The at:// URI of your pull, issue or comment. | ||
pub uri: String, | ||
/// A pull's or issue's new title. | ||
pub title: Option<String>, | ||
/// The new body, in Markdown. Empty takes a pull's or issue's body away. | ||
pub body: Option<String>, | ||
} | ||
| ||
#[derive(Deserialize, schemars::JsonSchema)] | ||
pub struct Unsay { | ||
pub repo: String, | ||
/// The at:// URI of your comment or review. | ||
pub uri: String, | ||
} | ||
| ||
#[derive(Deserialize, schemars::JsonSchema)] | ||
pub struct SetState { | ||
pub repo: String, | ||
| @@ -789,6 +807,7 @@ impl Server { | ||
"verdict": c.value["verdict"], | ||
"commit": c.value["commit"], | ||
"createdAt": c.value["createdAt"], | ||
"updatedAt": c.value["updatedAt"], | ||
}) | ||
}) | ||
.collect(); | ||
| @@ -858,6 +877,37 @@ impl Server { | ||
) | ||
} | ||
| ||
#[tool( | ||
description = "Change what your pull request, issue or comment says: a pull's or issue's title and body, or a comment's body. Only its author can. This is the way to update a pull's description." | ||
)] | ||
async fn edit_item(&self, Parameters(p): Parameters<Edit>) -> Result<CallToolResult, McpError> { | ||
answer( | ||
async { | ||
let url = repo_url(&p.repo)?; | ||
let (title, body) = (p.title.as_deref(), p.body.as_deref()); | ||
forge::edit(&self.ctx, &url, &p.uri, title, body).await?; | ||
Ok(json!({"edited": p.uri})) | ||
} | ||
.await, | ||
) | ||
} | ||
| ||
#[tool( | ||
description = "Delete your comment or review. Only its author can; a repository's owner hides others' with hide_item instead. Pulls and issues are closed with set_state, not deleted." | ||
)] | ||
async fn delete_comment( | ||
&self, | ||
Parameters(p): Parameters<Unsay>, | ||
) -> Result<CallToolResult, McpError> { | ||
answer( | ||
async { | ||
forge::delete_comment(&self.ctx, &repo_url(&p.repo)?, &p.uri).await?; | ||
Ok(json!({"deleted": p.uri})) | ||
} | ||
.await, | ||
) | ||
} | ||
| ||
#[tool( | ||
description = "Close or reopen a pull request or issue. Its author, the owner and collaborators may." | ||
)] | ||
| @@ -1043,7 +1093,7 @@ impl Server { | ||
| ||
#[tool_handler( | ||
name = "knotless", | ||
instructions = "Git repositories stored in atproto accounts. Name a repository as handle/name. Read with list_tree, read_file, search, log and diff: no clone needed. Change files with commit: no checkout needed. Collaborate with fork, open_pull, open_issue, list_items, show_item, comment, set_state and merge. Moderate your repositories with hide_item and block_account, and report with report." | ||
instructions = "Git repositories stored in atproto accounts. Name a repository as handle/name. Read with list_tree, read_file, search, log and diff: no clone needed. Change files with commit: no checkout needed. Collaborate with fork, open_pull, open_issue, list_items, show_item, comment, set_state and merge; change what you wrote with edit_item, and take a comment back with delete_comment. Moderate your repositories with hide_item and block_account, and report with report." | ||
)] | ||
impl ServerHandler for Server {} | ||