Skip to content

Commit deb8ed0

Browse files
committed
gitlab plugin: add delete file tool
Signed-off-by: Tuan Anh Tran <[email protected]>
1 parent 01b4dc2 commit deb8ed0

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

examples/plugins/gitlab/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ The plugin requires the following configuration:
4141
### Files
4242
- [x] `gl_get_file_contents`: Get file contents
4343
- [x] `gl_create_or_update_file`: Create or update a file
44+
- [x] `gl_delete_file`: Delete a file from the repository
4445
- [ ] `gl_push_files`: Push multiple files
4546

4647
### Branches and Merge Requests

examples/plugins/gitlab/src/lib.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ pub(crate) fn call(input: CallToolRequest) -> Result<CallToolResult, Error> {
139139
// Files
140140
"gl_get_file_contents" => get_file_contents(input),
141141
"gl_create_or_update_file" => create_or_update_file(input),
142+
"gl_delete_file" => delete_file(input),
142143

143144
// Branches
144145
"gl_create_branch" => create_branch(input),
@@ -597,6 +598,97 @@ fn get_file_contents(input: CallToolRequest) -> Result<CallToolResult, Error> {
597598
}
598599
}
599600

601+
fn delete_file(input: CallToolRequest) -> Result<CallToolResult, Error> {
602+
let args = input.params.arguments.clone().unwrap_or_default();
603+
let (token, gitlab_url) = get_gitlab_config()?;
604+
605+
if let (
606+
Some(Value::String(project_id)),
607+
Some(Value::String(file_path)),
608+
Some(Value::String(branch)),
609+
) = (
610+
args.get("project_id"),
611+
args.get("file_path"),
612+
args.get("branch"),
613+
) {
614+
let commit_message = args
615+
.get("commit_message")
616+
.and_then(|v| v.as_str())
617+
.unwrap_or("Delete file via API")
618+
.to_string();
619+
620+
let url = format!(
621+
"{}/projects/{}/repository/files/{}",
622+
gitlab_url,
623+
urlencode_if_needed(project_id),
624+
urlencode_if_needed(file_path)
625+
);
626+
627+
let mut headers = BTreeMap::new();
628+
headers.insert("PRIVATE-TOKEN".to_string(), token);
629+
headers.insert("Content-Type".to_string(), "application/json".to_string());
630+
headers.insert("User-Agent".to_string(), "hyper-mcp/0.1.0".to_string());
631+
632+
let mut body_map = serde_json::Map::new();
633+
body_map.insert("branch".to_string(), json!(branch));
634+
body_map.insert("commit_message".to_string(), json!(commit_message));
635+
if let Some(Value::String(author_email)) = args.get("author_email") {
636+
body_map.insert("author_email".to_string(), json!(author_email));
637+
}
638+
if let Some(Value::String(author_name)) = args.get("author_name") {
639+
body_map.insert("author_name".to_string(), json!(author_name));
640+
}
641+
let body = Value::Object(body_map);
642+
643+
let req = HttpRequest {
644+
url,
645+
headers,
646+
method: Some("DELETE".to_string()),
647+
};
648+
649+
let res = http::request(&req, Some(&body.to_string()))?;
650+
651+
if is_success_status(res.status_code()) {
652+
Ok(CallToolResult {
653+
is_error: None,
654+
content: vec![Content {
655+
annotations: None,
656+
text: Some(String::from_utf8_lossy(&res.body()).to_string()),
657+
mime_type: Some("application/json".to_string()),
658+
r#type: ContentType::Text,
659+
data: None,
660+
}],
661+
})
662+
} else {
663+
Ok(CallToolResult {
664+
is_error: Some(true),
665+
content: vec![Content {
666+
annotations: None,
667+
text: Some(format!(
668+
"Failed to delete file (status {}): {}",
669+
res.status_code(),
670+
String::from_utf8_lossy(&res.body())
671+
)),
672+
mime_type: None,
673+
r#type: ContentType::Text,
674+
data: None,
675+
}],
676+
})
677+
}
678+
} else {
679+
Ok(CallToolResult {
680+
is_error: Some(true),
681+
content: vec![Content {
682+
annotations: None,
683+
text: Some("Please provide project_id, file_path, and branch".into()),
684+
mime_type: None,
685+
r#type: ContentType::Text,
686+
data: None,
687+
}],
688+
})
689+
}
690+
}
691+
600692
fn create_or_update_file(input: CallToolRequest) -> Result<CallToolResult, Error> {
601693
let args = input.params.arguments.clone().unwrap_or_default();
602694
let (token, gitlab_url) = get_gitlab_config()?;
@@ -1722,6 +1814,43 @@ fn gl_get_repo_members(input: CallToolRequest) -> Result<CallToolResult, Error>
17221814
pub(crate) fn describe() -> Result<ListToolsResult, Error> {
17231815
Ok(ListToolsResult {
17241816
tools: vec![
1817+
ToolDescription {
1818+
name: "gl_delete_file".into(),
1819+
description: "Delete a file in a GitLab project repository. Requires project_id, file_path, branch, and optional commit_message.".into(),
1820+
input_schema: json!({
1821+
"type": "object",
1822+
"properties": {
1823+
"project_id": {
1824+
"type": "string",
1825+
"description": "The project identifier - can be a numeric project ID (e.g. '123') or a URL-encoded path (e.g. 'group%2Fproject')",
1826+
},
1827+
"file_path": {
1828+
"type": "string",
1829+
"description": "The path to the file in the project",
1830+
},
1831+
"branch": {
1832+
"type": "string",
1833+
"description": "The name of the branch to delete the file from",
1834+
},
1835+
"commit_message": {
1836+
"type": "string",
1837+
"description": "The commit message. Optional, defaults to 'Delete file via API'",
1838+
},
1839+
"author_email": {
1840+
"type": "string",
1841+
"description": "The email of the commit author. Optional.",
1842+
},
1843+
"author_name": {
1844+
"type": "string",
1845+
"description": "The name of the commit author. Optional.",
1846+
},
1847+
},
1848+
"required": ["project_id", "file_path", "branch"],
1849+
})
1850+
.as_object()
1851+
.unwrap()
1852+
.clone(),
1853+
},
17251854
ToolDescription {
17261855
name: "gl_create_issue".into(),
17271856
description: "Create a new issue in a GitLab project".into(),

0 commit comments

Comments
 (0)