@macaw-ai/client 0.0.0-stage → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +339 -0
- package/bin/install.js +422 -0
- package/package.json +37 -4
- package/payload/README.md +56 -0
- package/payload/secureAI/LICENSE +190 -0
- package/payload/secureAI/NOTICE +5 -0
- package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
- package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
- package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
- package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
- package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
- package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
- package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
- package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
- package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
- package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
- package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
- package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
- package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
- package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
- package/payload/secureAI/examples/README.md +244 -0
- package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
- package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
- package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
- package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
- package/payload/secureAI/examples/attestations/README.md +128 -0
- package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
- package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
- package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
- package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
- package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
- package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
- package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
- package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
- package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
- package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
- package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
- package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
- package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
- package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
- package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
- package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
- package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
- package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
- package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
- package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
- package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
- package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
- package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
- package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
- package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
- package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
- package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
- package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
- package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
- package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
- package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
- package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
- package/payload/secureAI/macaw_adapters/__init__.py +55 -0
- package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
- package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
- package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
- package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
- package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
- package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
- package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
- package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
- package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
- package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
- package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
- package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
- package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
- package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
- package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
- package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
- package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
- package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
- package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
- package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
- package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
- package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
- package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
- package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
- package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
- package/payload/secureAI/test_harness_quick.py +296 -0
- package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
- package/README.md +0 -3
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SecureMCP Server: Integrated with MACAW Local Agent
|
|
3
|
+
|
|
4
|
+
This provides a developer-friendly MCP API while leveraging MACAW Local Agent's
|
|
5
|
+
security infrastructure. All security is delegated to MACAWClient.
|
|
6
|
+
|
|
7
|
+
Simplified Architecture:
|
|
8
|
+
- SecureMCP provides MCP-style decorators (@tool, @resource, @prompt)
|
|
9
|
+
- All security handled by MACAWClient -> LocalAgent -> ToolAgent
|
|
10
|
+
- No duplicate permission checks - trust the MACAW protocol
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import logging
|
|
15
|
+
import time
|
|
16
|
+
from typing import Optional, Dict, Any, Callable, List
|
|
17
|
+
from functools import wraps
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Server:
|
|
23
|
+
"""
|
|
24
|
+
SecureMCP Server with MACAW Local Agent integration.
|
|
25
|
+
|
|
26
|
+
- Requires MACAW Local Agent running
|
|
27
|
+
- All security via Unix socket to Local Agent
|
|
28
|
+
- No direct MACAW SDK imports
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
name: str,
|
|
34
|
+
version: str = "1.0.0",
|
|
35
|
+
port: int = 8080,
|
|
36
|
+
host: str = "localhost",
|
|
37
|
+
**kwargs
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Initialize SecureMCP server.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
name: Server name
|
|
44
|
+
version: Server version
|
|
45
|
+
port: Port to listen on
|
|
46
|
+
host: Host to bind to
|
|
47
|
+
**kwargs: Additional configuration
|
|
48
|
+
"""
|
|
49
|
+
self.name = name
|
|
50
|
+
self.version = version
|
|
51
|
+
self.port = port
|
|
52
|
+
self.host = host
|
|
53
|
+
self.config = kwargs
|
|
54
|
+
|
|
55
|
+
# Registries for MCP components
|
|
56
|
+
self.tools = {}
|
|
57
|
+
self.resources = {}
|
|
58
|
+
self.prompts = {}
|
|
59
|
+
|
|
60
|
+
# MACAW client (initialized in start() after tools are registered)
|
|
61
|
+
self.macaw_client = None
|
|
62
|
+
self.server_id = None
|
|
63
|
+
|
|
64
|
+
def _init_with_macaw(self):
|
|
65
|
+
"""Initialize with MACAW Local Agent using agent-per-app model."""
|
|
66
|
+
try:
|
|
67
|
+
from macaw_client import MACAWClient
|
|
68
|
+
|
|
69
|
+
# Create this SecureMCP server's own agent with unique identity
|
|
70
|
+
# Following the agent-per-app model - each service is an autonomous agent
|
|
71
|
+
server_app_name = f"securemcp-{self.name}"
|
|
72
|
+
|
|
73
|
+
# Collect unified tools config (handler + metadata together)
|
|
74
|
+
tools_config = {}
|
|
75
|
+
tool_names = []
|
|
76
|
+
|
|
77
|
+
for tool_name, tool_info in self.tools.items():
|
|
78
|
+
tool_names.append(tool_name) # Collect tool names for intent_policy
|
|
79
|
+
|
|
80
|
+
# Create handler wrapper for this tool
|
|
81
|
+
func = tool_info['function']
|
|
82
|
+
def create_handler(tool_func):
|
|
83
|
+
def handler(params):
|
|
84
|
+
try:
|
|
85
|
+
if asyncio.iscoroutinefunction(tool_func):
|
|
86
|
+
# Return coroutine for async functions - MACAWClient will await it
|
|
87
|
+
return tool_func(**params)
|
|
88
|
+
else:
|
|
89
|
+
return tool_func(**params)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"Tool execution error in {tool_func.__name__}: {e}")
|
|
92
|
+
raise
|
|
93
|
+
return handler
|
|
94
|
+
|
|
95
|
+
# Unified format: handler + metadata together
|
|
96
|
+
tools_config[tool_name] = {
|
|
97
|
+
'handler': create_handler(func),
|
|
98
|
+
'description': tool_info.get('description', ''),
|
|
99
|
+
'metadata': tool_info.get('metadata', {})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Define the intent policy with the actual tools this server provides
|
|
103
|
+
intent_policy = {
|
|
104
|
+
"provided_capabilities": [
|
|
105
|
+
{
|
|
106
|
+
"type": "tools",
|
|
107
|
+
"tools": tool_names, # Populated with actual tool names
|
|
108
|
+
"description": f"Tools provided by SecureMCP server {self.name}"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"required_resources": [],
|
|
112
|
+
"data_access": ["none"],
|
|
113
|
+
"description": f"SecureMCP server: {self.name}"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Create MACAWClient with unified tools config
|
|
117
|
+
self.macaw_client = MACAWClient(
|
|
118
|
+
app_name=server_app_name,
|
|
119
|
+
app_version=self.version,
|
|
120
|
+
intent_policy=intent_policy,
|
|
121
|
+
tools=tools_config, # Unified: {name: {handler, description, ...}}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Register this server's agent with the Local Agent
|
|
125
|
+
success = self.macaw_client.register()
|
|
126
|
+
|
|
127
|
+
if success:
|
|
128
|
+
self.server_id = self.macaw_client.agent_id
|
|
129
|
+
logger.info(f"✅ SecureMCP server registered as agent: {self.server_id}")
|
|
130
|
+
logger.info("All security handled by MACAWClient -> LocalAgent -> ToolAgent")
|
|
131
|
+
else:
|
|
132
|
+
raise RuntimeError("Failed to register SecureMCP server agent with MACAW Local Agent")
|
|
133
|
+
|
|
134
|
+
except ImportError as e:
|
|
135
|
+
logger.error(f"MACAW Local Agent client not available: {e}")
|
|
136
|
+
raise RuntimeError("MACAW Local Agent required. Ensure macaw_client is installed.")
|
|
137
|
+
except ConnectionError as e:
|
|
138
|
+
logger.error(f"Cannot connect to MACAW Local Agent: {e}")
|
|
139
|
+
raise RuntimeError("MACAW Local Agent service not running. Start with: sudo systemctl start macaw-local")
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.error(f"Error initializing with MACAW: {e}")
|
|
142
|
+
raise
|
|
143
|
+
|
|
144
|
+
# ============================================
|
|
145
|
+
# Tool Registration and Execution
|
|
146
|
+
# ============================================
|
|
147
|
+
|
|
148
|
+
def tool(self, name: str = None, description: str = "", **kwargs):
|
|
149
|
+
"""
|
|
150
|
+
Decorator to register a tool.
|
|
151
|
+
|
|
152
|
+
Security is handled by MACAWClient -> LocalAgent -> ToolAgent.
|
|
153
|
+
No duplicate permission checks here - trust the MACAW protocol.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
name: Tool name (uses function name if not provided)
|
|
157
|
+
description: Tool description
|
|
158
|
+
**kwargs: Additional metadata
|
|
159
|
+
"""
|
|
160
|
+
def decorator(func):
|
|
161
|
+
tool_name = name or func.__name__
|
|
162
|
+
|
|
163
|
+
# Store the tool - MACAWClient will handle security when invoked
|
|
164
|
+
self.tools[tool_name] = {
|
|
165
|
+
'function': func,
|
|
166
|
+
'description': description or func.__doc__ or "",
|
|
167
|
+
'metadata': kwargs
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Return original function - all security via MACAW protocol
|
|
171
|
+
# When invoked via LocalAgent, ToolAgent.verify_and_execute() handles:
|
|
172
|
+
# - Signature verification
|
|
173
|
+
# - Policy enforcement
|
|
174
|
+
# - Audit logging
|
|
175
|
+
return func
|
|
176
|
+
return decorator
|
|
177
|
+
|
|
178
|
+
# ============================================
|
|
179
|
+
# Resource Registration
|
|
180
|
+
# ============================================
|
|
181
|
+
|
|
182
|
+
def resource(self, pattern: str, description: str = "", **kwargs):
|
|
183
|
+
"""
|
|
184
|
+
Decorator to register a resource (read-only tool semantics).
|
|
185
|
+
|
|
186
|
+
Resources are treated as read-only tools. Security is handled by
|
|
187
|
+
MACAWClient -> LocalAgent -> ToolAgent with read-only policy.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
pattern: Resource URI pattern (e.g., "config/{name}")
|
|
191
|
+
description: Resource description
|
|
192
|
+
**kwargs: Additional metadata
|
|
193
|
+
"""
|
|
194
|
+
def decorator(cls):
|
|
195
|
+
# Store the resource class - treated as read-only tool
|
|
196
|
+
self.resources[pattern] = {
|
|
197
|
+
'class': cls,
|
|
198
|
+
'description': description or cls.__doc__ or "",
|
|
199
|
+
'metadata': kwargs
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# Return original class - all security via MACAW protocol
|
|
203
|
+
return cls
|
|
204
|
+
return decorator
|
|
205
|
+
|
|
206
|
+
# ============================================
|
|
207
|
+
# Prompt Registration
|
|
208
|
+
# ============================================
|
|
209
|
+
|
|
210
|
+
def prompt(self, name: str = None, description: str = "", **kwargs):
|
|
211
|
+
"""
|
|
212
|
+
Decorator to register a prompt template.
|
|
213
|
+
|
|
214
|
+
Prompts are starting points for AuthenticatedPrompt creation.
|
|
215
|
+
Use MACAWClient.create_authenticated_prompt() to create signed,
|
|
216
|
+
policy-bound prompts for LLM invocation.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
name: Prompt name (uses function name if not provided)
|
|
220
|
+
description: Prompt description
|
|
221
|
+
**kwargs: Additional metadata
|
|
222
|
+
"""
|
|
223
|
+
def decorator(func):
|
|
224
|
+
prompt_name = name or func.__name__
|
|
225
|
+
|
|
226
|
+
# Store the prompt template
|
|
227
|
+
self.prompts[prompt_name] = {
|
|
228
|
+
'function': func,
|
|
229
|
+
'description': description or func.__doc__ or "",
|
|
230
|
+
'metadata': kwargs
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
# Return original function - prompt templates are just starting points
|
|
234
|
+
# Security comes from AuthenticatedPrompt when actually used
|
|
235
|
+
return func
|
|
236
|
+
return decorator
|
|
237
|
+
|
|
238
|
+
# ============================================
|
|
239
|
+
# Server Lifecycle
|
|
240
|
+
# ============================================
|
|
241
|
+
|
|
242
|
+
async def start(self):
|
|
243
|
+
"""Start the SecureMCP server"""
|
|
244
|
+
logger.info(f"Starting SecureMCP server '{self.name}' on {self.host}:{self.port}")
|
|
245
|
+
|
|
246
|
+
# Initialize MACAW now that all tools are registered
|
|
247
|
+
# MACAWClient handles connection - raises clear error if LocalAgent unavailable
|
|
248
|
+
self._init_with_macaw()
|
|
249
|
+
|
|
250
|
+
logger.info(f"Server '{self.name}' is ready")
|
|
251
|
+
logger.info(f" Tools: {list(self.tools.keys())}")
|
|
252
|
+
logger.info(f" Resources: {list(self.resources.keys())}")
|
|
253
|
+
logger.info(f" Prompts: {list(self.prompts.keys())}")
|
|
254
|
+
|
|
255
|
+
# Keep running
|
|
256
|
+
try:
|
|
257
|
+
while True:
|
|
258
|
+
await asyncio.sleep(1)
|
|
259
|
+
except KeyboardInterrupt:
|
|
260
|
+
await self.stop()
|
|
261
|
+
|
|
262
|
+
async def stop(self):
|
|
263
|
+
"""Stop the SecureMCP server"""
|
|
264
|
+
logger.info(f"Stopping SecureMCP server '{self.name}'")
|
|
265
|
+
|
|
266
|
+
# Unregister from Local Agent
|
|
267
|
+
if self.macaw_client:
|
|
268
|
+
try:
|
|
269
|
+
self.macaw_client.unregister()
|
|
270
|
+
logger.info(f"✅ Unregistered agent {self.server_id} from Local Agent")
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logger.error(f"Error unregistering from Local Agent: {e}")
|
|
273
|
+
|
|
274
|
+
logger.info(f"Server '{self.name}' stopped")
|
|
275
|
+
|
|
276
|
+
def run(self):
|
|
277
|
+
"""Run the server (blocking)"""
|
|
278
|
+
asyncio.run(self.start())
|
|
279
|
+
|
|
280
|
+
# ============================================
|
|
281
|
+
# Helper Methods
|
|
282
|
+
# ============================================
|
|
283
|
+
|
|
284
|
+
def _extract_parameters(self, func: Callable) -> Dict[str, Any]:
|
|
285
|
+
"""Extract parameter information from function signature"""
|
|
286
|
+
import inspect
|
|
287
|
+
|
|
288
|
+
sig = inspect.signature(func)
|
|
289
|
+
params = {}
|
|
290
|
+
|
|
291
|
+
for param_name, param in sig.parameters.items():
|
|
292
|
+
if param_name == "self":
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
param_info = {
|
|
296
|
+
"type": "string", # Default type
|
|
297
|
+
"required": param.default == inspect.Parameter.empty
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# Try to extract type from annotation
|
|
301
|
+
if param.annotation != inspect.Parameter.empty:
|
|
302
|
+
if param.annotation == str:
|
|
303
|
+
param_info["type"] = "string"
|
|
304
|
+
elif param.annotation == int:
|
|
305
|
+
param_info["type"] = "integer"
|
|
306
|
+
elif param.annotation == float:
|
|
307
|
+
param_info["type"] = "number"
|
|
308
|
+
elif param.annotation == bool:
|
|
309
|
+
param_info["type"] = "boolean"
|
|
310
|
+
elif param.annotation == dict:
|
|
311
|
+
param_info["type"] = "object"
|
|
312
|
+
elif param.annotation == list:
|
|
313
|
+
param_info["type"] = "array"
|
|
314
|
+
|
|
315
|
+
params[param_name] = param_info
|
|
316
|
+
|
|
317
|
+
return params
|