memra 0.2.0__tar.gz → 0.2.2__tar.gz

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.
Files changed (31) hide show
  1. memra-0.2.2/CHANGELOG.md +63 -0
  2. memra-0.2.2/MANIFEST.in +32 -0
  3. {memra-0.2.0/memra.egg-info → memra-0.2.2}/PKG-INFO +34 -47
  4. {memra-0.2.0 → memra-0.2.2}/README.md +28 -38
  5. memra-0.2.2/mcp_bridge_server.py +230 -0
  6. memra-0.2.2/memra/__init__.py +27 -0
  7. {memra-0.2.0/memra-sdk-package → memra-0.2.2}/memra/execution.py +29 -9
  8. {memra-0.2.0/memra-sdk-package → memra-0.2.2}/memra/models.py +1 -0
  9. memra-0.2.2/memra/tool_registry.py +189 -0
  10. memra-0.2.2/memra.egg-info/SOURCES.txt +14 -0
  11. {memra-0.2.0 → memra-0.2.2}/pyproject.toml +8 -5
  12. {memra-0.2.0 → memra-0.2.2}/setup.py +6 -4
  13. memra-0.2.0/PKG-INFO +0 -161
  14. memra-0.2.0/memra/__init__.py +0 -24
  15. memra-0.2.0/memra/discovery_client.py +0 -49
  16. memra-0.2.0/memra/execution.py +0 -434
  17. memra-0.2.0/memra/models.py +0 -98
  18. memra-0.2.0/memra/tool_registry.py +0 -190
  19. memra-0.2.0/memra-sdk-package/examples/accounts_payable_client.py +0 -207
  20. memra-0.2.0/memra-sdk-package/memra/__init__.py +0 -28
  21. memra-0.2.0/memra-sdk-package/memra/tool_registry_client.py +0 -105
  22. memra-0.2.0/memra.egg-info/SOURCES.txt +0 -23
  23. memra-0.2.0/memra.egg-info/dependency_links.txt +0 -1
  24. memra-0.2.0/memra.egg-info/entry_points.txt +0 -2
  25. memra-0.2.0/memra.egg-info/requires.txt +0 -12
  26. memra-0.2.0/memra.egg-info/top_level.txt +0 -2
  27. {memra-0.2.0 → memra-0.2.2}/LICENSE +0 -0
  28. {memra-0.2.0 → memra-0.2.2}/memra/discovery.py +0 -0
  29. {memra-0.2.0/memra-sdk-package → memra-0.2.2}/memra/discovery_client.py +0 -0
  30. {memra-0.2.0 → memra-0.2.2}/memra/tool_registry_client.py +0 -0
  31. {memra-0.2.0 → memra-0.2.2}/setup.cfg +0 -0
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to the Memra SDK will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.2] - 2025-05-28
9
+
10
+ ### Fixed
11
+ - **MCP Integration**: Fixed broken MCP tool execution after repository separation
12
+ - **Tool Registry**: Updated MCP tool routing to use correct endpoints
13
+ - **Bridge Server**: Added working MCP bridge server implementation
14
+ - **Real Work Detection**: Improved detection of real vs mock work for MCP tools
15
+
16
+ ### Added
17
+ - Complete MCP bridge server with DataValidator and PostgresInsert tools
18
+ - Health check endpoint for MCP bridge monitoring
19
+ - Better error handling and fallback for MCP tool execution
20
+
21
+ ### Changed
22
+ - MCP tools now perform real database operations instead of mock responses
23
+ - Improved logging and debugging for MCP tool execution flow
24
+
25
+ ## [0.2.1] - 2025-05-27
26
+
27
+ ## [0.2.0] - 2024-01-17
28
+
29
+ ### Added
30
+ - **MCP (Model Context Protocol) Integration**: Execute operations on local infrastructure while leveraging cloud AI processing
31
+ - New `mcp_bridge_server.py` for local resource bridging
32
+ - HMAC authentication for secure cloud-to-local communication
33
+ - Support for `hosted_by: "mcp"` in agent tool configurations
34
+ - PostgreSQL integration via MCP bridge
35
+ - Tool-level configuration support in execution engine
36
+ - New MCP tools: `PostgresInsert`, `DataValidator`
37
+
38
+ ### Enhanced
39
+ - **Execution Engine**: Updated to support tool-level configuration and MCP routing
40
+ - **Tool Registry Client**: Enhanced API client with better error handling and MCP support
41
+ - **Agent Configuration**: Added support for tool-specific configuration alongside agent-level config
42
+
43
+ ### Examples
44
+ - `examples/accounts_payable_mcp.py` - Complete invoice processing with MCP database integration
45
+ - `test_mcp_success.py` - Simple MCP integration test
46
+
47
+ ### Documentation
48
+ - `docs/mcp_integration.md` - Comprehensive MCP integration guide
49
+ - Updated README with MCP overview and quick start
50
+
51
+ ### Dependencies
52
+ - Added `aiohttp>=3.8.0` for MCP bridge server
53
+ - Added `aiohttp-cors>=0.7.0` for CORS support
54
+ - Added `psycopg2-binary>=2.9.0` for PostgreSQL integration
55
+
56
+ ## [0.1.0] - 2024-01-01
57
+
58
+ ### Added
59
+ - Initial release of Memra SDK
60
+ - Core agent and department framework
61
+ - API client for Memra cloud services
62
+ - Basic tool registry and execution engine
63
+ - Examples for accounts payable and propane delivery workflows
@@ -0,0 +1,32 @@
1
+ # Include only the client SDK files
2
+ include README.md
3
+ include LICENSE
4
+ include CHANGELOG.md
5
+ include requirements.txt
6
+ include examples/*.py
7
+ include docs/*.md
8
+ include docs/*.sql
9
+ include mcp_bridge_server.py
10
+ recursive-include memra *.py
11
+
12
+ # Explicitly exclude server-only files and directories
13
+ exclude app.py
14
+ exclude server_tool_registry.py
15
+ exclude config.py
16
+ exclude fly.toml
17
+ exclude Dockerfile
18
+ exclude Procfile
19
+ exclude docker-compose.yml
20
+ recursive-exclude logic *
21
+ recursive-exclude scripts *
22
+ recursive-exclude docs *
23
+ recursive-exclude examples *
24
+ recursive-exclude tests *
25
+ recursive-exclude temp_processing *
26
+ recursive-exclude invoices *
27
+ recursive-exclude local *
28
+ recursive-exclude dist *
29
+ recursive-exclude __pycache__ *
30
+ recursive-exclude *.egg-info *
31
+ exclude .DS_Store
32
+ exclude .dockerignore
@@ -1,10 +1,10 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: memra
3
- Version: 0.2.0
4
- Summary: Declarative framework for enterprise workflows with MCP integration
3
+ Version: 0.2.2
4
+ Summary: Declarative framework for enterprise workflows with MCP integration - Client SDK
5
5
  Home-page: https://github.com/memra/memra-sdk
6
6
  Author: Memra
7
- Author-email: Memra <info@memra.co>
7
+ Author-email: Memra <support@memra.com>
8
8
  License: MIT
9
9
  Project-URL: Homepage, https://memra.co
10
10
  Project-URL: Repository, https://github.com/memra-platform/memra-sdk
@@ -25,16 +25,13 @@ Requires-Dist: httpx>=0.24.0
25
25
  Requires-Dist: typing-extensions>=4.0.0
26
26
  Requires-Dist: aiohttp>=3.8.0
27
27
  Requires-Dist: aiohttp-cors>=0.7.0
28
- Requires-Dist: psycopg2-binary>=2.9.0
29
28
  Provides-Extra: dev
30
29
  Requires-Dist: pytest>=6.0; extra == "dev"
31
30
  Requires-Dist: pytest-asyncio; extra == "dev"
32
31
  Requires-Dist: black; extra == "dev"
33
32
  Requires-Dist: flake8; extra == "dev"
34
- Dynamic: author
35
- Dynamic: home-page
36
- Dynamic: license-file
37
- Dynamic: requires-python
33
+ Provides-Extra: mcp
34
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "mcp"
38
35
 
39
36
  # Memra SDK
40
37
 
@@ -102,6 +99,16 @@ echo 'export MEMRA_API_KEY="your-api-key-here"' >> ~/.zshrc
102
99
  python examples/accounts_payable_client.py
103
100
  ```
104
101
 
102
+ ## Architecture
103
+
104
+ The Memra platform consists of three main components:
105
+
106
+ - **Memra SDK** (this repository): Client library for building and executing workflows
107
+ - **Memra Server**: Hosted infrastructure for heavy AI processing tools
108
+ - **MCP Bridge**: Local execution environment for database operations
109
+
110
+ Tools are automatically routed between server and local execution based on their `hosted_by` configuration.
111
+
105
112
  ## Documentation
106
113
 
107
114
  Documentation is coming soon. For now, see the examples below and in the `examples/` directory.
@@ -118,44 +125,24 @@ We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) f
118
125
 
119
126
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
120
127
 
121
- ## Examples
128
+ ## Repository Structure
122
129
 
123
130
  ```
124
- ├── examples/
125
- │ ├── accounts_payable_client.py # API-based example
126
- │ ├── accounts_payable.py # Local example
127
- │ ├── invoice_processing.py # Simple workflow
128
- │ └── propane_delivery.py # Domain example
129
- ├── memra/ # Core SDK
130
- ├── logic/ # Tool implementations
131
- ├── local/dependencies/ # Database setup & schemas
132
- └── docker-compose.yml # Database setup
133
- ```
134
-
135
- ## New: MCP Integration
136
-
137
- Memra now supports **Model Context Protocol (MCP)** integration, allowing you to execute operations on your local infrastructure while leveraging Memra's cloud-based AI processing.
138
-
139
- **Key Benefits:**
140
- - 🔒 **Keep sensitive data local** - Your databases stay on your infrastructure
141
- - ⚡ **Hybrid processing** - AI processing in the cloud, data operations locally
142
- - 🔐 **Secure communication** - HMAC-authenticated requests between cloud and local
143
- - 🛠️ **Easy setup** - Simple bridge server connects your local resources
144
-
145
- **Quick Example:**
146
- ```python
147
- # Agent that uses local database via MCP
148
- agent = Agent(
149
- role="Data Writer",
150
- tools=[{
151
- "name": "PostgresInsert",
152
- "hosted_by": "mcp", # Routes to your local infrastructure
153
- "config": {
154
- "bridge_url": "http://localhost:8081",
155
- "bridge_secret": "your-secret"
156
- }
157
- }]
158
- )
131
+ ├── examples/ # Example workflows and use cases
132
+ │ ├── accounts_payable_client.py # API-based accounts payable workflow
133
+ │ ├── accounts_payable_mcp.py # MCP-enabled accounts payable workflow
134
+ │ ├── invoice_processing.py # Simple invoice processing example
135
+ │ └── propane_delivery.py # Propane delivery domain example
136
+ ├── memra/ # Core SDK package
137
+ ├── __init__.py # Package initialization
138
+ ├── tool_registry.py # Tool discovery and routing
139
+ └── sdk/ # SDK components
140
+ │ ├── __init__.py
141
+ │ ├── client.py # API client
142
+ │ ├── execution_engine.py # Workflow execution
143
+ │ └── models.py # Core data models
144
+ ├── docs/ # Documentation
145
+ ├── tests/ # Test suite
146
+ ├── local/dependencies/ # Local development setup
147
+ └── scripts/ # Utility scripts
159
148
  ```
160
-
161
- 📖 **[Complete MCP Integration Guide →](docs/mcp_integration.md)**
@@ -64,6 +64,16 @@ echo 'export MEMRA_API_KEY="your-api-key-here"' >> ~/.zshrc
64
64
  python examples/accounts_payable_client.py
65
65
  ```
66
66
 
67
+ ## Architecture
68
+
69
+ The Memra platform consists of three main components:
70
+
71
+ - **Memra SDK** (this repository): Client library for building and executing workflows
72
+ - **Memra Server**: Hosted infrastructure for heavy AI processing tools
73
+ - **MCP Bridge**: Local execution environment for database operations
74
+
75
+ Tools are automatically routed between server and local execution based on their `hosted_by` configuration.
76
+
67
77
  ## Documentation
68
78
 
69
79
  Documentation is coming soon. For now, see the examples below and in the `examples/` directory.
@@ -80,44 +90,24 @@ We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) f
80
90
 
81
91
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
82
92
 
83
- ## Examples
93
+ ## Repository Structure
84
94
 
85
95
  ```
86
- ├── examples/
87
- │ ├── accounts_payable_client.py # API-based example
88
- │ ├── accounts_payable.py # Local example
89
- │ ├── invoice_processing.py # Simple workflow
90
- │ └── propane_delivery.py # Domain example
91
- ├── memra/ # Core SDK
92
- ├── logic/ # Tool implementations
93
- ├── local/dependencies/ # Database setup & schemas
94
- └── docker-compose.yml # Database setup
95
- ```
96
-
97
- ## New: MCP Integration
98
-
99
- Memra now supports **Model Context Protocol (MCP)** integration, allowing you to execute operations on your local infrastructure while leveraging Memra's cloud-based AI processing.
100
-
101
- **Key Benefits:**
102
- - 🔒 **Keep sensitive data local** - Your databases stay on your infrastructure
103
- - ⚡ **Hybrid processing** - AI processing in the cloud, data operations locally
104
- - 🔐 **Secure communication** - HMAC-authenticated requests between cloud and local
105
- - 🛠️ **Easy setup** - Simple bridge server connects your local resources
106
-
107
- **Quick Example:**
108
- ```python
109
- # Agent that uses local database via MCP
110
- agent = Agent(
111
- role="Data Writer",
112
- tools=[{
113
- "name": "PostgresInsert",
114
- "hosted_by": "mcp", # Routes to your local infrastructure
115
- "config": {
116
- "bridge_url": "http://localhost:8081",
117
- "bridge_secret": "your-secret"
118
- }
119
- }]
120
- )
96
+ ├── examples/ # Example workflows and use cases
97
+ │ ├── accounts_payable_client.py # API-based accounts payable workflow
98
+ │ ├── accounts_payable_mcp.py # MCP-enabled accounts payable workflow
99
+ │ ├── invoice_processing.py # Simple invoice processing example
100
+ │ └── propane_delivery.py # Propane delivery domain example
101
+ ├── memra/ # Core SDK package
102
+ ├── __init__.py # Package initialization
103
+ ├── tool_registry.py # Tool discovery and routing
104
+ └── sdk/ # SDK components
105
+ │ ├── __init__.py
106
+ │ ├── client.py # API client
107
+ │ ├── execution_engine.py # Workflow execution
108
+ │ └── models.py # Core data models
109
+ ├── docs/ # Documentation
110
+ ├── tests/ # Test suite
111
+ ├── local/dependencies/ # Local development setup
112
+ └── scripts/ # Utility scripts
121
113
  ```
122
-
123
- 📖 **[Complete MCP Integration Guide →](docs/mcp_integration.md)**
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple MCP Bridge Server for local tool execution
4
+ """
5
+
6
+ import os
7
+ import json
8
+ import hmac
9
+ import hashlib
10
+ import logging
11
+ import asyncio
12
+ import psycopg2
13
+ from aiohttp import web, web_request
14
+ from typing import Dict, Any, Optional
15
+
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class MCPBridgeServer:
20
+ def __init__(self, postgres_url: str, bridge_secret: str):
21
+ self.postgres_url = postgres_url
22
+ self.bridge_secret = bridge_secret
23
+
24
+ def verify_signature(self, request_body: str, signature: str) -> bool:
25
+ """Verify HMAC signature"""
26
+ expected = hmac.new(
27
+ self.bridge_secret.encode(),
28
+ request_body.encode(),
29
+ hashlib.sha256
30
+ ).hexdigest()
31
+ return hmac.compare_digest(expected, signature)
32
+
33
+ async def execute_tool(self, request: web_request.Request) -> web.Response:
34
+ """Execute MCP tool endpoint"""
35
+ try:
36
+ # Get request body
37
+ body = await request.text()
38
+ data = json.loads(body)
39
+
40
+ # Verify signature
41
+ signature = request.headers.get('X-Bridge-Secret')
42
+ if not signature or signature != self.bridge_secret:
43
+ logger.warning("Invalid or missing bridge secret")
44
+ return web.json_response({
45
+ "success": False,
46
+ "error": "Invalid authentication"
47
+ }, status=401)
48
+
49
+ tool_name = data.get('tool_name')
50
+ input_data = data.get('input_data', {})
51
+
52
+ logger.info(f"Executing MCP tool: {tool_name}")
53
+
54
+ if tool_name == "DataValidator":
55
+ result = await self.data_validator(input_data)
56
+ elif tool_name == "PostgresInsert":
57
+ result = await self.postgres_insert(input_data)
58
+ else:
59
+ return web.json_response({
60
+ "success": False,
61
+ "error": f"Unknown tool: {tool_name}"
62
+ }, status=400)
63
+
64
+ return web.json_response(result)
65
+
66
+ except Exception as e:
67
+ logger.error(f"Tool execution failed: {str(e)}")
68
+ return web.json_response({
69
+ "success": False,
70
+ "error": str(e)
71
+ }, status=500)
72
+
73
+ async def data_validator(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
74
+ """Validate data against schema"""
75
+ try:
76
+ invoice_data = input_data.get('invoice_data', {})
77
+
78
+ # Perform basic validation
79
+ validation_errors = []
80
+
81
+ # Check required fields
82
+ required_fields = ['headerSection', 'billingDetails', 'chargesSummary']
83
+ for field in required_fields:
84
+ if field not in invoice_data:
85
+ validation_errors.append(f"Missing required field: {field}")
86
+
87
+ # Validate header section
88
+ if 'headerSection' in invoice_data:
89
+ header = invoice_data['headerSection']
90
+ if not header.get('vendorName'):
91
+ validation_errors.append("Missing vendor name in header")
92
+ if not header.get('subtotal'):
93
+ validation_errors.append("Missing subtotal in header")
94
+
95
+ # Validate billing details
96
+ if 'billingDetails' in invoice_data:
97
+ billing = invoice_data['billingDetails']
98
+ if not billing.get('invoiceNumber'):
99
+ validation_errors.append("Missing invoice number")
100
+ if not billing.get('invoiceDate'):
101
+ validation_errors.append("Missing invoice date")
102
+
103
+ is_valid = len(validation_errors) == 0
104
+
105
+ logger.info(f"Data validation completed: {'valid' if is_valid else 'invalid'}")
106
+
107
+ return {
108
+ "success": True,
109
+ "data": {
110
+ "is_valid": is_valid,
111
+ "validation_errors": validation_errors,
112
+ "validated_data": invoice_data
113
+ }
114
+ }
115
+
116
+ except Exception as e:
117
+ logger.error(f"Data validation failed: {str(e)}")
118
+ return {
119
+ "success": False,
120
+ "error": str(e)
121
+ }
122
+
123
+ async def postgres_insert(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
124
+ """Insert data into PostgreSQL"""
125
+ try:
126
+ invoice_data = input_data.get('invoice_data', {})
127
+ table_name = input_data.get('table_name', 'invoices')
128
+
129
+ # Extract key fields from invoice data
130
+ header = invoice_data.get('headerSection', {})
131
+ billing = invoice_data.get('billingDetails', {})
132
+ charges = invoice_data.get('chargesSummary', {})
133
+
134
+ # Prepare insert data
135
+ insert_data = {
136
+ 'invoice_number': billing.get('invoiceNumber', ''),
137
+ 'vendor_name': header.get('vendorName', ''),
138
+ 'invoice_date': billing.get('invoiceDate', ''),
139
+ 'total_amount': charges.get('document_total', 0),
140
+ 'tax_amount': charges.get('secondary_tax', 0),
141
+ 'line_items': json.dumps(charges.get('lineItemsBreakdown', [])),
142
+ 'status': 'processed'
143
+ }
144
+
145
+ # Connect to database and insert
146
+ conn = psycopg2.connect(self.postgres_url)
147
+ cursor = conn.cursor()
148
+
149
+ # Build insert query
150
+ columns = ', '.join(insert_data.keys())
151
+ placeholders = ', '.join(['%s'] * len(insert_data))
152
+ query = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders}) RETURNING id"
153
+
154
+ cursor.execute(query, list(insert_data.values()))
155
+ record_id = cursor.fetchone()[0]
156
+
157
+ conn.commit()
158
+ cursor.close()
159
+ conn.close()
160
+
161
+ logger.info(f"Successfully inserted record with ID: {record_id}")
162
+
163
+ return {
164
+ "success": True,
165
+ "data": {
166
+ "success": True,
167
+ "record_id": record_id,
168
+ "database_table": table_name,
169
+ "inserted_data": insert_data
170
+ }
171
+ }
172
+
173
+ except Exception as e:
174
+ logger.error(f"Database insert failed: {str(e)}")
175
+ return {
176
+ "success": False,
177
+ "error": str(e)
178
+ }
179
+
180
+ async def health_check(self, request: web_request.Request) -> web.Response:
181
+ """Health check endpoint"""
182
+ return web.json_response({"status": "healthy", "service": "mcp-bridge"})
183
+
184
+ def create_app(self) -> web.Application:
185
+ """Create aiohttp application"""
186
+ app = web.Application()
187
+
188
+ # Add routes
189
+ app.router.add_post('/execute_tool', self.execute_tool)
190
+ app.router.add_get('/health', self.health_check)
191
+
192
+ return app
193
+
194
+ async def start(self, port: int = 8081):
195
+ """Start the server"""
196
+ app = self.create_app()
197
+ runner = web.AppRunner(app)
198
+ await runner.setup()
199
+
200
+ site = web.TCPSite(runner, 'localhost', port)
201
+ await site.start()
202
+
203
+ logger.info(f"MCP Bridge Server started on http://localhost:{port}")
204
+ logger.info(f"Available endpoints:")
205
+ logger.info(f" POST /execute_tool - Execute MCP tools")
206
+ logger.info(f" GET /health - Health check")
207
+
208
+ # Keep running
209
+ try:
210
+ await asyncio.Future() # Run forever
211
+ except KeyboardInterrupt:
212
+ logger.info("Shutting down server...")
213
+ finally:
214
+ await runner.cleanup()
215
+
216
+ def main():
217
+ # Get configuration from environment
218
+ postgres_url = os.getenv('MCP_POSTGRES_URL', 'postgresql://tarpus@localhost:5432/memra_invoice_db')
219
+ bridge_secret = os.getenv('MCP_BRIDGE_SECRET', 'test-secret-for-development')
220
+
221
+ logger.info(f"Starting MCP Bridge Server...")
222
+ logger.info(f"PostgreSQL URL: {postgres_url}")
223
+ logger.info(f"Bridge Secret: {'*' * len(bridge_secret)}")
224
+
225
+ # Create and start server
226
+ server = MCPBridgeServer(postgres_url, bridge_secret)
227
+ asyncio.run(server.start())
228
+
229
+ if __name__ == '__main__':
230
+ main()
@@ -0,0 +1,27 @@
1
+ """
2
+ Memra SDK - Declarative AI Workflows
3
+
4
+ A framework for building AI-powered business workflows using a declarative approach.
5
+ Think of it as "Kubernetes for business logic" where agents are the pods and
6
+ departments are the deployments.
7
+ """
8
+
9
+ __version__ = "0.2.2"
10
+
11
+ # Core imports
12
+ from .models import Agent, Department, Tool
13
+ from .execution import ExecutionEngine
14
+
15
+ # Make key classes available at package level
16
+ __all__ = [
17
+ "Agent",
18
+ "Department",
19
+ "Tool",
20
+ "ExecutionEngine",
21
+ "__version__"
22
+ ]
23
+
24
+ # Optional: Add version check for compatibility
25
+ import sys
26
+ if sys.version_info < (3, 8):
27
+ raise RuntimeError("Memra requires Python 3.8 or higher")
@@ -2,6 +2,7 @@ import time
2
2
  import logging
3
3
  from typing import Dict, Any, List, Optional
4
4
  from .models import Department, Agent, DepartmentResult, ExecutionTrace, DepartmentAudit
5
+ from .tool_registry import ToolRegistry
5
6
  from .tool_registry_client import ToolRegistryClient
6
7
 
7
8
  logger = logging.getLogger(__name__)
@@ -10,7 +11,8 @@ class ExecutionEngine:
10
11
  """Engine that executes department workflows by coordinating agents and tools"""
11
12
 
12
13
  def __init__(self):
13
- self.tool_registry = ToolRegistryClient()
14
+ self.tool_registry = ToolRegistry()
15
+ self.api_client = ToolRegistryClient()
14
16
  self.last_execution_audit: Optional[DepartmentAudit] = None
15
17
 
16
18
  def execute_department(self, department: Department, input_data: Dict[str, Any]) -> DepartmentResult:
@@ -199,12 +201,28 @@ class ExecutionEngine:
199
201
  trace.tools_invoked.append(tool_name)
200
202
 
201
203
  # Get tool from registry and execute
202
- tool_result = self.tool_registry.execute_tool(
203
- tool_name,
204
- hosted_by,
205
- agent_input,
206
- agent.config
207
- )
204
+ print(f"🔍 {agent.role}: Tool {tool_name} is hosted by: {hosted_by}")
205
+ if hosted_by == "memra":
206
+ # Use API client for server-hosted tools
207
+ print(f"🌐 {agent.role}: Using API client for {tool_name}")
208
+ config_to_pass = tool_spec.get("config") if isinstance(tool_spec, dict) else tool_spec.config
209
+ tool_result = self.api_client.execute_tool(
210
+ tool_name,
211
+ hosted_by,
212
+ agent_input,
213
+ config_to_pass
214
+ )
215
+ else:
216
+ # Use local registry for MCP and other local tools
217
+ print(f"🏠 {agent.role}: Using local registry for {tool_name}")
218
+ config_to_pass = tool_spec.get("config") if isinstance(tool_spec, dict) else tool_spec.config
219
+ print(f"🔧 {agent.role}: Config for {tool_name}: {config_to_pass}")
220
+ tool_result = self.tool_registry.execute_tool(
221
+ tool_name,
222
+ hosted_by,
223
+ agent_input,
224
+ config_to_pass
225
+ )
208
226
 
209
227
  if not tool_result.get("success", False):
210
228
  print(f"😟 {agent.role}: Oh no! Tool {tool_name} failed: {tool_result.get('error', 'Unknown error')}")
@@ -292,7 +310,8 @@ class ExecutionEngine:
292
310
  isinstance(tool_data["validation_errors"], list) and
293
311
  "is_valid" in tool_data and
294
312
  # Check if it's validating real extracted data (not just mock data)
295
- len(str(tool_data)) > 100 # Real validation results are more substantial
313
+ len(str(tool_data)) > 100 and # Real validation results are more substantial
314
+ not tool_data.get("_mock", False) # Not mock data
296
315
  )
297
316
 
298
317
  elif tool_name == "PostgresInsert":
@@ -302,7 +321,8 @@ class ExecutionEngine:
302
321
  tool_data["success"] == True and
303
322
  "record_id" in tool_data and
304
323
  isinstance(tool_data["record_id"], int) and # Real DB returns integer IDs
305
- "database_table" in tool_data # Real implementation includes table name
324
+ "database_table" in tool_data and # Real implementation includes table name
325
+ not tool_data.get("_mock", False) # Not mock data
306
326
  )
307
327
 
308
328
  # Default to mock work
@@ -12,6 +12,7 @@ class Tool(BaseModel):
12
12
  hosted_by: str = "memra" # or "mcp" for customer's Model Context Protocol
13
13
  description: Optional[str] = None
14
14
  parameters: Optional[Dict[str, Any]] = None
15
+ config: Optional[Dict[str, Any]] = None
15
16
 
16
17
  class Agent(BaseModel):
17
18
  role: str