diff --git a/src/aegis_gitea_mcp/mcp_protocol.py b/src/aegis_gitea_mcp/mcp_protocol.py index 079956d..8277bd3 100644 --- a/src/aegis_gitea_mcp/mcp_protocol.py +++ b/src/aegis_gitea_mcp/mcp_protocol.py @@ -718,6 +718,38 @@ AVAILABLE_TOOLS: list[MCPTool] = [ }, write_operation=True, ), + _tool( + "gitea_request", + ( + "Generic escape hatch that calls an arbitrary Gitea REST endpoint " + "(method + path). Prefer the dedicated tools; use this only for " + "endpoints they do not cover. Subject to policy, write-mode and the " + "sensitive-path denylist. Methods other than GET/HEAD are writes and " + "require write-mode plus a whitelisted repository." + ), + { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"], + }, + "path": { + "type": "string", + "description": "Gitea REST path, e.g. /repos/{owner}/{repo}/pulls/1/merge", + }, + "query": {"type": "object", "description": "Optional query-string parameters"}, + "body": {"type": "object", "description": "Optional JSON request body"}, + }, + "required": ["method", "path"], + "additionalProperties": False, + }, + # write_operation is intentionally False: a static flag cannot describe a + # tool that is read OR write depending on the method. Setting it True + # would force the central write-mode gate on GETs and break reads. The + # handler is authoritative via its own per-method authorize() call. + write_operation=False, + ), ] diff --git a/src/aegis_gitea_mcp/server.py b/src/aegis_gitea_mcp/server.py index 087ba18..36d7376 100644 --- a/src/aegis_gitea_mcp/server.py +++ b/src/aegis_gitea_mcp/server.py @@ -60,6 +60,7 @@ from aegis_gitea_mcp.request_context import ( ) from aegis_gitea_mcp.security import sanitize_data from aegis_gitea_mcp.tools.arguments import extract_repository, extract_target_path +from aegis_gitea_mcp.tools.raw_tools import raw_api_request_tool from aegis_gitea_mcp.tools.read_tools import ( compare_refs_tool, get_branch_tool, @@ -420,6 +421,9 @@ TOOL_HANDLERS: dict[str, ToolHandler] = { "create_branch": create_branch_tool, "create_milestone": create_milestone_tool, "edit_issue_comment": edit_issue_comment_tool, + # Generic raw API dispatch (escape hatch). Registered as a read tool so GETs + # work without write-mode; the handler authorizes writes per-method itself. + "gitea_request": raw_api_request_tool, }