stinger-python-utils 0.1.8rc4__tar.gz → 0.1.8rc5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/PKG-INFO +1 -1
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/pyproject.toml +1 -1
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/mcp/plugin.py +17 -11
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/mcp/server.py +24 -11
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.github/workflows/python-tests.yml +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.github/workflows/python37.yml +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.gitignore +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.python-version +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.vscode/settings.json +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/LICENSE +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/README.md +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/__init__.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/mcp/__init__.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/mcp/__main__.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/message_creator.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/return_codes.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/test/__init__.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/test/test_message_creator.py +0 -0
- {stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/uv.lock +0 -0
|
@@ -17,6 +17,8 @@ from abc import ABC, abstractmethod
|
|
|
17
17
|
from dataclasses import dataclass, field
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
|
+
from pydantic import BaseModel
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
# ------------------------------------------------------------------
|
|
22
24
|
# Data models
|
|
@@ -66,19 +68,18 @@ class MethodDefinition:
|
|
|
66
68
|
"""Describes a callable method on a stinger-ipc client.
|
|
67
69
|
|
|
68
70
|
Each method is exposed as an MCP **tool** whose ``inputSchema`` is
|
|
69
|
-
*
|
|
71
|
+
derived from *arguments_model* via ``model_json_schema()``.
|
|
70
72
|
|
|
71
|
-
*
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
*arguments_model* must be a :class:`pydantic.BaseModel` subclass.
|
|
74
|
+
When the tool is invoked, the raw JSON arguments are loaded into
|
|
75
|
+
an instance of this model and the model is passed to
|
|
76
|
+
``call_{method_name}`` on the client.
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
If *arguments_model* is ``None`` the tool accepts no arguments.
|
|
76
79
|
"""
|
|
77
80
|
|
|
78
81
|
name: str
|
|
79
|
-
|
|
80
|
-
default_factory=lambda: {"type": "object"}
|
|
81
|
-
)
|
|
82
|
+
arguments_model: type[BaseModel] | None = None
|
|
82
83
|
description: str = ""
|
|
83
84
|
|
|
84
85
|
|
|
@@ -189,19 +190,24 @@ class StingerMCPPlugin(ABC):
|
|
|
189
190
|
setattr(client, prop_name, list(arguments.values())[0])
|
|
190
191
|
|
|
191
192
|
def call_method(
|
|
192
|
-
self, client: Any, method_name: str, arguments:
|
|
193
|
+
self, client: Any, method_name: str, arguments: BaseModel | None
|
|
193
194
|
) -> Any:
|
|
194
195
|
"""Invoke *method_name* on *client* with *arguments*.
|
|
195
196
|
|
|
197
|
+
*arguments* is a validated :class:`pydantic.BaseModel` instance
|
|
198
|
+
(or ``None`` when the method takes no parameters).
|
|
199
|
+
|
|
196
200
|
The default implementation calls::
|
|
197
201
|
|
|
198
|
-
getattr(client, method_name)(
|
|
202
|
+
getattr(client, f"call_{method_name}")(arguments)
|
|
199
203
|
|
|
200
204
|
and returns whatever the method returns (typically a
|
|
201
205
|
``concurrent.futures.Future``).
|
|
202
206
|
"""
|
|
203
207
|
method = getattr(client, f"call_{method_name}")
|
|
204
|
-
|
|
208
|
+
if arguments is None:
|
|
209
|
+
return method()
|
|
210
|
+
return method(arguments)
|
|
205
211
|
|
|
206
212
|
def serialize_property(self, prop_name: str, value: Any) -> str:
|
|
207
213
|
"""Serialize a property *value* to a JSON string for the MCP resource.
|
|
@@ -193,7 +193,11 @@ class StingerMCPServer:
|
|
|
193
193
|
or f"Call {mdef.name} on {state.plugin_name} "
|
|
194
194
|
f"instance {state.instance_id}"
|
|
195
195
|
),
|
|
196
|
-
inputSchema=
|
|
196
|
+
inputSchema=(
|
|
197
|
+
mdef.arguments_model.model_json_schema()
|
|
198
|
+
if mdef.arguments_model is not None
|
|
199
|
+
else {"type": "object"}
|
|
200
|
+
),
|
|
197
201
|
)
|
|
198
202
|
)
|
|
199
203
|
|
|
@@ -214,8 +218,8 @@ class StingerMCPServer:
|
|
|
214
218
|
|
|
215
219
|
def _resolve_tool(
|
|
216
220
|
self, name: str
|
|
217
|
-
) -> tuple[InstanceState, str,
|
|
218
|
-
"""Map a tool *name* → ``(state, kind,
|
|
221
|
+
) -> tuple[InstanceState, str, MethodDefinition | PropertyDefinition] | None:
|
|
222
|
+
"""Map a tool *name* → ``(state, kind, definition)``.
|
|
219
223
|
|
|
220
224
|
*kind* is ``"method"`` or ``"property"``. Methods are checked
|
|
221
225
|
first so that a method named ``set_foo`` takes precedence over
|
|
@@ -235,7 +239,7 @@ class StingerMCPServer:
|
|
|
235
239
|
# Methods take priority
|
|
236
240
|
for mdef in state.plugin.get_methods():
|
|
237
241
|
if _sanitize(mdef.name) == remainder:
|
|
238
|
-
return state, "method", mdef
|
|
242
|
+
return state, "method", mdef
|
|
239
243
|
|
|
240
244
|
# Property setters: set_<prop_name>
|
|
241
245
|
if remainder.startswith("set_"):
|
|
@@ -245,7 +249,7 @@ class StingerMCPServer:
|
|
|
245
249
|
_sanitize(pdef.name) == prop_token
|
|
246
250
|
and not pdef.readonly
|
|
247
251
|
):
|
|
248
|
-
return state, "property", pdef
|
|
252
|
+
return state, "property", pdef
|
|
249
253
|
|
|
250
254
|
return None
|
|
251
255
|
|
|
@@ -256,23 +260,32 @@ class StingerMCPServer:
|
|
|
256
260
|
if target is None:
|
|
257
261
|
raise ValueError(f"Unknown tool: {name}")
|
|
258
262
|
|
|
259
|
-
state, kind,
|
|
263
|
+
state, kind, defn = target
|
|
260
264
|
|
|
261
265
|
if kind == "property":
|
|
262
266
|
try:
|
|
263
|
-
state.plugin.write_property(
|
|
264
|
-
|
|
267
|
+
state.plugin.write_property(
|
|
268
|
+
state.client, defn.name, arguments
|
|
269
|
+
)
|
|
270
|
+
text = json.dumps({"status": "ok", "property": defn.name})
|
|
265
271
|
except Exception as exc:
|
|
266
|
-
logger.exception("Error setting property %s",
|
|
272
|
+
logger.exception("Error setting property %s", defn.name)
|
|
267
273
|
text = json.dumps(
|
|
268
274
|
{"status": "error", "error": str(exc)}, default=str
|
|
269
275
|
)
|
|
270
276
|
return [types.TextContent(type="text", text=text)]
|
|
271
277
|
|
|
272
278
|
# kind == "method"
|
|
279
|
+
assert isinstance(defn, MethodDefinition)
|
|
273
280
|
try:
|
|
281
|
+
# Load the pydantic model from the raw arguments
|
|
282
|
+
model = (
|
|
283
|
+
defn.arguments_model(**arguments)
|
|
284
|
+
if defn.arguments_model is not None
|
|
285
|
+
else None
|
|
286
|
+
)
|
|
274
287
|
result = state.plugin.call_method(
|
|
275
|
-
state.client,
|
|
288
|
+
state.client, defn.name, model
|
|
276
289
|
)
|
|
277
290
|
if isinstance(result, Future):
|
|
278
291
|
result = await _resolve_future(result)
|
|
@@ -284,7 +297,7 @@ class StingerMCPServer:
|
|
|
284
297
|
else:
|
|
285
298
|
text = json.dumps(result, default=str)
|
|
286
299
|
except Exception as exc:
|
|
287
|
-
logger.exception("Error calling method %s",
|
|
300
|
+
logger.exception("Error calling method %s", defn.name)
|
|
288
301
|
text = json.dumps(
|
|
289
302
|
{"status": "error", "error": str(exc)}, default=str
|
|
290
303
|
)
|
{stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.github/workflows/python-tests.yml
RENAMED
|
File without changes
|
{stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/.github/workflows/python37.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/src/stinger_python_utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stinger_python_utils-0.1.8rc4 → stinger_python_utils-0.1.8rc5}/test/test_message_creator.py
RENAMED
|
File without changes
|
|
File without changes
|