mcp-proxy-adapter 6.1.1__py3-none-any.whl โ†’ 6.2.1__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 (146) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +18 -7
  3. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  4. mcp_proxy_adapter/core/app_factory.py +87 -3
  5. mcp_proxy_adapter/core/app_runner.py +272 -0
  6. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  7. mcp_proxy_adapter/core/client.py +574 -0
  8. mcp_proxy_adapter/core/client_manager.py +284 -0
  9. mcp_proxy_adapter/core/server_adapter.py +17 -80
  10. mcp_proxy_adapter/core/server_engine.py +5 -99
  11. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  12. mcp_proxy_adapter/core/transport_manager.py +5 -5
  13. mcp_proxy_adapter/examples/__init__.py +16 -0
  14. mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
  15. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  16. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  17. mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
  18. mcp_proxy_adapter/examples/commands/__init__.py +5 -1
  19. mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
  20. mcp_proxy_adapter/examples/debug_request_state.py +4 -36
  21. mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
  22. mcp_proxy_adapter/examples/demo_client.py +0 -66
  23. mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
  24. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  25. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
  26. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
  27. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  28. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
  29. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
  30. mcp_proxy_adapter/examples/full_application/main.py +65 -44
  31. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  32. mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
  33. mcp_proxy_adapter/examples/generate_certificates.py +0 -15
  34. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  35. mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
  36. mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
  37. mcp_proxy_adapter/examples/run_example.py +1 -23
  38. mcp_proxy_adapter/examples/run_security_tests.py +2 -60
  39. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
  40. mcp_proxy_adapter/examples/security_test_client.py +18 -123
  41. mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
  42. mcp_proxy_adapter/examples/test_config.py +148 -0
  43. mcp_proxy_adapter/examples/test_config_generator.py +1 -25
  44. mcp_proxy_adapter/examples/test_examples.py +4 -67
  45. mcp_proxy_adapter/examples/universal_client.py +154 -162
  46. mcp_proxy_adapter/main.py +51 -161
  47. mcp_proxy_adapter/version.py +1 -1
  48. mcp_proxy_adapter-6.2.1.dist-info/METADATA +676 -0
  49. mcp_proxy_adapter-6.2.1.dist-info/RECORD +119 -0
  50. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +0 -285
  51. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +0 -285
  52. mcp_proxy_adapter/examples/README.md +0 -257
  53. mcp_proxy_adapter/examples/README_EN.md +0 -258
  54. mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
  55. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
  56. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
  57. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -43
  58. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +0 -36
  59. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -29
  60. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +0 -34
  61. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
  62. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +0 -35
  63. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
  64. mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
  65. mcp_proxy_adapter/examples/cert_config.json +0 -9
  66. mcp_proxy_adapter/examples/certs/admin.crt +0 -32
  67. mcp_proxy_adapter/examples/certs/admin.key +0 -52
  68. mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
  69. mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
  70. mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
  71. mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
  72. mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
  73. mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
  74. mcp_proxy_adapter/examples/certs/client.crt +0 -32
  75. mcp_proxy_adapter/examples/certs/client.key +0 -52
  76. mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
  77. mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
  78. mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
  79. mcp_proxy_adapter/examples/certs/client_user.key +0 -52
  80. mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
  81. mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
  82. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
  83. mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
  84. mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
  85. mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
  86. mcp_proxy_adapter/examples/certs/readonly.key +0 -52
  87. mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
  88. mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
  89. mcp_proxy_adapter/examples/certs/server.crt +0 -32
  90. mcp_proxy_adapter/examples/certs/server.key +0 -52
  91. mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
  92. mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
  93. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
  94. mcp_proxy_adapter/examples/certs/user.crt +0 -32
  95. mcp_proxy_adapter/examples/certs/user.key +0 -52
  96. mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
  97. mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
  98. mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
  99. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
  100. mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
  101. mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
  102. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
  103. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
  104. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
  105. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
  106. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
  107. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
  108. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
  109. mcp_proxy_adapter/examples/full_application/roles.json +0 -21
  110. mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
  111. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
  112. mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
  128. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
  129. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
  130. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
  131. mcp_proxy_adapter/examples/roles.json +0 -38
  132. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
  133. mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
  134. mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
  135. mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
  136. mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
  137. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
  138. mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
  139. mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
  140. mcp_proxy_adapter/utils/config_generator.py +0 -727
  141. mcp_proxy_adapter-6.1.1.dist-info/METADATA +0 -205
  142. mcp_proxy_adapter-6.1.1.dist-info/RECORD +0 -197
  143. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +0 -2
  144. mcp_proxy_adapter-6.1.1.dist-info/licenses/LICENSE +0 -21
  145. {mcp_proxy_adapter-6.1.1.dist-info โ†’ mcp_proxy_adapter-6.2.1.dist-info}/WHEEL +0 -0
  146. {mcp_proxy_adapter-6.1.1.dist-info โ†’ mcp_proxy_adapter-6.2.1.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Test Examples Script
4
-
5
4
  This script tests all examples with different configurations.
6
-
7
5
  Author: Vasiliy Zdanovskiy
8
6
  email: vasilyvz@gmail.com
9
7
  """
10
-
11
8
  import json
12
9
  import os
13
10
  import subprocess
@@ -15,7 +12,6 @@ import time
15
12
  import requests
16
13
  from pathlib import Path
17
14
  from typing import Dict, Any, List
18
-
19
15
  # Configuration for testing
20
16
  CONFIGS = {
21
17
  "basic_framework": {
@@ -35,28 +31,22 @@ CONFIGS = {
35
31
  "mtls_with_roles": {"port": 9448, "ssl": True, "auth": True, "mtls": True}
36
32
  }
37
33
  }
38
-
39
34
  API_KEYS = {
40
35
  "admin": "admin-secret-key-123",
41
36
  "user": "user-secret-key-456"
42
37
  }
43
-
44
-
45
38
  class ExampleTester:
46
39
  """Test examples with different configurations."""
47
-
48
40
  def __init__(self):
49
41
  self.examples_dir = Path(__file__).parent
50
42
  self.results = {}
51
43
  self.processes = []
52
-
53
44
  def generate_certificates(self):
54
45
  """Generate certificates for testing."""
55
46
  print("๐Ÿ” Generating certificates...")
56
-
57
47
  cert_script = self.examples_dir.parent / "generate_certificates.py"
58
48
  if cert_script.exists():
59
- result = subprocess.run([sys.executable, str(cert_script)],
49
+ result = subprocess.run([sys.executable, str(cert_script)],
60
50
  capture_output=True, text=True)
61
51
  if result.returncode == 0:
62
52
  print("โœ… Certificates generated successfully")
@@ -67,41 +57,31 @@ class ExampleTester:
67
57
  else:
68
58
  print("โš ๏ธ Certificate generation script not found, using existing certificates")
69
59
  return True
70
-
71
60
  def start_server(self, example_type: str, config_name: str) -> subprocess.Popen:
72
61
  """Start a server with specific configuration."""
73
62
  config_path = self.examples_dir / example_type / "configs" / f"{config_name}.json"
74
63
  main_script = self.examples_dir / example_type / "main.py"
75
-
76
64
  if not config_path.exists():
77
65
  raise FileNotFoundError(f"Configuration file not found: {config_path}")
78
-
79
66
  if not main_script.exists():
80
67
  raise FileNotFoundError(f"Main script not found: {main_script}")
81
-
82
68
  cmd = [
83
69
  sys.executable, str(main_script),
84
70
  "--config", str(config_path)
85
71
  ]
86
-
87
72
  print(f"๐Ÿš€ Starting {example_type} server with {config_name} config...")
88
73
  process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
89
-
90
74
  # Wait for server to start
91
75
  time.sleep(5)
92
-
93
76
  return process
94
-
95
- def test_health_endpoint(self, port: int, ssl: bool = False, auth: bool = False,
77
+ def test_health_endpoint(self, port: int, ssl: bool = False, auth: bool = False,
96
78
  api_key: str = None) -> Dict[str, Any]:
97
79
  """Test health endpoint."""
98
80
  protocol = "https" if ssl else "http"
99
81
  url = f"{protocol}://localhost:{port}/health"
100
-
101
82
  headers = {}
102
83
  if auth and api_key:
103
84
  headers["X-API-Key"] = api_key
104
-
105
85
  try:
106
86
  response = requests.get(url, headers=headers, verify=False, timeout=10)
107
87
  return {
@@ -114,24 +94,20 @@ class ExampleTester:
114
94
  "success": False,
115
95
  "error": str(e)
116
96
  }
117
-
118
97
  def test_echo_command(self, port: int, ssl: bool = False, auth: bool = False,
119
98
  api_key: str = None) -> Dict[str, Any]:
120
99
  """Test echo command."""
121
100
  protocol = "https" if ssl else "http"
122
101
  url = f"{protocol}://localhost:{port}/cmd"
123
-
124
102
  headers = {"Content-Type": "application/json"}
125
103
  if auth and api_key:
126
104
  headers["X-API-Key"] = api_key
127
-
128
105
  data = {
129
106
  "jsonrpc": "2.0",
130
107
  "method": "echo",
131
108
  "params": {"message": "Hello from test!"},
132
109
  "id": 1
133
110
  }
134
-
135
111
  try:
136
112
  response = requests.post(url, json=data, headers=headers, verify=False, timeout=10)
137
113
  return {
@@ -144,19 +120,15 @@ class ExampleTester:
144
120
  "success": False,
145
121
  "error": str(e)
146
122
  }
147
-
148
123
  def test_full_application_commands(self, port: int, ssl: bool = False, auth: bool = False,
149
124
  api_key: str = None) -> Dict[str, Any]:
150
125
  """Test full application specific commands."""
151
126
  protocol = "https" if ssl else "http"
152
127
  url = f"{protocol}://localhost:{port}/cmd"
153
-
154
128
  headers = {"Content-Type": "application/json"}
155
129
  if auth and api_key:
156
130
  headers["X-API-Key"] = api_key
157
-
158
131
  results = {}
159
-
160
132
  # Test custom echo command
161
133
  data = {
162
134
  "jsonrpc": "2.0",
@@ -164,7 +136,6 @@ class ExampleTester:
164
136
  "params": {"message": "Custom echo test", "repeat": 3},
165
137
  "id": 1
166
138
  }
167
-
168
139
  try:
169
140
  response = requests.post(url, json=data, headers=headers, verify=False, timeout=10)
170
141
  results["custom_echo"] = {
@@ -177,7 +148,6 @@ class ExampleTester:
177
148
  "success": False,
178
149
  "error": str(e)
179
150
  }
180
-
181
151
  # Test dynamic calculator command
182
152
  data = {
183
153
  "jsonrpc": "2.0",
@@ -185,7 +155,6 @@ class ExampleTester:
185
155
  "params": {"operation": "add", "a": 10, "b": 5},
186
156
  "id": 2
187
157
  }
188
-
189
158
  try:
190
159
  response = requests.post(url, json=data, headers=headers, verify=False, timeout=10)
191
160
  results["dynamic_calculator"] = {
@@ -198,40 +167,31 @@ class ExampleTester:
198
167
  "success": False,
199
168
  "error": str(e)
200
169
  }
201
-
202
170
  return results
203
-
204
171
  def run_tests(self):
205
172
  """Run all tests."""
206
173
  print("๐Ÿงช Starting Example Tests")
207
174
  print("=" * 60)
208
-
209
175
  # Generate certificates first
210
176
  if not self.generate_certificates():
211
177
  print("โŒ Certificate generation failed, skipping tests")
212
178
  return
213
-
214
179
  for example_type, configs in CONFIGS.items():
215
180
  print(f"\n๐Ÿ“ Testing {example_type.upper()}")
216
181
  print("-" * 40)
217
-
218
182
  for config_name, config_info in configs.items():
219
183
  print(f"\n๐Ÿ”ง Testing {config_name} configuration...")
220
-
221
184
  try:
222
185
  # Start server
223
186
  process = self.start_server(example_type, config_name)
224
187
  self.processes.append(process)
225
-
226
188
  port = config_info["port"]
227
189
  ssl = config_info.get("ssl", False)
228
190
  auth = config_info.get("auth", False)
229
-
230
191
  # Test health endpoint
231
192
  print(f" ๐Ÿ“Š Testing health endpoint...")
232
193
  health_result = self.test_health_endpoint(port, ssl, auth)
233
194
  print(f" Health: {'โœ…' if health_result['success'] else 'โŒ'}")
234
-
235
195
  # Test echo command
236
196
  print(f" ๐Ÿ“ Testing echo command...")
237
197
  if auth:
@@ -240,96 +200,75 @@ class ExampleTester:
240
200
  else:
241
201
  echo_result = self.test_echo_command(port, ssl, auth)
242
202
  print(f" Echo: {'โœ…' if echo_result['success'] else 'โŒ'}")
243
-
244
203
  # Test full application specific commands
245
204
  if example_type == "full_application":
246
205
  print(f" ๐Ÿ”ง Testing full application commands...")
247
- app_results = self.test_full_application_commands(port, ssl, auth,
206
+ app_results = self.test_full_application_commands(port, ssl, auth,
248
207
  API_KEYS["admin"] if auth else None)
249
208
  for cmd_name, result in app_results.items():
250
209
  print(f" {cmd_name}: {'โœ…' if result['success'] else 'โŒ'}")
251
-
252
210
  # Store results
253
211
  self.results[f"{example_type}_{config_name}"] = {
254
212
  "health": health_result,
255
213
  "echo": echo_result,
256
214
  "config_info": config_info
257
215
  }
258
-
259
216
  if example_type == "full_application":
260
217
  self.results[f"{example_type}_{config_name}"]["app_commands"] = app_results
261
-
262
218
  except Exception as e:
263
219
  print(f" โŒ Error testing {config_name}: {e}")
264
220
  self.results[f"{example_type}_{config_name}"] = {
265
221
  "error": str(e),
266
222
  "config_info": config_info
267
223
  }
268
-
269
224
  finally:
270
225
  # Stop server
271
226
  if process:
272
227
  process.terminate()
273
228
  process.wait()
274
229
  time.sleep(2)
275
-
276
230
  self.print_results()
277
-
278
231
  def print_results(self):
279
232
  """Print test results."""
280
233
  print("\n๐Ÿ“Š Test Results Summary")
281
234
  print("=" * 60)
282
-
283
235
  total_tests = len(self.results)
284
236
  successful_tests = 0
285
-
286
237
  for test_name, result in self.results.items():
287
238
  print(f"\n๐Ÿ” {test_name}")
288
-
289
239
  if "error" in result:
290
240
  print(f" โŒ Error: {result['error']}")
291
241
  continue
292
-
293
242
  # Check health test
294
243
  health_success = result.get("health", {}).get("success", False)
295
244
  print(f" Health: {'โœ…' if health_success else 'โŒ'}")
296
-
297
245
  # Check echo test
298
246
  echo_success = result.get("echo", {}).get("success", False)
299
247
  print(f" Echo: {'โœ…' if echo_success else 'โŒ'}")
300
-
301
248
  # Check app commands for full application
302
249
  if "app_commands" in result:
303
- app_success = all(cmd_result.get("success", False)
250
+ app_success = all(cmd_result.get("success", False)
304
251
  for cmd_result in result["app_commands"].values())
305
252
  print(f" App Commands: {'โœ…' if app_success else 'โŒ'}")
306
-
307
253
  # Overall test success
308
254
  test_success = health_success and echo_success
309
255
  if test_success:
310
256
  successful_tests += 1
311
-
312
257
  print(f"\n๐ŸŽฏ Overall Results: {successful_tests}/{total_tests} tests passed")
313
-
314
258
  if successful_tests == total_tests:
315
259
  print("๐ŸŽ‰ All tests passed!")
316
260
  else:
317
261
  print("โš ๏ธ Some tests failed. Check the details above.")
318
-
319
262
  def cleanup(self):
320
263
  """Cleanup processes."""
321
264
  for process in self.processes:
322
265
  if process.poll() is None:
323
266
  process.terminate()
324
267
  process.wait()
325
-
326
-
327
268
  def main():
328
269
  """Main function."""
329
270
  import sys
330
-
331
271
  tester = ExampleTester()
332
-
333
272
  try:
334
273
  tester.run_tests()
335
274
  except KeyboardInterrupt:
@@ -338,7 +277,5 @@ def main():
338
277
  print(f"\nโŒ Test execution failed: {e}")
339
278
  finally:
340
279
  tester.cleanup()
341
-
342
-
343
280
  if __name__ == "__main__":
344
281
  main()