mcp-proxy-adapter 2.1.11__py3-none-any.whl → 2.1.13__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.
@@ -1,14 +1 @@
1
- """
2
- Analyzers for extracting metadata from functions and docstrings.
3
-
4
- This module contains classes for analyzing type annotations and docstrings
5
- of Python functions to automatically extract metadata for commands.
6
- """
7
-
8
- from .type_analyzer import TypeAnalyzer
9
- from .docstring_analyzer import DocstringAnalyzer
10
-
11
- __all__ = [
12
- 'TypeAnalyzer',
13
- 'DocstringAnalyzer',
14
- ]
1
+
@@ -1,14 +1 @@
1
- """
2
- Command dispatchers for registering and executing commands.
3
-
4
- This module contains base classes and implementations of command dispatchers
5
- that are responsible for registering and executing commands.
6
- """
7
-
8
- from .base_dispatcher import BaseDispatcher
9
- from .json_rpc_dispatcher import JsonRpcDispatcher
10
-
11
- __all__ = [
12
- 'BaseDispatcher',
13
- 'JsonRpcDispatcher',
14
- ]
1
+
@@ -1,14 +1,20 @@
1
1
  """
2
2
  Implementation of a JSON-RPC based command dispatcher.
3
+
4
+ CHANGELOG:
5
+ - 2024-06-13: execute() now always returns awaitable. If handler is sync and for any reason result is not awaitable, it is wrapped in an async function and awaited. This guarantees await-safety for all handler types and fixes 'object ... can't be used in await expression' errors in all environments.
3
6
  """
4
7
  from typing import Dict, Any, Callable, List, Optional, Union
5
8
  import inspect
6
9
  import logging
7
10
  import traceback
8
11
  from .base_dispatcher import BaseDispatcher
12
+ import asyncio
9
13
 
10
14
  logger = logging.getLogger("command_registry")
11
15
 
16
+ print('[DEBUG] LOADED json_rpc_dispatcher.py')
17
+
12
18
  class CommandError(Exception):
13
19
  """Base class for command errors"""
14
20
  pass
@@ -27,6 +33,34 @@ class JsonRpcDispatcher(BaseDispatcher):
27
33
 
28
34
  Implements the BaseDispatcher interface for handling commands in JSON-RPC 2.0 format.
29
35
  Supports registration, execution, and retrieval of command information.
