mirror of
https://github.com/omnara-ai/omnara.git
synced 2025-08-12 20:39:09 +03:00
Claude code hooks (#16)
* intermediate commit * auth sent to webhook * mock * mock --------- Co-authored-by: Kartik Sarangmath <kartiksarangmath@Kartiks-MacBook-Air.local>
This commit is contained in:
@@ -29,6 +29,9 @@ logger = logging.getLogger(__name__)
|
||||
# Global client instance
|
||||
client: Optional[AsyncOmnaraClient] = None
|
||||
|
||||
# Global state for current agent instance (primarily used for claude code approval tool)
|
||||
current_agent_instance_id: Optional[str] = None
|
||||
|
||||
|
||||
def get_client() -> AsyncOmnaraClient:
|
||||
"""Get the initialized AsyncOmnaraClient instance."""
|
||||
@@ -48,6 +51,8 @@ async def log_step_tool(
|
||||
agent_instance_id: str | None = None,
|
||||
step_description: str = "",
|
||||
) -> LogStepResponse:
|
||||
global current_agent_instance_id
|
||||
|
||||
agent_type = detect_agent_type_from_environment()
|
||||
client = get_client()
|
||||
|
||||
@@ -57,6 +62,9 @@ async def log_step_tool(
|
||||
agent_instance_id=agent_instance_id,
|
||||
)
|
||||
|
||||
# Store the instance ID for use by other tools
|
||||
current_agent_instance_id = response.agent_instance_id
|
||||
|
||||
return LogStepResponse(
|
||||
success=response.success,
|
||||
agent_instance_id=response.agent_instance_id,
|
||||
@@ -73,6 +81,8 @@ async def ask_question_tool(
|
||||
agent_instance_id: str | None = None,
|
||||
question_text: str | None = None,
|
||||
) -> AskQuestionResponse:
|
||||
global current_agent_instance_id
|
||||
|
||||
if not agent_instance_id:
|
||||
raise ValueError("agent_instance_id is required")
|
||||
if not question_text:
|
||||
@@ -80,12 +90,15 @@ async def ask_question_tool(
|
||||
|
||||
client = get_client()
|
||||
|
||||
# Store the instance ID for use by other tools
|
||||
current_agent_instance_id = agent_instance_id
|
||||
|
||||
try:
|
||||
response = await client.ask_question(
|
||||
agent_instance_id=agent_instance_id,
|
||||
question_text=question_text,
|
||||
timeout_minutes=1440, # 24 hours default
|
||||
poll_interval=1.0,
|
||||
poll_interval=10.0,
|
||||
)
|
||||
|
||||
return AskQuestionResponse(
|
||||
@@ -116,6 +129,69 @@ async def end_session_tool(
|
||||
)
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
name="approve",
|
||||
description="Handle permission prompts for Claude Code. Returns approval/denial for tool execution.",
|
||||
enabled=False,
|
||||
)
|
||||
async def approve_tool(
|
||||
tool_name: str,
|
||||
input: dict,
|
||||
tool_use_id: Optional[str] = None,
|
||||
) -> dict:
|
||||
"""Claude Code permission prompt handler."""
|
||||
global current_agent_instance_id
|
||||
|
||||
if not tool_name:
|
||||
raise ValueError("tool_name is required")
|
||||
|
||||
client = get_client()
|
||||
|
||||
# Format the permission request as a question
|
||||
question_text = f"Allow execution of {tool_name}? Input: {input}"
|
||||
|
||||
try:
|
||||
# Use existing instance ID or create a new one
|
||||
if current_agent_instance_id:
|
||||
instance_id = current_agent_instance_id
|
||||
else:
|
||||
# Only create a new instance if we don't have one
|
||||
response = await client.log_step(
|
||||
agent_type="Claude Code",
|
||||
step_description="Permission request",
|
||||
agent_instance_id=None,
|
||||
)
|
||||
instance_id = response.agent_instance_id
|
||||
current_agent_instance_id = instance_id
|
||||
|
||||
# Ask the permission question
|
||||
answer_response = await client.ask_question(
|
||||
agent_instance_id=instance_id,
|
||||
question_text=question_text,
|
||||
timeout_minutes=1440,
|
||||
poll_interval=10.0,
|
||||
)
|
||||
|
||||
# Parse the answer to determine approval
|
||||
answer = answer_response.answer.lower().strip()
|
||||
if answer in ["yes", "y", "allow", "approve", "ok"]:
|
||||
return {
|
||||
"behavior": "allow",
|
||||
"updatedInput": input,
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"behavior": "deny",
|
||||
"message": f"Permission denied by user: {answer_response.answer}",
|
||||
}
|
||||
|
||||
except OmnaraTimeoutError:
|
||||
return {
|
||||
"behavior": "deny",
|
||||
"message": "Permission request timed out",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for the stdio server"""
|
||||
parser = argparse.ArgumentParser(description="Omnara MCP Server (Stdio)")
|
||||
@@ -125,6 +201,11 @@ def main():
|
||||
default="https://agent-dashboard-mcp.onrender.com",
|
||||
help="Base URL of the Omnara API server",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--claude-code-permission-tool",
|
||||
action="store_true",
|
||||
help="Enable Claude Code permission prompt tool for handling tool execution approvals",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -135,8 +216,16 @@ def main():
|
||||
base_url=args.base_url,
|
||||
)
|
||||
|
||||
# Enable/disable tools based on feature flags
|
||||
if args.claude_code_permission_tool:
|
||||
approve_tool.enable()
|
||||
logger.info("Claude Code permission tool enabled")
|
||||
|
||||
logger.info("Starting Omnara MCP server (stdio)")
|
||||
logger.info(f"Using API server: {args.base_url}")
|
||||
logger.info(
|
||||
f"Claude Code permission tool: {'enabled' if args.claude_code_permission_tool else 'disabled'}"
|
||||
)
|
||||
|
||||
try:
|
||||
# Run with stdio transport (default)
|
||||
|
||||
Reference in New Issue
Block a user