mcp-proxy-adapter 2.1.16__py3-none-any.whl → 3.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.
- examples/__init__.py +19 -0
- examples/anti_patterns/README.md +51 -0
- examples/anti_patterns/__init__.py +9 -0
- examples/anti_patterns/bad_design/README.md +72 -0
- examples/anti_patterns/bad_design/global_state.py +170 -0
- examples/anti_patterns/bad_design/monolithic_command.py +272 -0
- examples/basic_example/README.md +131 -0
- examples/basic_example/__init__.py +8 -0
- examples/basic_example/commands/__init__.py +5 -0
- examples/basic_example/commands/echo_command.py +95 -0
- examples/basic_example/commands/math_command.py +151 -0
- examples/basic_example/commands/time_command.py +152 -0
- examples/basic_example/config.json +21 -0
- examples/basic_example/config.yaml +20 -0
- examples/basic_example/docs/EN/README.md +136 -0
- examples/basic_example/docs/RU/README.md +136 -0
- examples/basic_example/main.py +50 -0
- examples/basic_example/server.py +45 -0
- examples/basic_example/tests/conftest.py +243 -0
- examples/commands/echo_command.py +52 -0
- examples/commands/echo_result.py +65 -0
- examples/commands/get_date_command.py +98 -0
- examples/commands/new_uuid4_command.py +91 -0
- examples/complete_example/Dockerfile +24 -0
- examples/complete_example/README.md +92 -0
- examples/complete_example/__init__.py +8 -0
- examples/complete_example/commands/__init__.py +5 -0
- examples/complete_example/commands/system_command.py +327 -0
- examples/complete_example/config.json +41 -0
- examples/complete_example/configs/config.dev.yaml +40 -0
- examples/complete_example/configs/config.docker.yaml +40 -0
- examples/complete_example/docker-compose.yml +35 -0
- examples/complete_example/main.py +67 -0
- examples/complete_example/requirements.txt +20 -0
- examples/complete_example/server.py +85 -0
- examples/minimal_example/README.md +51 -0
- examples/minimal_example/__init__.py +8 -0
- examples/minimal_example/config.json +21 -0
- examples/minimal_example/config.yaml +26 -0
- examples/minimal_example/main.py +67 -0
- examples/minimal_example/simple_server.py +124 -0
- examples/minimal_example/tests/conftest.py +171 -0
- examples/minimal_example/tests/test_hello_command.py +111 -0
- examples/minimal_example/tests/test_integration.py +183 -0
- examples/server.py +69 -0
- examples/simple_server.py +137 -0
- examples/test_server.py +126 -0
- mcp_proxy_adapter/__init__.py +33 -1
- mcp_proxy_adapter/config.py +186 -0
- mcp_proxy_adapter/custom_openapi.py +125 -0
- mcp_proxy_adapter/framework.py +109 -0
- mcp_proxy_adapter/openapi.py +403 -0
- mcp_proxy_adapter/version.py +3 -0
- mcp_proxy_adapter-3.0.0.dist-info/METADATA +200 -0
- mcp_proxy_adapter-3.0.0.dist-info/RECORD +58 -0
- {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/top_level.txt +1 -0
- mcp_proxy_adapter/adapter.py +0 -697
- mcp_proxy_adapter/analyzers/__init__.py +0 -1
- mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
- mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
- mcp_proxy_adapter/dispatchers/__init__.py +0 -1
- mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
- mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -235
- mcp_proxy_adapter/examples/analyze_config.py +0 -141
- mcp_proxy_adapter/examples/basic_integration.py +0 -155
- mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
- mcp_proxy_adapter/examples/extension_example.py +0 -72
- mcp_proxy_adapter/examples/help_best_practices.py +0 -67
- mcp_proxy_adapter/examples/help_usage.py +0 -64
- mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
- mcp_proxy_adapter/examples/openapi_server.py +0 -383
- mcp_proxy_adapter/examples/project_structure_example.py +0 -47
- mcp_proxy_adapter/examples/testing_example.py +0 -64
- mcp_proxy_adapter/models.py +0 -47
- mcp_proxy_adapter/registry.py +0 -439
- mcp_proxy_adapter/schema.py +0 -257
- mcp_proxy_adapter/testing_utils.py +0 -112
- mcp_proxy_adapter/validators/__init__.py +0 -1
- mcp_proxy_adapter/validators/docstring_validator.py +0 -75
- mcp_proxy_adapter/validators/metadata_validator.py +0 -76
- mcp_proxy_adapter-2.1.16.dist-info/METADATA +0 -341
- mcp_proxy_adapter-2.1.16.dist-info/RECORD +0 -30
- {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-2.1.16.dist-info → mcp_proxy_adapter-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
# Basic MCP Microservice Example
|
2
|
+
|
3
|
+
This example demonstrates the basic functionality of MCP Microservice with multiple commands
|
4
|
+
organized in separate files.
|
5
|
+
|
6
|
+
## Structure
|
7
|
+
|
8
|
+
```
|
9
|
+
basic_example/
|
10
|
+
├── commands/ # Commands directory
|
11
|
+
│ ├── __init__.py # Package initialization
|
12
|
+
│ ├── echo_command.py # Echo command
|
13
|
+
│ ├── math_command.py # Math command
|
14
|
+
│ └── time_command.py # Time command
|
15
|
+
├── config.yaml # Configuration file
|
16
|
+
├── docs/ # Documentation
|
17
|
+
│ ├── EN/ # English documentation
|
18
|
+
│ │ └── README.md # This file
|
19
|
+
│ └── RU/ # Russian documentation
|
20
|
+
│ └── README.md # Russian version of this file
|
21
|
+
├── logs/ # Logs directory
|
22
|
+
└── server.py # Server startup file
|
23
|
+
```
|
24
|
+
|
25
|
+
## Running the Example
|
26
|
+
|
27
|
+
```bash
|
28
|
+
# Navigate to the project directory
|
29
|
+
cd examples/basic_example
|
30
|
+
|
31
|
+
# Create logs directory if it doesn't exist
|
32
|
+
mkdir -p logs
|
33
|
+
|
34
|
+
# Run the server
|
35
|
+
python server.py
|
36
|
+
```
|
37
|
+
|
38
|
+
After starting, the server will be available at [http://localhost:8000](http://localhost:8000).
|
39
|
+
|
40
|
+
## Available Commands
|
41
|
+
|
42
|
+
### 1. `echo` - Echo Command
|
43
|
+
|
44
|
+
Returns the provided message.
|
45
|
+
|
46
|
+
**Parameters:**
|
47
|
+
- `message` (string) - Message to echo back
|
48
|
+
|
49
|
+
**Example request:**
|
50
|
+
```json
|
51
|
+
{
|
52
|
+
"jsonrpc": "2.0",
|
53
|
+
"method": "echo",
|
54
|
+
"params": {
|
55
|
+
"message": "Hello, World!"
|
56
|
+
},
|
57
|
+
"id": 1
|
58
|
+
}
|
59
|
+
```
|
60
|
+
|
61
|
+
### 2. `math` - Math Command
|
62
|
+
|
63
|
+
Performs a math operation on two numbers.
|
64
|
+
|
65
|
+
**Parameters:**
|
66
|
+
- `a` (number) - First number
|
67
|
+
- `b` (number) - Second number
|
68
|
+
- `operation` (string) - Operation (add, subtract, multiply, divide)
|
69
|
+
|
70
|
+
**Example request:**
|
71
|
+
```json
|
72
|
+
{
|
73
|
+
"jsonrpc": "2.0",
|
74
|
+
"method": "math",
|
75
|
+
"params": {
|
76
|
+
"a": 10,
|
77
|
+
"b": 5,
|
78
|
+
"operation": "add"
|
79
|
+
},
|
80
|
+
"id": 1
|
81
|
+
}
|
82
|
+
```
|
83
|
+
|
84
|
+
### 3. `time` - Time Command
|
85
|
+
|
86
|
+
Returns the current time and date.
|
87
|
+
|
88
|
+
**Parameters:**
|
89
|
+
- `format` (string, optional) - Time format (default: "%Y-%m-%d %H:%M:%S")
|
90
|
+
- `timezone` (string, optional) - Timezone (default: "UTC")
|
91
|
+
|
92
|
+
**Example request:**
|
93
|
+
```json
|
94
|
+
{
|
95
|
+
"jsonrpc": "2.0",
|
96
|
+
"method": "time",
|
97
|
+
"params": {
|
98
|
+
"format": "%d.%m.%Y %H:%M:%S",
|
99
|
+
"timezone": "Europe/London"
|
100
|
+
},
|
101
|
+
"id": 1
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
105
|
+
## Testing the API
|
106
|
+
|
107
|
+
### Via Web Interface
|
108
|
+
|
109
|
+
Open [http://localhost:8000/docs](http://localhost:8000/docs) in your browser to access the Swagger UI interactive documentation.
|
110
|
+
|
111
|
+
### Via Command Line
|
112
|
+
|
113
|
+
```bash
|
114
|
+
# Call echo command via JSON-RPC
|
115
|
+
curl -X POST "http://localhost:8000/api/jsonrpc" \
|
116
|
+
-H "Content-Type: application/json" \
|
117
|
+
-d '{"jsonrpc": "2.0", "method": "echo", "params": {"message": "Hello!"}, "id": 1}'
|
118
|
+
|
119
|
+
# Call math command via simplified endpoint
|
120
|
+
curl -X POST "http://localhost:8000/cmd" \
|
121
|
+
-H "Content-Type: application/json" \
|
122
|
+
-d '{"command": "math", "params": {"a": 10, "b": 5, "operation": "add"}}'
|
123
|
+
|
124
|
+
# Call time command via endpoint /api/command/{command_name}
|
125
|
+
curl -X POST "http://localhost:8000/api/command/time" \
|
126
|
+
-H "Content-Type: application/json" \
|
127
|
+
-d '{"format": "%d.%m.%Y %H:%M:%S", "timezone": "UTC"}'
|
128
|
+
```
|
129
|
+
|
130
|
+
## Key Features Demonstrated
|
131
|
+
|
132
|
+
1. Command organization in separate files
|
133
|
+
2. Automatic command discovery and registration
|
134
|
+
3. Different command types and parameter handling
|
135
|
+
4. Error handling
|
136
|
+
5. Different ways to call commands (JSON-RPC, /cmd, /api/command/{command_name})
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Базовый пример MCP Microservice
|
2
|
+
|
3
|
+
Этот пример демонстрирует базовую функциональность MCP Microservice с несколькими командами,
|
4
|
+
организованными в отдельные файлы.
|
5
|
+
|
6
|
+
## Структура
|
7
|
+
|
8
|
+
```
|
9
|
+
basic_example/
|
10
|
+
├── commands/ # Директория с командами
|
11
|
+
│ ├── __init__.py # Инициализация пакета
|
12
|
+
│ ├── echo_command.py # Команда эхо
|
13
|
+
│ ├── math_command.py # Математическая команда
|
14
|
+
│ └── time_command.py # Команда времени
|
15
|
+
├── config.yaml # Файл конфигурации
|
16
|
+
├── docs/ # Документация
|
17
|
+
│ ├── EN/ # Английская документация
|
18
|
+
│ │ └── README.md # Английская версия данного файла
|
19
|
+
│ └── RU/ # Русская документация
|
20
|
+
│ └── README.md # Этот файл
|
21
|
+
├── logs/ # Директория для логов
|
22
|
+
└── server.py # Файл запуска сервера
|
23
|
+
```
|
24
|
+
|
25
|
+
## Запуск примера
|
26
|
+
|
27
|
+
```bash
|
28
|
+
# Перейти в директорию проекта
|
29
|
+
cd examples/basic_example
|
30
|
+
|
31
|
+
# Создать директорию для логов, если она не существует
|
32
|
+
mkdir -p logs
|
33
|
+
|
34
|
+
# Запустить сервер
|
35
|
+
python server.py
|
36
|
+
```
|
37
|
+
|
38
|
+
После запуска сервер будет доступен по адресу [http://localhost:8000](http://localhost:8000).
|
39
|
+
|
40
|
+
## Доступные команды
|
41
|
+
|
42
|
+
### 1. `echo` - Эхо-команда
|
43
|
+
|
44
|
+
Возвращает переданное сообщение.
|
45
|
+
|
46
|
+
**Параметры:**
|
47
|
+
- `message` (string) - Сообщение для эхо
|
48
|
+
|
49
|
+
**Пример запроса:**
|
50
|
+
```json
|
51
|
+
{
|
52
|
+
"jsonrpc": "2.0",
|
53
|
+
"method": "echo",
|
54
|
+
"params": {
|
55
|
+
"message": "Hello, World!"
|
56
|
+
},
|
57
|
+
"id": 1
|
58
|
+
}
|
59
|
+
```
|
60
|
+
|
61
|
+
### 2. `math` - Математическая команда
|
62
|
+
|
63
|
+
Выполняет математическую операцию над двумя числами.
|
64
|
+
|
65
|
+
**Параметры:**
|
66
|
+
- `a` (number) - Первое число
|
67
|
+
- `b` (number) - Второе число
|
68
|
+
- `operation` (string) - Операция (add, subtract, multiply, divide)
|
69
|
+
|
70
|
+
**Пример запроса:**
|
71
|
+
```json
|
72
|
+
{
|
73
|
+
"jsonrpc": "2.0",
|
74
|
+
"method": "math",
|
75
|
+
"params": {
|
76
|
+
"a": 10,
|
77
|
+
"b": 5,
|
78
|
+
"operation": "add"
|
79
|
+
},
|
80
|
+
"id": 1
|
81
|
+
}
|
82
|
+
```
|
83
|
+
|
84
|
+
### 3. `time` - Команда времени
|
85
|
+
|
86
|
+
Возвращает текущее время и дату.
|
87
|
+
|
88
|
+
**Параметры:**
|
89
|
+
- `format` (string, опционально) - Формат времени (по умолчанию: "%Y-%m-%d %H:%M:%S")
|
90
|
+
- `timezone` (string, опционально) - Часовой пояс (по умолчанию: "UTC")
|
91
|
+
|
92
|
+
**Пример запроса:**
|
93
|
+
```json
|
94
|
+
{
|
95
|
+
"jsonrpc": "2.0",
|
96
|
+
"method": "time",
|
97
|
+
"params": {
|
98
|
+
"format": "%d.%m.%Y %H:%M:%S",
|
99
|
+
"timezone": "Europe/Moscow"
|
100
|
+
},
|
101
|
+
"id": 1
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
105
|
+
## Тестирование API
|
106
|
+
|
107
|
+
### Через веб-интерфейс
|
108
|
+
|
109
|
+
Откройте в браузере [http://localhost:8000/docs](http://localhost:8000/docs) для доступа к интерактивной документации Swagger UI.
|
110
|
+
|
111
|
+
### Через командную строку
|
112
|
+
|
113
|
+
```bash
|
114
|
+
# Вызов команды echo через JSON-RPC
|
115
|
+
curl -X POST "http://localhost:8000/api/jsonrpc" \
|
116
|
+
-H "Content-Type: application/json" \
|
117
|
+
-d '{"jsonrpc": "2.0", "method": "echo", "params": {"message": "Hello!"}, "id": 1}'
|
118
|
+
|
119
|
+
# Вызов команды math через упрощенный эндпоинт
|
120
|
+
curl -X POST "http://localhost:8000/cmd" \
|
121
|
+
-H "Content-Type: application/json" \
|
122
|
+
-d '{"command": "math", "params": {"a": 10, "b": 5, "operation": "add"}}'
|
123
|
+
|
124
|
+
# Вызов команды time через endpoint /api/command/{command_name}
|
125
|
+
curl -X POST "http://localhost:8000/api/command/time" \
|
126
|
+
-H "Content-Type: application/json" \
|
127
|
+
-d '{"format": "%d.%m.%Y %H:%M:%S", "timezone": "UTC"}'
|
128
|
+
```
|
129
|
+
|
130
|
+
## Ключевые особенности, демонстрируемые примером
|
131
|
+
|
132
|
+
1. Организация команд в отдельные файлы
|
133
|
+
2. Автоматическое обнаружение и регистрация команд
|
134
|
+
3. Различные типы команд и параметров
|
135
|
+
4. Обработка ошибок
|
136
|
+
5. Различные способы вызова команд (JSON-RPC, /cmd, /api/command/{command_name})
|
@@ -0,0 +1,50 @@
|
|
1
|
+
"""
|
2
|
+
Пример запуска сервера mcp-proxy-adapter с базовыми командами.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import sys
|
7
|
+
import uvicorn
|
8
|
+
import logging
|
9
|
+
|
10
|
+
from mcp_proxy_adapter.api.app import create_app
|
11
|
+
from mcp_proxy_adapter.config import config
|
12
|
+
from mcp_proxy_adapter.core.logging import setup_logging
|
13
|
+
|
14
|
+
# Импортируем команды из примера
|
15
|
+
from commands.echo_command import EchoCommand
|
16
|
+
from commands.math_command import MathCommand
|
17
|
+
|
18
|
+
def main():
|
19
|
+
"""
|
20
|
+
Основная функция запуска сервера.
|
21
|
+
"""
|
22
|
+
try:
|
23
|
+
# Настраиваем логирование
|
24
|
+
log_level = config.get("logging.level", "INFO")
|
25
|
+
setup_logging(log_level)
|
26
|
+
|
27
|
+
# Получаем настройки сервера
|
28
|
+
host = config.get("server.host", "0.0.0.0")
|
29
|
+
port = int(config.get("server.port", 8000))
|
30
|
+
|
31
|
+
# Регистрируем команды
|
32
|
+
app = create_app()
|
33
|
+
|
34
|
+
# Запускаем сервер
|
35
|
+
print(f"Запуск сервера на http://{host}:{port}")
|
36
|
+
print(f"Документация доступна по адресу: http://{host}:{port}/docs")
|
37
|
+
|
38
|
+
uvicorn.run(
|
39
|
+
app,
|
40
|
+
host=host,
|
41
|
+
port=port,
|
42
|
+
reload=True if os.environ.get("DEBUG") else False,
|
43
|
+
log_level=log_level.lower()
|
44
|
+
)
|
45
|
+
except Exception as e:
|
46
|
+
print(f"Ошибка при запуске приложения: {e}")
|
47
|
+
sys.exit(1)
|
48
|
+
|
49
|
+
if __name__ == "__main__":
|
50
|
+
main()
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"""
|
2
|
+
Basic MCP Microservice example.
|
3
|
+
|
4
|
+
This example demonstrates a microservice with multiple commands
|
5
|
+
organized in separate files.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import sys
|
10
|
+
from typing import Dict, Any
|
11
|
+
|
12
|
+
import mcp_proxy_adapter as mcp
|
13
|
+
from mcp_proxy_adapter import MicroService
|
14
|
+
|
15
|
+
# Add commands directory to path for local imports
|
16
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
17
|
+
|
18
|
+
|
19
|
+
def main():
|
20
|
+
"""Run microservice with command discovery."""
|
21
|
+
# Get absolute paths
|
22
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
23
|
+
config_path = os.path.join(current_dir, "config.yaml")
|
24
|
+
|
25
|
+
# Create log directory if it doesn't exist
|
26
|
+
os.makedirs(os.path.join(current_dir, "logs"), exist_ok=True)
|
27
|
+
|
28
|
+
# Create microservice
|
29
|
+
service = MicroService(
|
30
|
+
title="Basic MCP Microservice Example",
|
31
|
+
description="Example microservice with multiple commands",
|
32
|
+
version="1.0.0",
|
33
|
+
config_path=config_path
|
34
|
+
)
|
35
|
+
|
36
|
+
# Discover and register commands from the commands directory
|
37
|
+
package_path = "commands" # Relative import path
|
38
|
+
service.discover_commands(package_path)
|
39
|
+
|
40
|
+
# Run server
|
41
|
+
service.run(reload=True)
|
42
|
+
|
43
|
+
|
44
|
+
if __name__ == "__main__":
|
45
|
+
main()
|
@@ -0,0 +1,243 @@
|
|
1
|
+
"""
|
2
|
+
Pytest configuration and fixtures for basic example tests.
|
3
|
+
|
4
|
+
This module provides fixtures for testing the basic microservice example,
|
5
|
+
including running an actual server instance for integration tests.
|
6
|
+
All commands in the microservice are implemented as asynchronous functions.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import sys
|
11
|
+
import time
|
12
|
+
import socket
|
13
|
+
import asyncio
|
14
|
+
import threading
|
15
|
+
import multiprocessing
|
16
|
+
from typing import Callable, Dict, Any, List
|
17
|
+
|
18
|
+
import pytest
|
19
|
+
import requests
|
20
|
+
|
21
|
+
# Add parent directory to path for imports
|
22
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
23
|
+
|
24
|
+
# Import the server module from the parent directory
|
25
|
+
import server as server_module
|
26
|
+
from commands.echo_command import EchoCommand
|
27
|
+
from commands.math_command import MathCommand
|
28
|
+
from commands.time_command import TimeCommand
|
29
|
+
|
30
|
+
|
31
|
+
def find_free_port() -> int:
|
32
|
+
"""
|
33
|
+
Find a free port on localhost.
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
Free port number
|
37
|
+
"""
|
38
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
39
|
+
sock.bind(('localhost', 0))
|
40
|
+
return sock.getsockname()[1]
|
41
|
+
|
42
|
+
|
43
|
+
class ServerProcess:
|
44
|
+
"""Helper class to manage server process."""
|
45
|
+
|
46
|
+
def __init__(self, port: int):
|
47
|
+
"""
|
48
|
+
Initialize server process with a specified port.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
port: Port number to use
|
52
|
+
"""
|
53
|
+
self.port = port
|
54
|
+
self.process = None
|
55
|
+
|
56
|
+
def start(self) -> None:
|
57
|
+
"""Start the server in a separate process."""
|
58
|
+
def run_server():
|
59
|
+
# Mock the configuration to use the test port
|
60
|
+
os.environ["TEST_SERVER_PORT"] = str(self.port)
|
61
|
+
server_module.main()
|
62
|
+
|
63
|
+
self.process = multiprocessing.Process(target=run_server)
|
64
|
+
self.process.daemon = True
|
65
|
+
self.process.start()
|
66
|
+
|
67
|
+
# Wait for server to start
|
68
|
+
self._wait_for_server()
|
69
|
+
|
70
|
+
def stop(self) -> None:
|
71
|
+
"""Stop the server process."""
|
72
|
+
if self.process and self.process.is_alive():
|
73
|
+
self.process.terminate()
|
74
|
+
self.process.join(timeout=2)
|
75
|
+
|
76
|
+
def _wait_for_server(self, max_attempts: int = 10) -> None:
|
77
|
+
"""
|
78
|
+
Wait for the server to become available.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
max_attempts: Maximum number of connection attempts
|
82
|
+
"""
|
83
|
+
for i in range(max_attempts):
|
84
|
+
try:
|
85
|
+
response = requests.get(f"http://localhost:{self.port}/health")
|
86
|
+
if response.status_code == 200:
|
87
|
+
return
|
88
|
+
except requests.ConnectionError:
|
89
|
+
pass
|
90
|
+
|
91
|
+
time.sleep(0.5)
|
92
|
+
|
93
|
+
raise TimeoutError(f"Server did not start within {max_attempts * 0.5} seconds")
|
94
|
+
|
95
|
+
|
96
|
+
@pytest.fixture
|
97
|
+
def server_port() -> int:
|
98
|
+
"""
|
99
|
+
Fixture that provides a free port for the test server.
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Port number
|
103
|
+
"""
|
104
|
+
return find_free_port()
|
105
|
+
|
106
|
+
|
107
|
+
@pytest.fixture
|
108
|
+
def server(server_port: int) -> ServerProcess:
|
109
|
+
"""
|
110
|
+
Fixture that provides a running server instance.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
server_port: Port to run the server on
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
Server process object
|
117
|
+
"""
|
118
|
+
server_process = ServerProcess(server_port)
|
119
|
+
server_process.start()
|
120
|
+
|
121
|
+
yield server_process
|
122
|
+
|
123
|
+
server_process.stop()
|
124
|
+
|
125
|
+
|
126
|
+
@pytest.fixture
|
127
|
+
def api_url(server_port: int) -> str:
|
128
|
+
"""
|
129
|
+
Fixture that provides the base API URL.
|
130
|
+
|
131
|
+
Args:
|
132
|
+
server_port: Server port
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Base API URL
|
136
|
+
"""
|
137
|
+
return f"http://localhost:{server_port}"
|
138
|
+
|
139
|
+
|
140
|
+
@pytest.fixture
|
141
|
+
def jsonrpc_client(api_url: str) -> Callable:
|
142
|
+
"""
|
143
|
+
Fixture that provides a JSON-RPC client function.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
api_url: Base API URL
|
147
|
+
|
148
|
+
Returns:
|
149
|
+
Function to make JSON-RPC requests
|
150
|
+
"""
|
151
|
+
def make_request(method: str, params: Dict[str, Any], request_id: int = 1) -> Dict[str, Any]:
|
152
|
+
"""
|
153
|
+
Make a JSON-RPC request.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
method: Method name
|
157
|
+
params: Method parameters
|
158
|
+
request_id: Request ID
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
JSON-RPC response
|
162
|
+
"""
|
163
|
+
payload = {
|
164
|
+
"jsonrpc": "2.0",
|
165
|
+
"method": method,
|
166
|
+
"params": params,
|
167
|
+
"id": request_id
|
168
|
+
}
|
169
|
+
|
170
|
+
response = requests.post(
|
171
|
+
f"{api_url}/api/jsonrpc",
|
172
|
+
json=payload,
|
173
|
+
headers={"Content-Type": "application/json"}
|
174
|
+
)
|
175
|
+
|
176
|
+
return response.json()
|
177
|
+
|
178
|
+
return make_request
|
179
|
+
|
180
|
+
|
181
|
+
@pytest.fixture
|
182
|
+
def batch_jsonrpc_client(api_url: str) -> Callable:
|
183
|
+
"""
|
184
|
+
Fixture that provides a batch JSON-RPC client function.
|
185
|
+
|
186
|
+
Args:
|
187
|
+
api_url: Base API URL
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
Function to make batch JSON-RPC requests
|
191
|
+
"""
|
192
|
+
def make_batch_request(requests_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
193
|
+
"""
|
194
|
+
Make a batch JSON-RPC request.
|
195
|
+
|
196
|
+
Args:
|
197
|
+
requests_data: List of request objects
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
List of JSON-RPC responses
|
201
|
+
"""
|
202
|
+
response = requests.post(
|
203
|
+
f"{api_url}/api/jsonrpc",
|
204
|
+
json=requests_data,
|
205
|
+
headers={"Content-Type": "application/json"}
|
206
|
+
)
|
207
|
+
|
208
|
+
return response.json()
|
209
|
+
|
210
|
+
return make_batch_request
|
211
|
+
|
212
|
+
|
213
|
+
@pytest.fixture
|
214
|
+
def echo_command() -> EchoCommand:
|
215
|
+
"""
|
216
|
+
Fixture that provides an instance of EchoCommand.
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
EchoCommand instance
|
220
|
+
"""
|
221
|
+
return EchoCommand()
|
222
|
+
|
223
|
+
|
224
|
+
@pytest.fixture
|
225
|
+
def math_command() -> MathCommand:
|
226
|
+
"""
|
227
|
+
Fixture that provides an instance of MathCommand.
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
MathCommand instance
|
231
|
+
"""
|
232
|
+
return MathCommand()
|
233
|
+
|
234
|
+
|
235
|
+
@pytest.fixture
|
236
|
+
def time_command() -> TimeCommand:
|
237
|
+
"""
|
238
|
+
Fixture that provides an instance of TimeCommand.
|
239
|
+
|
240
|
+
Returns:
|
241
|
+
TimeCommand instance
|
242
|
+
"""
|
243
|
+
return TimeCommand()
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
Module with echo command implementation.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Any, Dict, Optional, ClassVar, Type
|
6
|
+
|
7
|
+
from pydantic import BaseModel, Field, ValidationError as PydanticValidationError
|
8
|
+
|
9
|
+
from mcp_proxy_adapter.commands.base import Command
|
10
|
+
from examples.commands.echo_result import EchoResult
|
11
|
+
from mcp_proxy_adapter.core.errors import ValidationError
|
12
|
+
from mcp_proxy_adapter.core.logging import logger
|
13
|
+
|
14
|
+
|
15
|
+
class EchoCommand(Command):
|
16
|
+
"""
|
17
|
+
Command that echoes back the parameters it receives.
|
18
|
+
|
19
|
+
This command is useful for testing parameter passing and debugging.
|
20
|
+
"""
|
21
|
+
|
22
|
+
name: ClassVar[str] = "echo"
|
23
|
+
result_class: ClassVar[Type[EchoResult]] = EchoResult
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def get_schema(cls) -> Dict[str, Any]:
|
27
|
+
"""
|
28
|
+
Returns JSON schema for command parameters validation.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
Dictionary with JSON schema.
|
32
|
+
"""
|
33
|
+
return {
|
34
|
+
"type": "object",
|
35
|
+
"additionalProperties": True,
|
36
|
+
"description": "Any parameters will be echoed back in the response"
|
37
|
+
}
|
38
|
+
|
39
|
+
async def execute(self, **kwargs) -> EchoResult:
|
40
|
+
"""
|
41
|
+
Executes echo command and returns the parameters back.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
**kwargs: Any parameters to echo back.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
EchoResult: Command execution result with the parameters.
|
48
|
+
"""
|
49
|
+
logger.debug(f"Echo command received parameters: {kwargs}")
|
50
|
+
|
51
|
+
# Simply return the parameters that were passed
|
52
|
+
return EchoResult(params=kwargs)
|