36
+
37
+ Best practice:
38
+ ----------------
39
+ Register handlers explicitly using register_handler (no decorators!).
40
+ Both sync and async handlers are supported.
41
+
42
+ Example:
43
+ import asyncio
44
+ from mcp_proxy_adapter.dispatchers.json_rpc_dispatcher import JsonRpcDispatcher
45
+
46
+ def sync_handler(x):
47
+ return x + 1
48
+
49
+ async def async_handler(x):
50
+ await asyncio.sleep(0.1)
51
+ return x * 2
52
+
53
+ dispatcher = JsonRpcDispatcher()
54
+ dispatcher.register_handler('sync', sync_handler, description='Sync handler')
55
+ dispatcher.register_handler('async', async_handler, description='Async handler')
56
+
57
+ # Call sync handler
58
+ result_sync = asyncio.run(dispatcher.execute('sync', x=10))
59
+ print(result_sync) # 11
60
+
61
+ # Call async handler
62
+ result_async = asyncio.run(dispatcher.execute('async', x=10))
63
+ print(result_async) # 20
30
64
  """
31
65
 
32
66
  def __init__(self):
@@ -38,10 +72,14 @@ class JsonRpcDispatcher(BaseDispatcher):
38
72
  self.register_handler(
39
73
  command="help",
40
74
  handler=self._help_command,
41
- description="Returns information about available commands",
75
+ description=(
76
+ "Returns information about available commands.\n"
77
+ "Best practice: Register handlers explicitly using register_handler (no decorators).\n"
78
+ "Example: dispatcher.register_handler('mycmd', my_handler, description='...')"
79
+ ),
42
80
  summary="Command help",
43
81
  params={
44
- "cmdname": {
82
+ "command": {
45
83
  "type": "string",
46
84
  "description": "Command name for detailed information",
47
85
  "required": False
@@ -82,43 +120,41 @@ class JsonRpcDispatcher(BaseDispatcher):
82
120
 
83
121
  logger.debug(f"Registered command: {command}")
84
122
 
85
- def execute(self, command: str, **kwargs) -> Any:
86
- """
87
- Executes a command with the specified parameters.
88
-
89
- Args:
90
- command: Command name
91
- **kwargs: Command parameters
92
-
93
- Returns:
94
- Any: Command execution result
95
-
96
- Raises:
97
- CommandNotFoundError: If command is not found
98
- CommandExecutionError: On command execution error
99
- """
123
+ async def _call_handler_always_awaitable(self, handler, kwargs):
124
+ loop = asyncio.get_running_loop()
125
+ sig = inspect.signature(handler)
126
+ params = sig.parameters
127
+ try:
128
+ if inspect.iscoroutinefunction(handler):
129
+ if len(params) == 1 and 'params' in params:
130
+ result = handler(params=kwargs)
131
+ else:
132
+ result = handler(**kwargs)
133
+ else:
134
+ if len(params) == 1 and 'params' in params:
135
+ result = loop.run_in_executor(None, lambda: handler(params=kwargs))
136
+ else:
137
+ result = loop.run_in_executor(None, lambda: handler(**kwargs))
138
+ if inspect.isawaitable(result):
139
+ return await result
140
+ else:
141
+ async def _return_sync():
142
+ return result
143
+ return await _return_sync()
144
+ except Exception as e:
145
+ raise e
146
+
147
+ async def execute(self, command: str, **kwargs) -> Any:
100
148
  # Check if command exists
101
149
  if command not in self._handlers:
102
150
  raise CommandNotFoundError(f"Command '{command}' not found")
103
-
104
151
  handler = self._handlers[command]
105
-
106
152
  try:
107
- # Get function signature
108
- sig = inspect.signature(handler)
109
-
110
- # If function accepts params dictionary, pass all parameters in it
111
- if len(sig.parameters) == 1 and list(sig.parameters.keys())[0] == 'params':
112
- return handler(params=kwargs)
113
-
114
- # Otherwise pass parameters as named arguments
115
- return handler(**kwargs)
153
+ result = await self._call_handler_always_awaitable(handler, kwargs)
154
+ return result
116
155
  except Exception as e:
117
- # Log the error
118
156
  logger.error(f"Error executing command '{command}': {str(e)}")
119
157
  logger.debug(traceback.format_exc())
120
-
121
- # Re-raise the exception
122
158
  raise CommandExecutionError(f"Error executing command '{command}': {str(e)}")
123
159
 
124
160
  def get_valid_commands(self) -> List[str]:
@@ -160,7 +196,7 @@ class JsonRpcDispatcher(BaseDispatcher):
160
196
 
161
197
  Args:
162
198
  params: Command parameters
163
- cmdname: Command name for detailed information
199
+ command: Command name for detailed information
164
200
 
165
201
  Returns:
166
202
  Dict[str, Any]: Command help information
@@ -168,18 +204,18 @@ class JsonRpcDispatcher(BaseDispatcher):
168
204
  if not params:
169
205
  params = {}
170
206
 
171
- # Only support 'cmdname' parameter
172
- if "cmdname" in params and params["cmdname"]:
173
- cmdname = params["cmdname"]
174
- if cmdname not in self._metadata:
207
+ # If specific command is specified, return information only about it
208
+ if "command" in params and params["command"]:
209
+ command = params["command"]
210
+ if command not in self._metadata:
175
211
  return {
176
- "error": f"Command '{cmdname}' not found",
212
+ "error": f"Command '{command}' not found",
177
213
  "available_commands": list(self._metadata.keys())
178
214
  }
179
215
 
180
216
  return {
181
- "cmdname": cmdname,
182
- "info": self._metadata[cmdname]
217
+ "command": command,
218
+ "info": self._metadata[command]
183
219
  }
184
220
 
185
221
  # Otherwise return brief information about all commands
@@ -194,5 +230,5 @@ class JsonRpcDispatcher(BaseDispatcher):
194
230
  return {
195
231
  "commands": commands_info,
196
232
  "total": len(commands_info),
197
- "note": "To get info about a specific command, call help with parameter: POST /cmd {\"command\": \"help\", \"params\": {\"cmdname\": \"<command_name>\"}}. Only the 'cmdname' parameter is supported. Calling 'help <command>' (with space) is NOT supported."
233
+ "note": "Use the 'command' parameter to get detailed information about a specific command"
198
234
  }
@@ -14,14 +14,8 @@ if parent_dir not in sys.path:
14
14
 
15
15
  from fastapi import FastAPI, HTTPException, APIRouter
16
16
  from pydantic import BaseModel
17
-
18
- # Import from installed package or local file
19
- try:
20
- from mcp_proxy_adapter.adapter import MCPProxyAdapter, configure_logger
21
- from mcp_proxy_adapter.registry import CommandRegistry
22
- except ImportError:
23
- from src.adapter import MCPProxyAdapter, configure_logger
24
- from src.registry import CommandRegistry
17
+ from mcp_proxy_adapter.adapter import MCPProxyAdapter, configure_logger
18
+ from mcp_proxy_adapter.registry import CommandRegistry
25
19
 
26
20
  # Configure project logging
27
21
  logging.basicConfig(
@@ -24,29 +24,29 @@ registry = MockRegistry()
24
24
  adapter = MCPProxyAdapter(registry)
25
25
 
26
26
  # --- Best practice: always check if 'help' is in commands ---
27
- def call_help(cmdname: str = None) -> Dict[str, Any]:
27
+ def call_help(command: str = None) -> Dict[str, Any]:
28
28
  """Call help command with or without parameter."""
29
29
  dispatcher = registry.dispatcher
30
30
  if "help" in dispatcher.get_valid_commands():
31
- if cmdname:
31
+ if command:
32
32
  try:
33
- return dispatcher.help_command(cmdname=cmdname)
33
+ return dispatcher.help_command(command=command)
34
34
  except Exception as e:
35
35
  print(f"Project help failed: {e}. Fallback to adapter help.")
36
- return adapter_help(cmdname)
36
+ return adapter_help(command)
37
37
  else:
38
38
  return dispatcher.help_command()
39
39
  else:
40
- return adapter_help(cmdname)
40
+ return adapter_help(command)
41
41
 
42
- def adapter_help(cmdname: str = None) -> Dict[str, Any]:
42
+ def adapter_help(command: str = None) -> Dict[str, Any]:
43
43
  """Fallback: call adapter's help (simulate)."""
