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:
ksarangmath
2025-07-14 20:42:47 -07:00
committed by GitHub
parent 97e0b12d99
commit f0d1f98000
5 changed files with 333 additions and 55 deletions

View File

@@ -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)