mcp-proxy-adapter 3.1.5__py3-none-any.whl → 4.0.0__py3-none-any.whl

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 (122) hide show
  1. mcp_proxy_adapter/api/app.py +86 -27
  2. mcp_proxy_adapter/api/handlers.py +1 -1
  3. mcp_proxy_adapter/api/middleware/error_handling.py +11 -10
  4. mcp_proxy_adapter/api/tool_integration.py +5 -2
  5. mcp_proxy_adapter/api/tools.py +3 -3
  6. mcp_proxy_adapter/commands/base.py +19 -1
  7. mcp_proxy_adapter/commands/command_registry.py +258 -6
  8. mcp_proxy_adapter/commands/help_command.py +54 -65
  9. mcp_proxy_adapter/commands/hooks.py +260 -0
  10. mcp_proxy_adapter/commands/reload_command.py +211 -0
  11. mcp_proxy_adapter/commands/reload_settings_command.py +125 -0
  12. mcp_proxy_adapter/commands/settings_command.py +189 -0
  13. mcp_proxy_adapter/config.py +16 -1
  14. mcp_proxy_adapter/core/__init__.py +44 -0
  15. mcp_proxy_adapter/core/logging.py +87 -34
  16. mcp_proxy_adapter/core/settings.py +376 -0
  17. mcp_proxy_adapter/core/utils.py +2 -2
  18. mcp_proxy_adapter/custom_openapi.py +81 -2
  19. mcp_proxy_adapter/examples/README.md +124 -0
  20. mcp_proxy_adapter/examples/__init__.py +7 -0
  21. mcp_proxy_adapter/examples/basic_server/README.md +60 -0
  22. mcp_proxy_adapter/examples/basic_server/__init__.py +7 -0
  23. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +39 -0
  24. mcp_proxy_adapter/examples/basic_server/config.json +35 -0
  25. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +238 -0
  26. mcp_proxy_adapter/examples/basic_server/server.py +98 -0
  27. mcp_proxy_adapter/examples/custom_commands/README.md +127 -0
  28. mcp_proxy_adapter/examples/custom_commands/__init__.py +27 -0
  29. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +250 -0
  30. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +6 -0
  31. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +103 -0
  32. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +111 -0
  33. mcp_proxy_adapter/examples/custom_commands/config.json +62 -0
  34. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +169 -0
  35. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +215 -0
  36. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +76 -0
  37. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +96 -0
  38. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +241 -0
  39. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +135 -0
  40. mcp_proxy_adapter/examples/custom_commands/echo_command.py +122 -0
  41. mcp_proxy_adapter/examples/custom_commands/hooks.py +230 -0
  42. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +123 -0
  43. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +103 -0
  44. mcp_proxy_adapter/examples/custom_commands/server.py +223 -0
  45. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +176 -0
  46. mcp_proxy_adapter/examples/deployment/README.md +49 -0
  47. mcp_proxy_adapter/examples/deployment/__init__.py +7 -0
  48. mcp_proxy_adapter/examples/deployment/config.development.json +8 -0
  49. {examples/basic_example → mcp_proxy_adapter/examples/deployment}/config.json +11 -7
  50. mcp_proxy_adapter/examples/deployment/config.production.json +12 -0
  51. mcp_proxy_adapter/examples/deployment/config.staging.json +11 -0
  52. mcp_proxy_adapter/examples/deployment/docker-compose.yml +31 -0
  53. mcp_proxy_adapter/examples/deployment/run.sh +43 -0
  54. mcp_proxy_adapter/examples/deployment/run_docker.sh +84 -0
  55. mcp_proxy_adapter/openapi.py +3 -2
  56. mcp_proxy_adapter/tests/api/test_custom_openapi.py +617 -0
  57. mcp_proxy_adapter/tests/api/test_handlers.py +522 -0
  58. mcp_proxy_adapter/tests/api/test_schemas.py +546 -0
  59. mcp_proxy_adapter/tests/api/test_tool_integration.py +531 -0
  60. mcp_proxy_adapter/tests/commands/test_help_command.py +8 -5
  61. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +4 -5
  62. mcp_proxy_adapter/tests/test_command_registry.py +37 -1
  63. mcp_proxy_adapter/tests/unit/test_base_command.py +391 -85
  64. mcp_proxy_adapter/version.py +1 -1
  65. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/METADATA +1 -1
  66. mcp_proxy_adapter-4.0.0.dist-info/RECORD +110 -0
  67. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/WHEEL +1 -1
  68. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/top_level.txt +0 -1
  69. examples/__init__.py +0 -19
  70. examples/anti_patterns/README.md +0 -51
  71. examples/anti_patterns/__init__.py +0 -9
  72. examples/anti_patterns/bad_design/README.md +0 -72
  73. examples/anti_patterns/bad_design/global_state.py +0 -170
  74. examples/anti_patterns/bad_design/monolithic_command.py +0 -272
  75. examples/basic_example/README.md +0 -245
  76. examples/basic_example/__init__.py +0 -8
  77. examples/basic_example/commands/__init__.py +0 -5
  78. examples/basic_example/commands/echo_command.py +0 -95
  79. examples/basic_example/commands/math_command.py +0 -151
  80. examples/basic_example/commands/time_command.py +0 -152
  81. examples/basic_example/docs/EN/README.md +0 -177
  82. examples/basic_example/docs/RU/README.md +0 -177
  83. examples/basic_example/server.py +0 -151
  84. examples/basic_example/tests/conftest.py +0 -243
  85. examples/check_vstl_schema.py +0 -106
  86. examples/commands/echo_command.py +0 -52
  87. examples/commands/echo_command_di.py +0 -152
  88. examples/commands/echo_result.py +0 -65
  89. examples/commands/get_date_command.py +0 -98
  90. examples/commands/new_uuid4_command.py +0 -91
  91. examples/complete_example/Dockerfile +0 -24
  92. examples/complete_example/README.md +0 -92
  93. examples/complete_example/__init__.py +0 -8
  94. examples/complete_example/commands/__init__.py +0 -5
  95. examples/complete_example/commands/system_command.py +0 -328
  96. examples/complete_example/config.json +0 -41
  97. examples/complete_example/configs/config.dev.yaml +0 -40
  98. examples/complete_example/configs/config.docker.yaml +0 -40
  99. examples/complete_example/docker-compose.yml +0 -35
  100. examples/complete_example/requirements.txt +0 -20
  101. examples/complete_example/server.py +0 -113
  102. examples/di_example/.pytest_cache/README.md +0 -8
  103. examples/di_example/server.py +0 -249
  104. examples/fix_vstl_help.py +0 -123
  105. examples/minimal_example/README.md +0 -65
  106. examples/minimal_example/__init__.py +0 -8
  107. examples/minimal_example/config.json +0 -14
  108. examples/minimal_example/main.py +0 -136
  109. examples/minimal_example/simple_server.py +0 -163
  110. examples/minimal_example/tests/conftest.py +0 -171
  111. examples/minimal_example/tests/test_hello_command.py +0 -111
  112. examples/minimal_example/tests/test_integration.py +0 -181
  113. examples/patch_vstl_service.py +0 -105
  114. examples/patch_vstl_service_mcp.py +0 -108
  115. examples/server.py +0 -69
  116. examples/simple_server.py +0 -128
  117. examples/test_package_3.1.4.py +0 -177
  118. examples/test_server.py +0 -134
  119. examples/tool_description_example.py +0 -82
  120. mcp_proxy_adapter/py.typed +0 -0
  121. mcp_proxy_adapter-3.1.5.dist-info/RECORD +0 -118
  122. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/licenses/LICENSE +0 -0