44
44
  dispatcher = registry.dispatcher
45
- if not cmdname:
45
+ if not command:
46
46
  return {"source": "adapter", "commands": dispatcher.get_valid_commands()}
47
- if cmdname in dispatcher.get_valid_commands():
48
- return {"source": "adapter", "cmdname": cmdname, "info": {"description": "Adapter help for command"}}
49
- return {"source": "adapter", "error": f"Command '{cmdname}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
47
+ if command in dispatcher.get_valid_commands():
48
+ return {"source": "adapter", "command": command, "info": {"description": "Adapter help for command"}}
49
+ return {"source": "adapter", "error": f"Command '{command}' not found (adapter)", "available_commands": dispatcher.get_valid_commands()}
50
50
 
51
51
  if __name__ == "__main__":
52
52
  print("=== Project help (no param) ===")
@@ -171,7 +171,7 @@ class MockDispatcher:
171
171
  "help": {
172
172
  "description": "Show information about available commands or a specific command.",
173
173
  "params": {
174
- "cmdname": {
174
+ "command": {
175
175
  "type": "string",
176
176
  "description": "Command name for detailed info",
177
177
  "required": False
@@ -278,18 +278,19 @@ class MockDispatcher:
278
278
 
279
279
  def help_command(self, **params):
280
280
  """Return info about all commands or a specific command."""
281
- cmdname = params.get("cmdname")
282
- if cmdname:
283
- info = self.commands_info.get(cmdname)
281
+ # Если в будущем появится пользовательская команда help, можно реализовать её здесь
282
+ command = params.get("command")
283
+ if command:
284
+ info = self.commands_info.get(command)
284
285
  if info:
285
- return {"cmdname": cmdname, "info": info}
286
+ return {"command": command, "info": info}
286
287
  else:
287
- return {"error": f"Command '{cmdname}' not found", "available_commands": list(self.commands_info.keys())}
288
- # Если параметр cmdname не указан, возвращаем краткую информацию обо всех
288
+ return {"error": f"Command '{command}' not found", "available_commands": list(self.commands_info.keys())}
289
+ # Если параметр command не указан, возвращаем краткую информацию обо всех
289
290
  return {
290
291
  "commands": {cmd: {"description": info["description"], "params": info["params"]} for cmd, info in self.commands_info.items()},
291
292
  "total": len(self.commands_info),
292
- "note": "Use the 'cmdname' parameter to get detailed information about a specific command"
293
+ "note": "Use the 'command' parameter to get detailed information about a specific command"
293
294
  }
294
295
 
295
296
  # --- Создание registry и FastAPI-приложения на верхнем уровне ---
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-proxy-adapter
3
+ Version: 2.1.13
4
+ Summary: Adapter for exposing Command Registry commands as tools for AI models via MCP Proxy.
5
+ Home-page: https://github.com/vasilyvz/mcp-proxy-adapter
6
+ Author: Vasiliy VZ
7
+ Author-email: Vasiliy VZ <vasilyvz@example.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/vasilyvz/mcp-proxy-adapter
10
+ Project-URL: Bug Tracker, https://github.com/vasilyvz/mcp-proxy-adapter/issues
11
+ Project-URL: Documentation, https://github.com/vasilyvz/mcp-proxy-adapter/tree/main/docs
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Requires-Python: >=3.9, <4
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: fastapi<1.0.0,>=0.95.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: uvicorn<1.0.0,>=0.22.0
26
+ Requires-Dist: docstring-parser<1.0.0,>=0.15
27
+ Requires-Dist: typing-extensions<5.0.0,>=4.5.0
28
+ Dynamic: author
29
+ Dynamic: home-page
30
+ Dynamic: license-file
31
+ Dynamic: requires-python
32
+
33
+ # MCP Proxy Adapter
34
+
35
+ Adapter for integrating [Command Registry](docs/README.md) with MCP Proxy, allowing you to use commands as tools for AI models.
36
+
37
+ ## Overview
38
+
39
+ MCP Proxy Adapter transforms commands registered in the Command Registry into a format compatible with MCP Proxy. This enables:
40
+
41
+ 1. Using existing commands as tools for AI models
42
+ 2. Creating a hybrid REST/JSON-RPC API for command execution
43
+ 3. Automatic generation of OpenAPI schemas optimized for MCP Proxy
44
+ 4. Managing tool metadata for better AI system integration
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install mcp-proxy-adapter
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ```python
55
+ from mcp_proxy_adapter import MCPProxyAdapter, CommandRegistry
56
+ from fastapi import FastAPI
57
+
58
+ # Create a command registry instance
59
+ registry = CommandRegistry()
60
+
61
+ # Register commands
62
+ def calculate_total(prices: list[float], discount: float = 0.0) -> float:
63
+ """
64
+ Calculates the total price with discount.
65
+ Args:
66
+ prices: List of item prices
67
+ discount: Discount percentage (0-100)
68
+ Returns:
69
+ Total price with discount
70
+ """
71
+ subtotal = sum(prices)
72
+ return subtotal * (1 - discount / 100)
73
+
74
+ registry.register_command("calculate_total", calculate_total)
75
+
76
+ # Create FastAPI app
77
+ app = FastAPI()
78
+
79
+ # Create and configure MCP Proxy adapter
80
+ adapter = MCPProxyAdapter(registry)
81
+
82
+ # Register endpoints in FastAPI app
83
+ adapter.register_endpoints(app)
84
+
85
+ # Generate and save MCP Proxy config
86
+ adapter.save_config_to_file("mcp_proxy_config.json")
87
+ ```
88
+
89
+ ## Supported Request Formats
90
+
91
+ The adapter supports three request formats for command execution:
92
+
93
+ ### 1. JSON-RPC format
94
+
95
+ ```json
96
+ {
97
+ "jsonrpc": "2.0",
98
+ "method": "command_name",
99
+ "params": {
100
+ "param1": "value1",
101
+ "param2": "value2"
102
+ },
103
+ "id": 1
104
+ }
105
+ ```
106
+
107
+ Example request to `/cmd` endpoint:
108
+
109
+ ```bash
110
+ curl -X POST -H "Content-Type: application/json" -d '{
111
+ "jsonrpc": "2.0",
112
+ "method": "calculate_total",
113
+ "params": {
114
+ "prices": [100, 200, 300],
115
+ "discount": 10
116
+ },
117
+ "id": 1
118
+ }' http://localhost:8000/cmd
119
+ ```
120
+
121
+ Response:
122
+
123
+ ```json
124
+ {
125
+ "jsonrpc": "2.0",
126
+ "result": 540.0,
127
+ "id": 1
128
+ }
129
+ ```
130
+
131
+ ### 2. MCP Proxy format
132
+
133
+ ```json
134
+ {
135
+ "command": "command_name",
136
+ "params": {
137
+ "param1": "value1",
138
+ "param2": "value2"
139
+ }
140
+ }
141
+ ```
142
+
143
+ Example request:
144
+
145
+ ```bash
146
+ curl -X POST -H "Content-Type: application/json" -d '{
147
+ "command": "calculate_total",
148
+ "params": {
149
+ "prices": [100, 200, 300],
150
+ "discount": 10
151
+ }
152
+ }' http://localhost:8000/cmd
153
+ ```
154
+
155
+ Response:
156
+
157
+ ```json
158
+ {
159
+ "result": 540.0
160
+ }
161
+ ```
162
+
163
+ ### 3. Params-only format
164
+
165
+ ```json
166
+ {
167
+ "params": {
168
+ "command": "command_name",
169
+ "param1": "value1",
170
+ "param2": "value2"
171
+ }
172
+ }
173
+ ```
174
+
175
+ or
176
+
177
+ ```json
178
+ {
179
+ "params": {
180
+ "query": "command_name",
181
+ "param1": "value1",
182
+ "param2": "value2"
183
+ }
184
+ }
185
+ ```
186
+
187
+ Example request:
188
+
189
+ ```bash
190
+ curl -X POST -H "Content-Type: application/json" -d '{
191
+ "params": {
192
+ "command": "calculate_total",
193
+ "prices": [100, 200, 300],
194
+ "discount": 10
195
+ }
196
+ }' http://localhost:8000/cmd
197
+ ```
198
+
199
+ Response:
200
+
201
+ ```json
202
+ {
203
+ "result": 540.0
204
+ }
205
+ ```
206
+
207
+ ## Full Example: Integration with FastAPI
208
+
209
+ ```python
210
+ import logging
211
+ from fastapi import FastAPI, APIRouter
212
+ from mcp_proxy_adapter import CommandRegistry, MCPProxyAdapter, configure_logger
213
+
214
+ # Configure logging
215
+ logging.basicConfig(level=logging.INFO)
216
+ project_logger = logging.getLogger("my_project")
217
+
218
+ # Create FastAPI app
219
+ app = FastAPI(title="My API with MCP Proxy Integration")
220
+
221
+ # Create existing API router
222
+ router = APIRouter()
223
+
224
+ @router.get("/items")
225
+ async def get_items():
226
+ """Returns a list of items."""
227
+ return [
228
+ {"id": 1, "name": "Smartphone X", "price": 999.99},
229
+ {"id": 2, "name": "Laptop Y", "price": 1499.99},
230
+ ]
231
+
232
+ app.include_router(router)
233
+
234
+ # Register commands
235
+ registry = CommandRegistry()
236
+
237
+ def get_discounted_price(price: float, discount: float = 0.0) -> float:
238
+ """
239
+ Returns the price after applying a discount.
240
+ """
241
+ return price * (1 - discount / 100)
242
+
243
+ registry.register_command("get_discounted_price", get_discounted_price)
244
+
245
+ # Create and register MCP Proxy adapter
246
+ adapter = MCPProxyAdapter(registry)
247
+ adapter.register_endpoints(app)
248
+
249
+ # Save MCP Proxy config
250
+ adapter.save_config_to_file("mcp_proxy_config.json")
251
+ ```
252
+
253
+ ## Features
254
+ - Universal JSON-RPC endpoint for command execution
255
+ - Automatic OpenAPI schema generation and optimization for MCP Proxy
256
+ - Tool metadata for AI models
257
+ - Customizable endpoints and logging
258
+ - Full test coverage and examples
259
+
260
+ ## FAQ
261
+
262
+ **Q: Почему не удаётся использовать декоратор @registry.command для регистрации команд?**
263
+ A: В последних версиях пакета декоратор @registry.command больше не поддерживается. Теперь команды регистрируются только явно, через вызов метода:
264
+
265
+ ```python
266
+ registry.register_command("имя_команды", функция)
267
+ ```
268
+
269
+ **Q: Почему не удаётся импортировать CommandRegistry напрямую из mcp_proxy_adapter?**
270
+ A: Класс CommandRegistry находится в подмодуле registry. Используйте:
271
+
272
+ ```python
273
+ from mcp_proxy_adapter.registry import CommandRegistry
274
+ ```
275
+
276
+ **Q: Какой способ импорта MCPProxyAdapter и configure_logger?**
277
+ A: Импортируйте их из подмодуля adapter:
278
+
279
+ ```python
280
+ from mcp_proxy_adapter.adapter import MCPProxyAdapter, configure_logger
281
+ ```
282
+
283
+ **Q: Как добавить свою команду?**
284
+ A: Зарегистрируйте функцию через registry.register_command или декоратор (см. примеры выше).
285
+
286
+ **Q: Как получить OpenAPI-схему?**
287
+ A: После регистрации адаптера вызовите /openapi.json в вашем FastAPI-приложении.
288
+
289
+ **Q: Какой формат запроса поддерживается?**
290
+ A: JSON-RPC, MCP Proxy и params-only (см. раздел Supported Request Formats).
291
+
292
+ ## HOWTO
293
+
294
+ **Как интегрировать MCP Proxy Adapter с FastAPI:**
295
+ 1. Установите пакет:
296
+ ```bash
297
+ pip install mcp-proxy-adapter
298
+ ```
299
+ 2. Импортируйте нужные классы:
300
+ ```python
301
+ from mcp_proxy_adapter.registry import CommandRegistry
302
+ from mcp_proxy_adapter.adapter import MCPProxyAdapter
303
+ ```
304
+ 3. Зарегистрируйте команды и настройте FastAPI:
305
+ ```python
306
+ registry = CommandRegistry()
307
+ registry.register_command("my_command", my_function)
308
+ app = FastAPI()
309
+ adapter = MCPProxyAdapter(registry)
310
+ adapter.register_endpoints(app)
311
+ ```
312
+ 4. (Опционально) Сохраните конфиг для MCP Proxy:
313
+ ```python
314
+ adapter.save_config_to_file("mcp_proxy_config.json")
315
+ ```
316
+
317
+ **Как добавить поддержку кастомного логгера:**
318
+ ```python
319
+ import logging
320
+ from mcp_proxy_adapter.adapter import configure_logger
321
+ logger = logging.getLogger("my_project")
322
+ adapter_logger = configure_logger(logger)
323
+ ```
324
+
325
+ **Как получить список всех команд через API:**
326
+ Вызовите GET `/api/commands` на вашем сервере FastAPI.
327
+
328
+ ## License
329
+ MIT
330
+
331
+ ## Documentation
332
+ See [docs/](docs/) for detailed guides, architecture, and examples.
333
+
334
+ ## CI/CD & PyPI automation
335
+
336
+ This project uses GitHub Actions for continuous integration and automated publishing to PyPI.
337
+
338
+ - All tests are run on every push and pull request.
339
+ - On push of a new tag (vX.Y.Z), the package is built and published to PyPI automatically.
340
+
341
+ See `.github/workflows/publish.yml` for details.
@@ -0,0 +1,19 @@
1
+ mcp_proxy_adapter/testing_utils.py,sha256=RWjQFNSUtVkeP0qNzp6_jrT6_tub3w_052DrRmvxVk0,4243
2
+ mcp_proxy_adapter/analyzers/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
3
+ mcp_proxy_adapter/dispatchers/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
4
+ mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py,sha256=Ibp-4d4kKf_AW_yv_27Zb_PgFIfa3tZLmuppMg8YqsY,8034
5
+ mcp_proxy_adapter/examples/analyze_config.py,sha256=vog7TNHDw5ZoYhQLbAvZvEoufmQwH54KJzQBJrSq5w4,4283
6
+ mcp_proxy_adapter/examples/basic_integration.py,sha256=mtRval4VSUgTb_C2p8U_DPPSEKA08dZYKZk-bOrE4H4,4470
7
+ mcp_proxy_adapter/examples/docstring_and_schema_example.py,sha256=c96L4KF_7yWzffmvd4hyeQuXSdYyYkv7Uvuy0QxgMcQ,1929
8
+ mcp_proxy_adapter/examples/extension_example.py,sha256=vnatnFdNTapMpPcQ79Ugitk92ZiUfpLTs7Dvsodf1og,2277
9
+ mcp_proxy_adapter/examples/help_best_practices.py,sha256=Bit9Ywl9vGvM_kuV8DJ6pIDK4mY4mF2Gia9rLc56RpI,2646
10
+ mcp_proxy_adapter/examples/help_usage.py,sha256=JIUsZofdLFyI7FcwPF-rLxipF1-HaZINzVK1KBh0vxA,2577
11
+ mcp_proxy_adapter/examples/mcp_proxy_client.py,sha256=z4IzFlGigVTQSb8TpcrQ_a0migsmC58LnNwc8wZmTfw,3811
12
+ mcp_proxy_adapter/examples/openapi_server.py,sha256=5gRM-EHvMsnNtS_M6l_pNPN5EkSf4X1Lcq4E1Xs5tp0,13387
13
+ mcp_proxy_adapter/examples/project_structure_example.py,sha256=sswTo6FZb1F5juHa0FYG3cgvrh3wfgGfJu2bBy5tCm4,1460
14
+ mcp_proxy_adapter/examples/testing_example.py,sha256=AB13c4C1bjs1145O-yriwyreeVXtMOlQLzs2BCGmprk,1719
15
+ mcp_proxy_adapter-2.1.13.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
16
+ mcp_proxy_adapter-2.1.13.dist-info/METADATA,sha256=1flBa3KLq1QMMf9T-NcpgpP3LxJY4_BwMAUW21K915Q,8886
17
+ mcp_proxy_adapter-2.1.13.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
18
+ mcp_proxy_adapter-2.1.13.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
19
+ mcp_proxy_adapter-2.1.13.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5