Ai_Assistant/hakuro_MCP/manual_mcp_call.py
2026-06-10 21:40:08 +02:00

292 lines
9.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import json
from pathlib import Path
import yaml
# ─────────────────────────────
# 🗂️ Credential + Config Paths
# ─────────────────────────────
def load_mcp_config():
"""Load MCP configuration from file located in the same directory as this script."""
# Always use the current scripts directory
script_dir = Path(__file__).resolve().parent
mcp_config_path = script_dir / "mcp_config.json"
if not mcp_config_path.exists():
print(f"⚠️ MCP config not found at {mcp_config_path}")
return None
try:
with open(mcp_config_path, "r", encoding="utf-8") as f:
return json.load(f)
except (json.JSONDecodeError, OSError) as e:
print(f"⚠️ Failed to load MCP config: {e}")
return None
def get_mcp_tools_metadata():
"""
Get metadata for all available MCP tools.
Returns:
List of tool metadata dictionaries, or None if failed
"""
mcp_config = load_mcp_config()
if not mcp_config:
print("⚠️ MCP config unavailable")
return None
try:
server_url = mcp_config["url"].rstrip("/")
access_token = mcp_config["token"]
# MCP protocol endpoint
mcp_endpoint = f"{server_url}/mcp/"
# Construct MCP JSON-RPC call to list tools
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
# Make the request with SSE support
response = requests.post(
mcp_endpoint,
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream"
},
json=payload,
timeout=10,
stream=True
)
response.raise_for_status()
# Handle SSE stream response
if response.headers.get('content-type', '').startswith('text/event-stream'):
# Parse SSE stream
for line in response.iter_lines():
if line:
decoded = line.decode('utf-8')
if decoded.startswith('data: '):
data = decoded[6:] # Remove 'data: ' prefix
try:
event_data = json.loads(data)
# Check for JSON-RPC error
if "error" in event_data:
print(f"⚠️ MCP error: {event_data['error']}")
return None
# Return the tools list when found
if event_data.get("jsonrpc") == "2.0" and "result" in event_data:
result = event_data["result"]
# The result contains a "tools" array
if "tools" in result:
return result["tools"]
return result
except json.JSONDecodeError:
continue
return None
else:
# Handle regular JSON response (fallback)
result = response.json()
# Check for JSON-RPC error
if "error" in result:
print(f"⚠️ MCP error: {result['error']}")
return None
# Return the tools list
if "result" in result:
if "tools" in result["result"]:
return result["result"]["tools"]
return result["result"]
return result
except requests.exceptions.RequestException as e:
print(f"⚠️ MCP call failed: {e}")
return None
except Exception as e:
print(f"⚠️ Unexpected error: {e}")
return None
def print_tools_metadata(tools):
"""Pretty print tools metadata."""
if not tools:
print("No tools found")
return
print(f"\n📋 Found {len(tools)} tool(s):\n")
for i, tool in enumerate(tools, 1):
print(f"{'='*60}")
print(f"Tool #{i}: {tool.get('name', 'Unknown')}")
print(f"{'='*60}")
print(f"Description: {tool.get('description', 'No description')}")
# Print input schema
if 'inputSchema' in tool or 'input_schema' in tool:
schema = tool.get('inputSchema') or tool.get('input_schema')
print(f"\n📝 Input Schema:")
print(f" Type: {schema.get('type', 'N/A')}")
if 'properties' in schema:
print(f" Properties:")
for prop_name, prop_info in schema['properties'].items():
prop_type = prop_info.get('type', 'unknown')
prop_title = prop_info.get('title', prop_name)
print(f" - {prop_name} ({prop_type}): {prop_title}")
if 'required' in schema:
print(f" Required: {', '.join(schema['required'])}")
# Print metadata (your custom meta field)
if 'meta' in tool:
print(f"\n🏷️ Metadata:")
for key, value in tool['meta'].items():
print(f" {key}: {value}")
# Print annotations
if 'annotations' in tool:
print(f"\n🔖 Annotations:")
for key, value in tool['annotations'].items():
print(f" {key}: {value}")
print()
def get_manual_mcp_response(tool_name: str, arguments: dict):
"""
Manually call an MCP tool using the MCP protocol.
Args:
tool_name: Name of the tool to call (e.g., "play_sound_effect")
arguments: Dictionary of arguments for the tool (e.g., {"sound_type": "bong"})
Returns:
The tool result or None if failed
"""
mcp_config = load_mcp_config()
if not mcp_config:
print("⚠️ MCP config unavailable")
return None
try:
server_url = mcp_config["url"].rstrip("/")
access_token = mcp_config["token"]
# MCP protocol endpoint
mcp_endpoint = f"{server_url}/mcp/"
# Construct MCP JSON-RPC call
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": arguments
}
}
# Make the request with SSE support
response = requests.post(
mcp_endpoint,
headers={
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream"
},
json=payload,
timeout=10,
stream=True # Important for SSE
)
response.raise_for_status()
# Handle SSE stream response
if response.headers.get('content-type', '').startswith('text/event-stream'):
# Parse SSE stream
for line in response.iter_lines():
if line:
decoded = line.decode('utf-8')
if decoded.startswith('data: '):
data = decoded[6:] # Remove 'data: ' prefix
try:
event_data = json.loads(data)
# Check for JSON-RPC error
if "error" in event_data:
print(f"⚠️ MCP error: {event_data['error']}")
return None
# Return the result when found
if event_data.get("jsonrpc") == "2.0" and "result" in event_data:
return event_data["result"]
except json.JSONDecodeError:
continue
return None
else:
# Handle regular JSON response (fallback)
result = response.json()
# Check for JSON-RPC error
if "error" in result:
print(f"⚠️ MCP error: {result['error']}")
return None
# Return the actual result
if "result" in result:
return result["result"]
return result
except requests.exceptions.RequestException as e:
print(f"⚠️ MCP call failed: {e}")
return None
except Exception as e:
print(f"⚠️ Unexpected error: {e}")
return None
# Example usage
if __name__ == "__main__":
print("🔍 Fetching MCP tools metadata...")
tools = get_mcp_tools_metadata()
if tools:
print_tools_metadata(tools)
# Also print raw JSON for inspection
print("\n" + "="*60)
print("📦 Raw JSON Response:")
print("="*60)
print(json.dumps(tools, indent=2))
else:
print("❌ Failed to fetch tools metadata")
print("Starte Google Kalender Test...")
result = get_manual_mcp_response(
tool_name="get_calendar_events",
arguments={"max_results": 5},
)
print("Kalender Resultat:", result)
# manual call argument
result = get_manual_mcp_response(
tool_name="roll_dice",
arguments={"n_dice": 3, "manual_call": True},
)
print("Manually called Result:", result)