examples/__init__.py DELETED
@@ -1,19 +0,0 @@
1
- """
2
- Examples package for MCP Microservice.
3
-
4
- This package contains examples of using the MCP Microservice framework
5
- for building microservices with different levels of complexity.
6
-
7
- Examples:
8
- - minimal_example: Minimal working example with a single command
9
- - basic_example: Basic example with multiple commands and tests
10
- - complete_example: Complete example with Docker support and advanced features
11
- - anti_patterns: Examples of anti-patterns and bad practices (for educational purposes)
12
- """
13
-
14
- __all__ = [
15
- "minimal_example",
16
- "basic_example",
17
- "complete_example",
18
- "anti_patterns"
19
- ]
@@ -1,51 +0,0 @@
1
- # Anti-Patterns for MCP Microservice
2
-
3
- This directory contains examples of anti-patterns and bad practices when using MCP Microservice.
4
- These examples demonstrate what **NOT** to do and common mistakes to avoid.
5
-
6
- ## Categories
7
-
8
- 1. **Bad Design Patterns** - Examples of poor architectural and design choices
9
- 2. **Performance Issues** - Examples that can cause performance problems
10
- 3. **Security Problems** - Examples with potential security vulnerabilities
11
-
12
- ## Purpose
13
-
14
- These examples serve as educational resources to help you:
15
-
16
- 1. Recognize common mistakes
17
- 2. Understand why they are problematic
18
- 3. Learn better alternatives
19
-
20
- **WARNING:** These examples contain intentional errors, bugs, and security vulnerabilities.
21
- Do not use them in production environments or as a basis for your own code.
22
-
23
- ## Structure
24
-
25
- ```
26
- anti_patterns/
27
- ├── bad_design/ # Poor architectural and design choices
28
- │ ├── monolithic_command.py # Example of a command that does too much
29
- │ ├── global_state.py # Example of using global state
30
- │ └── README.md # Explanation of bad design patterns
31
- ├── performance_issues/ # Performance problems
32
- │ ├── blocking_operations.py # Example of blocking the event loop
33
- │ ├── memory_leaks.py # Example of potential memory leaks
34
- │ └── README.md # Explanation of performance issues
35
- ├── security_problems/ # Security vulnerabilities
36
- │ ├── no_validation.py # Example of missing input validation
37
- │ ├── command_injection.py # Example of command injection vulnerability
38
- │ └── README.md # Explanation of security problems
39
- └── README.md # This file
40
- ```
41
-
42
- ## How to Use
43
-
44
- Each example directory contains a README.md file explaining:
45
-
46
- 1. What the anti-pattern is
47
- 2. Why it's problematic
48
- 3. Better alternatives
49
- 4. How to fix the issues
50
-
51
- Use these examples as a reference for what to avoid, not as templates for your code.
@@ -1,9 +0,0 @@
1
- """
2
- Anti-patterns examples for MCP Microservice.
3
-
4
- This package contains examples of anti-patterns and bad practices
5
- when using MCP Microservice framework. These examples are for educational
6
- purposes only and should NOT be used in production.
7
- """
8
-
9
- __all__ = []
@@ -1,72 +0,0 @@
1
- # Bad Design Patterns
2
-
3
- This directory contains examples of poor architectural and design choices when using MCP Microservice.
4
-
5
- ## Included Examples
6
-
7
- 1. **[monolithic_command.py](./monolithic_command.py)** - A command that tries to do too much
8
- 2. **[global_state.py](./global_state.py)** - Using global state for command data
9
-
10
- ## Monolithic Command Anti-Pattern
11
-
12
- ### Problem
13
-
14
- The `MonolithicCommand` tries to handle multiple responsibilities:
15
- - Data processing
16
- - Validation
17
- - Database operations
18
- - File operations
19
- - Notification
20
-
21
- ### Why It's Bad
22
-
23
- 1. **Violates Single Responsibility Principle**: Each command should do one thing well
24
- 2. **Difficult to Test**: Large commands with many responsibilities are hard to test
25
- 3. **Hard to Maintain**: Changes to one feature may affect others
26
- 4. **Poor Reusability**: Cannot reuse parts of the functionality
27
-
28
- ### Better Alternative
29
-
30
- Split the monolithic command into multiple smaller commands:
31
- - `ProcessDataCommand` - Handles data processing
32
- - `SaveDataCommand` - Handles database operations
33
- - `GenerateReportCommand` - Handles file operations
34
- - `SendNotificationCommand` - Handles notifications
35
-
36
- ## Global State Anti-Pattern
37
-
38
- ### Problem
39
-
40
- The `GlobalStateCommand` uses global variables to store and share data between command executions.
41
-
42
- ### Why It's Bad
43
-
44
- 1. **Thread Safety**: Global state is not thread-safe in asynchronous environments
45
- 2. **Testing Difficulty**: Commands with global state are hard to test in isolation
46
- 3. **Hidden Dependencies**: Dependencies are not explicit
47
- 4. **State Management**: Difficult to track and manage state changes
48
-
49
- ### Better Alternative
50
-
51
- Use proper dependency injection and maintain state within the command instance:
52
-
53
- ```python
54
- class ProperStateCommand(Command):
55
- name = "proper_state"
56
- result_class = StateResult
57
-
58
- def __init__(self):
59
- self._state = {} # State is maintained per instance
60
-
61
- async def execute(self, key: str, value: Any = None) -> StateResult:
62
- if value is not None:
63
- self._state[key] = value
64
- return StateResult(key, self._state.get(key))
65
- ```
66
-
67
- ## How to Fix
68
-
69
- 1. **Identify Single Responsibilities**: Break down commands by responsibility
70
- 2. **Explicit Dependencies**: Use constructor parameters or method arguments
71
- 3. **Avoid Global State**: Maintain state within instances or external services
72
- 4. **Use Dependency Injection**: Inject dependencies through constructors
@@ -1,170 +0,0 @@
1
- """
2
- ANTI-PATTERN EXAMPLE: Global State
3
-
4
- This module demonstrates an anti-pattern where a command uses global state
5
- to store and share data between executions.
6
-
7
- WARNING: This is a BAD EXAMPLE! Do not use this approach in production.
8
- """
9
-
10
- from typing import Dict, Any, Optional
11
-
12
- from mcp_proxy_adapter import Command, SuccessResult
13
-
14
-
15
- # Global state - an anti-pattern that creates hidden dependencies
16
- # and makes the code hard to test and maintain
17
- GLOBAL_STATE: Dict[str, Any] = {}
18
-
19
-
20
- class StateResult(SuccessResult):
21
- """Result of state command."""
22
-
23
- def __init__(self, key: str, value: Any):
24
- """
25
- Initialize result.
26
-
27
- Args:
28
- key: The key accessed
29
- value: The value retrieved
30
- """
31
- self.key = key
32
- self.value = value
33
-
34
- def to_dict(self) -> Dict[str, Any]:
35
- """Convert result to dictionary."""
36
- return {
37
- "key": self.key,
38
- "value": self.value
39
- }
40
-
41
- @classmethod
42
- def get_schema(cls) -> Dict[str, Any]:
43
- """Get JSON schema for result."""
44
- return {
45
- "type": "object",
46
- "properties": {
47
- "key": {"type": "string"},
48
- "value": {"type": "string"}
49
- },
50
- "required": ["key"]
51
- }
52
-
53
-
54
- class GlobalStateCommand(Command):
55
- """
56
- ANTI-PATTERN: This command uses global state.
57
-
58
- It stores and retrieves values from a global dictionary,
59
- creating hidden dependencies and making the code:
60
- - Not thread-safe
61
- - Hard to test
62
- - Prone to unexpected behavior
63
- - Difficult to reason about
64
- """
65
-
66
- name = "global_state"
67
- result_class = StateResult
68
-
69
- async def execute(self, key: str, value: Any = None) -> StateResult:
70
- """
71
- Execute global state command.
72
-
73
- Args:
74
- key: Key to get or set
75
- value: Value to set (if not None)
76
-
77
- Returns:
78
- Result with key and current value
79
- """
80
- # Get or set value in global state
81
- if value is not None:
82
- # Set the value in the global state
83
- GLOBAL_STATE[key] = value
84
-
85
- # Return the current value (which might be None if the key doesn't exist)
86
- current_value = GLOBAL_STATE.get(key)
87
- return StateResult(key, current_value)
88
-
89
- @classmethod
90
- def get_schema(cls) -> Dict[str, Any]:
91
- """Get JSON schema for command parameters."""
92
- return {
93
- "type": "object",
94
- "properties": {
95
- "key": {
96
- "type": "string",
97
- "description": "Key to get or set"
98
- },
99
- "value": {
100
- "type": ["string", "number", "boolean", "null", "array", "object"],
101
- "description": "Value to set (optional)"
102
- }
103
- },
104
- "required": ["key"]
105
- }
106
-
107
-
108
- # Even worse: Command with mixed global and instance state
109
- class MixedStateCommand(Command):
110
- """
111
- ANTI-PATTERN: This command mixes global and instance state.
112
-
113
- It's even worse than just global state because it creates confusion
114
- about where state is stored and how it's accessed.
115
- """
116
-
117
- name = "mixed_state"
118
- result_class = StateResult
119
-
120
- def __init__(self):
121
- """Initialize with instance-specific counter."""
122
- self._counter = 0 # Instance state
123
-
124
- async def execute(self, key: str, value: Any = None, increment: bool = False) -> StateResult:
125
- """
126
- Execute mixed state command.
127
-
128
- Args:
129
- key: Key to get or set
130
- value: Value to set (if not None)
131
- increment: Whether to increment the instance counter
132
-
133
- Returns:
134
- Result with key and current value
135
- """
136
- # Instance state modification
137
- if increment:
138
- self._counter += 1
139
-
140
- # Global state modification - mixing different state types!
141
- if value is not None:
142
- # Set in global state
143
- GLOBAL_STATE[key] = value
144
-
145
- # Mix instance state with the value
146
- if self._counter > 0:
147
- # Modify value based on instance state
148
- if isinstance(GLOBAL_STATE.get(key), (int, float)):
149
- GLOBAL_STATE[key] = GLOBAL_STATE.get(key, 0) + self._counter
150
-
151
- # Return the current value (which might be None if the key doesn't exist)
152
- current_value = GLOBAL_STATE.get(key)
153
- return StateResult(key, current_value)
154
-
155
-
156
- # Example of how this causes problems:
157
- # Both commands modify the same global state, which creates hidden dependencies
158
- # and makes behavior unpredictable, especially with concurrent requests.
159
-
160
- # Usage example (don't do this!):
161
- #
162
- # # Command 1 sets a value
163
- # await GlobalStateCommand().execute("user_count", 10)
164
- #
165
- # # Command 2 also modifies the same value
166
- # await MixedStateCommand().execute("user_count", 5)
167
- #
168
- # # Command 1 reads the value but gets a result it didn't expect
169
- # result = await GlobalStateCommand().execute("user_count")
170
- # # result.value might be 5 instead of 10!
@@ -1,272 +0,0 @@
1
- """
2
- ANTI-PATTERN EXAMPLE: Monolithic Command
3
-
4
- This module demonstrates an anti-pattern where a command tries to do too much,
5
- violating the Single Responsibility Principle.
6
-
7
- WARNING: This is a BAD EXAMPLE! Do not use this approach in production.
8
- """
9
-
10
- import os
11
- import json
12
- import sqlite3
13
- import smtplib
14
- import time
15
- from email.message import EmailMessage
16
- from typing import Dict, Any, List, Optional
17
-
18
- from mcp_proxy_adapter import Command, SuccessResult
19
-
20
-
21
- # Global connection pool - another anti-pattern
22
- DB_CONNECTIONS = {}
23
-
24
-
25
- class MonolithicResult(SuccessResult):
26
- """Result of monolithic command."""
27
-
28
- def __init__(self, status: str, data: Dict[str, Any]):
29
- """
30
- Initialize result.
31
-
32
- Args:
33
- status: Operation status
34
- data: Result data
35
- """
36
- self.status = status
37
- self.data = data
38
-
39
- def to_dict(self) -> Dict[str, Any]:
40
- """Convert result to dictionary."""
41
- return {
42
- "status": self.status,
43
- "data": self.data
44
- }
45
-
46
- @classmethod
47
- def get_schema(cls) -> Dict[str, Any]:
48
- """Get JSON schema for result."""
49
- return {
50
- "type": "object",
51
- "properties": {
52
- "status": {"type": "string"},
53
- "data": {"type": "object"}
54
- },
55
- "required": ["status", "data"]
56
- }
57
-
58
-
59
- class MonolithicCommand(Command):
60
- """
61
- ANTI-PATTERN: This command tries to do too much.
62
-
63
- It handles:
64
- 1. Data processing
65
- 2. Validation
66
- 3. Database operations
67
- 4. File operations
68
- 5. Notifications
69
-
70
- This violates the Single Responsibility Principle and makes the code:
71
- - Hard to test
72
- - Hard to maintain
73
- - Difficult to reuse
74
- """
75
-
76
- name = "do_everything"
77
- result_class = MonolithicResult
78
-
79
- async def execute(
80
- self,
81
- action: str,
82
- data: Dict[str, Any],
83
- save_to_db: bool = True,
84
- generate_report: bool = True,
85
- notify_users: List[str] = None
86
- ) -> MonolithicResult:
87
- """
88
- Execute monolithic command.
89
-
90
- Args:
91
- action: Action to perform (process, update, delete)
92
- data: Input data to process
93
- save_to_db: Whether to save data to database
94
- generate_report: Whether to generate a report
95
- notify_users: List of users to notify
96
-
97
- Returns:
98
- Result of operations
99
- """
100
- result_data = {}
101
-
102
- # Step 1: Process data - this should be a separate command
103
- processed_data = self._process_data(action, data)
104
- result_data["processed"] = processed_data
105
-
106
- # Step 2: Save to database - this should be a separate command
107
- if save_to_db:
108
- db_result = self._save_to_database(processed_data)
109
- result_data["database"] = db_result
110
-
111
- # Step 3: Generate report - this should be a separate command
112
- if generate_report:
113
- report_path = self._generate_report(processed_data)
114
- result_data["report"] = report_path
115
-
116
- # Step 4: Notify users - this should be a separate command
117
- if notify_users:
118
- notification_result = self._send_notifications(
119
- notify_users, processed_data, report_path if generate_report else None
120
- )
121
- result_data["notifications"] = notification_result
122
-
123
- return MonolithicResult("success", result_data)
124
-
125
- def _process_data(self, action: str, data: Dict[str, Any]) -> Dict[str, Any]:
126
- """Process input data based on action."""
127
- # Simulate data processing
128
- processed = dict(data)
129
-
130
- if action == "process":
131
- # Complex data processing logic
132
- processed["status"] = "processed"
133
- processed["timestamp"] = time.time()
134
-
135
- # Calculate some values
136
- if "values" in processed and isinstance(processed["values"], list):
137
- processed["sum"] = sum(processed["values"])
138
- processed["average"] = sum(processed["values"]) / len(processed["values"])
139
-
140
- elif action == "update":
141
- # Update logic
142
- processed["status"] = "updated"
143
- processed["updated_at"] = time.time()
144
-
145
- elif action == "delete":
146
- # Delete logic
147
- processed["status"] = "deleted"
148
- processed["deleted_at"] = time.time()
149
-
150
- return processed
151
-
152
- def _save_to_database(self, data: Dict[str, Any]) -> Dict[str, Any]:
153
- """Save data to database - should be a separate command."""
154
- # Get or create database connection
155
- conn = self._get_db_connection()
156
- cursor = conn.cursor()
157
-
158
- # Create table if not exists
159
- cursor.execute("""
160
- CREATE TABLE IF NOT EXISTS items (
161
- id INTEGER PRIMARY KEY,
162
- data TEXT,
163
- status TEXT,
164
- timestamp REAL
165
- )
166
- """)
167
-
168
- # Insert data
169
- data_json = json.dumps(data)
170
- cursor.execute(
171
- "INSERT INTO items (data, status, timestamp) VALUES (?, ?, ?)",
172
- (data_json, data.get("status", "unknown"), time.time())
173
- )
174
-
175
- # Get ID of inserted row
176
- row_id = cursor.lastrowid
177
- conn.commit()
178
-
179
- return {"id": row_id, "status": "saved"}
180
-
181
- def _generate_report(self, data: Dict[str, Any]) -> str:
182
- """Generate report file - should be a separate command."""
183
- # Create reports directory if not exists
184
- os.makedirs("reports", exist_ok=True)
185
-
186
- # Generate report filename
187
- filename = f"reports/report_{int(time.time())}.json"
188
-
189
- # Write report to file
190
- with open(filename, "w") as f:
191
- json.dump(data, f, indent=2)
192
-
193
- return filename
194
-
195
- def _send_notifications(
196
- self,
197
- recipients: List[str],
198
- data: Dict[str, Any],
199
- report_path: Optional[str] = None
200
- ) -> Dict[str, Any]:
201
- """Send notifications to users - should be a separate command."""
202
- # Configure email server (hardcoded values - another anti-pattern)
203
- smtp_server = "smtp.example.com"
204
- smtp_port = 587
205
- smtp_user = "service@example.com"
206
- smtp_password = "password123" # Hardcoding passwords is a security issue!
207
-
208
- # Create message
209
- msg = EmailMessage()
210
- msg["Subject"] = f"Data {data.get('status', 'processed')}"
211
- msg["From"] = "service@example.com"
212
- msg["To"] = ", ".join(recipients)
213
-
214
- # Set content
215
- content = f"Data has been {data.get('status', 'processed')}.\n\n"
216
- if report_path:
217
- content += f"Report is available at: {report_path}\n"
218
- msg.set_content(content)
219
-
220
- # Send email (commented out to prevent actual sending)
221
- # with smtplib.SMTP(smtp_server, smtp_port) as server:
222
- # server.login(smtp_user, smtp_password)
223
- # server.send_message(msg)
224
-
225
- # Simulate successful sending
226
- return {
227
- "sent_to": recipients,
228
- "status": "sent"
229
- }
230
-
231
- def _get_db_connection(self) -> sqlite3.Connection:
232
- """Get database connection from pool."""
233
- if "default" not in DB_CONNECTIONS:
234
- # Create new connection
235
- DB_CONNECTIONS["default"] = sqlite3.connect(":memory:")
236
-
237
- return DB_CONNECTIONS["default"]
238
-
239
- @classmethod
240
- def get_schema(cls) -> Dict[str, Any]:
241
- """Get JSON schema for command parameters."""
242
- return {
243
- "type": "object",
244
- "properties": {
245
- "action": {
246
- "type": "string",
247
- "enum": ["process", "update", "delete"],
248
- "description": "Action to perform"
249
- },
250
- "data": {
251
- "type": "object",
252
- "description": "Input data to process"
253
- },
254
- "save_to_db": {
255
- "type": "boolean",
256
- "description": "Whether to save data to database",
257
- "default": True
258
- },
259
- "generate_report": {
260
- "type": "boolean",
261
- "description": "Whether to generate a report",
262
- "default": True
263
- },
264
- "notify_users": {
265
- "type": "array",
266
- "items": {"type": "string"},
267
- "description": "List of users to notify",
268
- "default": []
269
- }
270
- },
271
- "required": ["action", "data"]
272
- }