Pyjo-MCP 0.1.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.
- Pyjo/MCP/Client.py +840 -0
- Pyjo/MCP/Prompt.py +260 -0
- Pyjo/MCP/Resource.py +205 -0
- Pyjo/MCP/ResourceTemplate.py +276 -0
- Pyjo/MCP/Server.py +1526 -0
- Pyjo/MCP/Test.py +761 -0
- Pyjo/MCP/Tool.py +399 -0
- Pyjo/MCP/__init__.py +80 -0
- pyjo_mcp-0.1.0.dist-info/METADATA +193 -0
- pyjo_mcp-0.1.0.dist-info/RECORD +12 -0
- pyjo_mcp-0.1.0.dist-info/WHEEL +4 -0
- pyjo_mcp-0.1.0.dist-info/licenses/LICENSE +201 -0
Pyjo/MCP/Prompt.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjo.MCP.Prompt - Prompt of a server of MCP
|
|
3
|
+
===========================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.MCP.Prompt import Prompt
|
|
7
|
+
|
|
8
|
+
def review(code: str, language: str = 'Python') -> str:
|
|
9
|
+
\"\"\"Review a piece of code.
|
|
10
|
+
|
|
11
|
+
:param code: The code to review.
|
|
12
|
+
:param language: The language of the code.
|
|
13
|
+
\"\"\"
|
|
14
|
+
return f'Review this {language} code:\\n{code}'
|
|
15
|
+
|
|
16
|
+
prompt = Prompt(review)
|
|
17
|
+
print(prompt.name, prompt.description, prompt.arguments)
|
|
18
|
+
|
|
19
|
+
# Or import the module, as in Google Python Style Guide
|
|
20
|
+
|
|
21
|
+
import Pyjo.MCP.Prompt
|
|
22
|
+
|
|
23
|
+
prompt = Pyjo.MCP.Prompt.new(review, name='code_review', title='Code review')
|
|
24
|
+
prompt.get_p({'code': 'x = 1'}).then(print).wait()
|
|
25
|
+
|
|
26
|
+
:mod:`Pyjo.MCP.Prompt` is a prompt of :mod:`Pyjo.MCP.Server`, a template of
|
|
27
|
+
messages for a language model, which a user picks and fills with its
|
|
28
|
+
arguments. The name, the description and the arguments of the prompt are those
|
|
29
|
+
of the function by default: its name, the first paragraph of its docstring,
|
|
30
|
+
and its parameters, with the descriptions of its docstring, of which those
|
|
31
|
+
without a default are required.
|
|
32
|
+
|
|
33
|
+
The function returns the messages of the prompt, as a value, a
|
|
34
|
+
:mod:`Pyjo.Promise` of it or a coroutine, of an ``async def``:
|
|
35
|
+
|
|
36
|
+
* a string is one message of the user with the text;
|
|
37
|
+
* a :class:`list` is the messages, each with its ``role`` and its
|
|
38
|
+
``content``;
|
|
39
|
+
* a :class:`dict` with ``messages`` is the result as it is, as with a
|
|
40
|
+
``description`` of its own.
|
|
41
|
+
|
|
42
|
+
A missing argument that is required, or one that the function does not take,
|
|
43
|
+
is an error of the request, ``-32602``, as the specification requires, and an
|
|
44
|
+
exception of the function an error of the server.
|
|
45
|
+
|
|
46
|
+
See also
|
|
47
|
+
--------
|
|
48
|
+
|
|
49
|
+
:mod:`Pyjo.MCP.Server`, :mod:`Pyjo.MCP.Resource`, :mod:`Pyjo.MCP.Tool`,
|
|
50
|
+
:mod:`Pyjo.MCP`.
|
|
51
|
+
|
|
52
|
+
Classes
|
|
53
|
+
-------
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
import Pyjo.Base
|
|
57
|
+
import Pyjo.JSON.RPC
|
|
58
|
+
import Pyjo.JSON.Schema
|
|
59
|
+
import Pyjo.Promise
|
|
60
|
+
|
|
61
|
+
import inspect
|
|
62
|
+
|
|
63
|
+
from collections.abc import Callable, Mapping
|
|
64
|
+
from typing import Any, cast
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Pyjo_MCP_Prompt(Pyjo.Base.object):
|
|
68
|
+
"""
|
|
69
|
+
:mod:`Pyjo.MCP.Prompt` inherits all methods from :mod:`Pyjo.Base` and
|
|
70
|
+
implements the following new ones.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
cb: Callable[..., Any],
|
|
76
|
+
*,
|
|
77
|
+
name: str | None = None,
|
|
78
|
+
title: str | None = None,
|
|
79
|
+
description: str | None = None,
|
|
80
|
+
arguments: list[Mapping[str, Any]] | None = None,
|
|
81
|
+
context: bool = False,
|
|
82
|
+
complete: Mapping[str, Any] | None = None,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""::
|
|
85
|
+
|
|
86
|
+
prompt = Pyjo.MCP.Prompt.new(review)
|
|
87
|
+
prompt = Pyjo.MCP.Prompt.new(review, name='code_review', title='Code review')
|
|
88
|
+
prompt = Pyjo.MCP.Prompt.new(lambda **args: ..., arguments=[{'name': 'code', 'required': True}])
|
|
89
|
+
prompt = Pyjo.MCP.Prompt.new(lambda context, code: ..., context=True)
|
|
90
|
+
prompt = Pyjo.MCP.Prompt.new(review, complete={'language': ['Perl', 'Python']})
|
|
91
|
+
|
|
92
|
+
Construct a new :mod:`Pyjo.MCP.Prompt` object of a function. With
|
|
93
|
+
``context``, the function gets the :class:`~Pyjo.MCP.Server.Context` of
|
|
94
|
+
the request before its arguments.
|
|
95
|
+
"""
|
|
96
|
+
skip = 1 if context else 0
|
|
97
|
+
completions: dict[str, Any] = {}
|
|
98
|
+
if arguments is None:
|
|
99
|
+
data = cast(dict[str, Any], Pyjo.JSON.Schema.parameters(cb, skip=skip).data)
|
|
100
|
+
if description is None:
|
|
101
|
+
description = data.get('description')
|
|
102
|
+
required = cast(list[str], data.get('required', []))
|
|
103
|
+
arguments = []
|
|
104
|
+
for key, property in cast(dict[str, Any], data.get('properties', {})).items():
|
|
105
|
+
argument: dict[str, Any] = {'name': key}
|
|
106
|
+
if (text := cast(dict[str, Any], property).get('description')) is not None:
|
|
107
|
+
argument['description'] = text
|
|
108
|
+
argument['required'] = key in required
|
|
109
|
+
arguments.append(argument)
|
|
110
|
+
if isinstance(values := cast(dict[str, Any], property).get('enum'), list):
|
|
111
|
+
completions[key] = [str(value) for value in cast(list[Any], values)]
|
|
112
|
+
|
|
113
|
+
self.arguments: list[dict[str, Any]] = [dict(argument) for argument in arguments]
|
|
114
|
+
"""::
|
|
115
|
+
|
|
116
|
+
arguments = prompt.arguments
|
|
117
|
+
|
|
118
|
+
The arguments of the prompt, each with its ``name``, its
|
|
119
|
+
``description`` and whether it is ``required``, the parameters of the
|
|
120
|
+
function by default.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
self.cb = cb
|
|
124
|
+
"""::
|
|
125
|
+
|
|
126
|
+
cb = prompt.cb
|
|
127
|
+
|
|
128
|
+
The function of the prompt.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
self.complete: dict[str, Any] = {**completions, **(complete or {})}
|
|
132
|
+
"""::
|
|
133
|
+
|
|
134
|
+
completions = prompt.complete
|
|
135
|
+
|
|
136
|
+
The completions of the values of the arguments, by their names, which
|
|
137
|
+
``completion/complete`` gives: a list of the values, which the server
|
|
138
|
+
filters by what the user has typed, or a function that gets it, and the
|
|
139
|
+
values of the other arguments, after the context of the request with
|
|
140
|
+
``context``, and returns the values, or a :mod:`Pyjo.Promise` of them. The
|
|
141
|
+
values of a parameter with ``Literal`` or ``Enum`` complete by default. ::
|
|
142
|
+
|
|
143
|
+
prompt.complete['language'] = lambda value, arguments: ['Perl', 'Python']
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
self.context = context
|
|
147
|
+
"""::
|
|
148
|
+
|
|
149
|
+
boolean = prompt.context
|
|
150
|
+
|
|
151
|
+
Whether the function gets the context of the request before its
|
|
152
|
+
arguments.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
self.description = description or ''
|
|
156
|
+
"""::
|
|
157
|
+
|
|
158
|
+
description = prompt.description
|
|
159
|
+
|
|
160
|
+
What the prompt is for, the first paragraph of the docstring of the
|
|
161
|
+
function by default.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
self.name = name or cb.__name__
|
|
165
|
+
"""::
|
|
166
|
+
|
|
167
|
+
name = prompt.name
|
|
168
|
+
|
|
169
|
+
The name of the prompt, the one of the function by default.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
self.title = title
|
|
173
|
+
"""::
|
|
174
|
+
|
|
175
|
+
title = prompt.title
|
|
176
|
+
|
|
177
|
+
The name of the prompt for people, or ``None``.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
self._signature = inspect.signature(cb)
|
|
181
|
+
|
|
182
|
+
def __json__(self) -> dict[str, Any]:
|
|
183
|
+
"""::
|
|
184
|
+
|
|
185
|
+
definition = prompt.__json__()
|
|
186
|
+
|
|
187
|
+
The definition of the prompt, as ``prompts/list`` gives it, with its
|
|
188
|
+
``name``, ``title``, ``description`` and ``arguments``, where it has
|
|
189
|
+
them.
|
|
190
|
+
"""
|
|
191
|
+
definition: dict[str, Any] = {'name': self.name}
|
|
192
|
+
if self.title is not None:
|
|
193
|
+
definition['title'] = self.title
|
|
194
|
+
definition['description'] = self.description
|
|
195
|
+
definition['arguments'] = self.arguments
|
|
196
|
+
return definition
|
|
197
|
+
|
|
198
|
+
def get_p(self, arguments: Mapping[str, Any], context: Any = None) -> Pyjo.Promise.object:
|
|
199
|
+
"""::
|
|
200
|
+
|
|
201
|
+
promise = prompt.get_p({'code': 'x = 1'})
|
|
202
|
+
promise = prompt.get_p({'code': 'x = 1'}, context)
|
|
203
|
+
|
|
204
|
+
Call the function with the arguments, and return a :mod:`Pyjo.Promise`
|
|
205
|
+
of the result of the prompt, as ``prompts/get`` gives it, without its
|
|
206
|
+
``resultType``, or rejected with a :class:`Pyjo.JSON.RPC.Error` of
|
|
207
|
+
arguments that are not valid, or an exception of the function. ::
|
|
208
|
+
|
|
209
|
+
# {'messages': [{'role': 'user', 'content': {'type': 'text', 'text': 'Review this Python code:\\nx = 1'}}]}
|
|
210
|
+
prompt.get_p({'code': 'x = 1'}).then(print).wait()
|
|
211
|
+
"""
|
|
212
|
+
for argument in self.arguments:
|
|
213
|
+
if argument.get('required') and argument['name'] not in arguments:
|
|
214
|
+
message = f'Missing required argument: {argument["name"]}'
|
|
215
|
+
return Pyjo.Promise.reject(Pyjo.JSON.RPC.Error.new(Pyjo.JSON.RPC.INVALID_PARAMS, message))
|
|
216
|
+
try:
|
|
217
|
+
bound = self._signature.bind(*([context] if self.context else []), **arguments)
|
|
218
|
+
except TypeError as e:
|
|
219
|
+
message = f'Invalid arguments: {e}'
|
|
220
|
+
return Pyjo.Promise.reject(Pyjo.JSON.RPC.Error.new(Pyjo.JSON.RPC.INVALID_PARAMS, message))
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
value = self.cb(*bound.args, **bound.kwargs)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
return Pyjo.Promise.reject(e)
|
|
226
|
+
if inspect.iscoroutine(value):
|
|
227
|
+
value = Pyjo.Promise.new(value)
|
|
228
|
+
if isinstance(value, Pyjo.Promise.object):
|
|
229
|
+
return value.then(self.result)
|
|
230
|
+
return Pyjo.Promise.resolve(self.result(value))
|
|
231
|
+
|
|
232
|
+
def result(self, value: Any = None) -> dict[str, Any]:
|
|
233
|
+
"""::
|
|
234
|
+
|
|
235
|
+
result = prompt.result('Review this code')
|
|
236
|
+
result = prompt.result([{'role': 'assistant', 'content': {'type': 'text', 'text': 'Done'}}])
|
|
237
|
+
|
|
238
|
+
The result of the prompt of what the function returns.
|
|
239
|
+
"""
|
|
240
|
+
if isinstance(value, dict) and 'messages' in value:
|
|
241
|
+
return cast(dict[str, Any], value)
|
|
242
|
+
if isinstance(value, list):
|
|
243
|
+
return {'messages': cast(list[Any], value)}
|
|
244
|
+
return text_result('' if value is None else str(cast(Any, value)))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def text_result(text: str, role: str = 'user') -> dict[str, Any]:
|
|
248
|
+
"""::
|
|
249
|
+
|
|
250
|
+
result = Pyjo.MCP.Prompt.text_result('Review this code')
|
|
251
|
+
result = Pyjo.MCP.Prompt.text_result('Here is the review', role='assistant')
|
|
252
|
+
|
|
253
|
+
A result of a prompt with one message of the text, of the user by default.
|
|
254
|
+
"""
|
|
255
|
+
return {'messages': [{'role': role, 'content': {'type': 'text', 'text': text}}]}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
new = Pyjo_MCP_Prompt.new
|
|
259
|
+
object = Pyjo_MCP_Prompt
|
|
260
|
+
Prompt = Pyjo_MCP_Prompt
|
Pyjo/MCP/Resource.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjo.MCP.Resource - Resource of a server of MCP
|
|
3
|
+
===============================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
from Pyjo.MCP.Resource import Resource
|
|
7
|
+
|
|
8
|
+
def readme() -> str:
|
|
9
|
+
\"\"\"The documentation of the project.\"\"\"
|
|
10
|
+
return '# Pyjo-MCP'
|
|
11
|
+
|
|
12
|
+
resource = Resource('file:///project/README.md', readme, mime_type='text/markdown')
|
|
13
|
+
print(resource.uri, resource.name, resource.description)
|
|
14
|
+
|
|
15
|
+
# Or import the module, as in Google Python Style Guide
|
|
16
|
+
|
|
17
|
+
import Pyjo.MCP.Resource
|
|
18
|
+
|
|
19
|
+
resource = Pyjo.MCP.Resource.new('file:///project/logo.png', lambda: b'\\x89PNG', mime_type='image/png')
|
|
20
|
+
resource.read_p().then(print).wait()
|
|
21
|
+
|
|
22
|
+
:mod:`Pyjo.MCP.Resource` is a resource of :mod:`Pyjo.MCP.Server`, data that a
|
|
23
|
+
client reads by its URI, as the context of a language model. The name and the
|
|
24
|
+
description of the resource are those of the function by default: its name
|
|
25
|
+
and the first paragraph of its docstring.
|
|
26
|
+
|
|
27
|
+
The function returns the contents of the resource, as a value, a
|
|
28
|
+
:mod:`Pyjo.Promise` of it or a coroutine, of an ``async def``:
|
|
29
|
+
|
|
30
|
+
* a string is the ``text`` of the contents;
|
|
31
|
+
* :class:`bytes` are the ``blob`` of the contents, in Base64;
|
|
32
|
+
* a :class:`dict` with ``contents`` is the result as it is, as with several
|
|
33
|
+
of them;
|
|
34
|
+
* another value is the text of its JSON.
|
|
35
|
+
|
|
36
|
+
See also
|
|
37
|
+
--------
|
|
38
|
+
|
|
39
|
+
:mod:`Pyjo.MCP.Server`, :mod:`Pyjo.MCP.Prompt`, :mod:`Pyjo.MCP.Tool`,
|
|
40
|
+
:mod:`Pyjo.MCP`.
|
|
41
|
+
|
|
42
|
+
Classes
|
|
43
|
+
-------
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
import Pyjo.Base
|
|
47
|
+
import Pyjo.JSON
|
|
48
|
+
import Pyjo.Promise
|
|
49
|
+
|
|
50
|
+
import base64
|
|
51
|
+
import inspect
|
|
52
|
+
|
|
53
|
+
from collections.abc import Callable
|
|
54
|
+
from typing import Any, cast
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Pyjo_MCP_Resource(Pyjo.Base.object):
|
|
58
|
+
"""
|
|
59
|
+
:mod:`Pyjo.MCP.Resource` inherits all methods from :mod:`Pyjo.Base` and
|
|
60
|
+
implements the following new ones.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
uri: str,
|
|
66
|
+
cb: Callable[..., Any],
|
|
67
|
+
*,
|
|
68
|
+
name: str | None = None,
|
|
69
|
+
title: str | None = None,
|
|
70
|
+
description: str | None = None,
|
|
71
|
+
mime_type: str = 'text/plain',
|
|
72
|
+
context: bool = False,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""::
|
|
75
|
+
|
|
76
|
+
resource = Pyjo.MCP.Resource.new('file:///project/README.md', readme)
|
|
77
|
+
resource = Pyjo.MCP.Resource.new('file:///project/README.md', readme, mime_type='text/markdown')
|
|
78
|
+
resource = Pyjo.MCP.Resource.new('config://app', lambda context: ..., context=True)
|
|
79
|
+
|
|
80
|
+
Construct a new :mod:`Pyjo.MCP.Resource` object of a function. With
|
|
81
|
+
``context``, the function gets the :class:`~Pyjo.MCP.Server.Context` of
|
|
82
|
+
the request.
|
|
83
|
+
"""
|
|
84
|
+
if description is None:
|
|
85
|
+
doc = inspect.getdoc(cb) or ''
|
|
86
|
+
description = doc.split('\n\n', 1)[0].replace('\n', ' ').strip() or None
|
|
87
|
+
|
|
88
|
+
self.cb = cb
|
|
89
|
+
"""::
|
|
90
|
+
|
|
91
|
+
cb = resource.cb
|
|
92
|
+
|
|
93
|
+
The function of the resource.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
self.context = context
|
|
97
|
+
"""::
|
|
98
|
+
|
|
99
|
+
boolean = resource.context
|
|
100
|
+
|
|
101
|
+
Whether the function gets the context of the request.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
self.description = description
|
|
105
|
+
"""::
|
|
106
|
+
|
|
107
|
+
description = resource.description
|
|
108
|
+
|
|
109
|
+
What the resource is, the first paragraph of the docstring of the
|
|
110
|
+
function by default, or ``None``.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
self.mime_type = mime_type
|
|
114
|
+
"""::
|
|
115
|
+
|
|
116
|
+
type = resource.mime_type
|
|
117
|
+
|
|
118
|
+
The media type of the contents, ``text/plain`` by default.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
self.name = name or cb.__name__
|
|
122
|
+
"""::
|
|
123
|
+
|
|
124
|
+
name = resource.name
|
|
125
|
+
|
|
126
|
+
The name of the resource, the one of the function by default.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
self.title = title
|
|
130
|
+
"""::
|
|
131
|
+
|
|
132
|
+
title = resource.title
|
|
133
|
+
|
|
134
|
+
The name of the resource for people, or ``None``.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
self.uri = uri
|
|
138
|
+
"""::
|
|
139
|
+
|
|
140
|
+
uri = resource.uri
|
|
141
|
+
|
|
142
|
+
The URI of the resource, which ``resources/read`` asks for.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
def __json__(self) -> dict[str, Any]:
|
|
146
|
+
"""::
|
|
147
|
+
|
|
148
|
+
definition = resource.__json__()
|
|
149
|
+
|
|
150
|
+
The definition of the resource, as ``resources/list`` gives it, with
|
|
151
|
+
its ``uri``, ``name``, ``title``, ``description`` and ``mimeType``,
|
|
152
|
+
where it has them.
|
|
153
|
+
"""
|
|
154
|
+
definition: dict[str, Any] = {'uri': self.uri, 'name': self.name}
|
|
155
|
+
if self.title is not None:
|
|
156
|
+
definition['title'] = self.title
|
|
157
|
+
if self.description is not None:
|
|
158
|
+
definition['description'] = self.description
|
|
159
|
+
definition['mimeType'] = self.mime_type
|
|
160
|
+
return definition
|
|
161
|
+
|
|
162
|
+
def read_p(self, context: Any = None) -> Pyjo.Promise.object:
|
|
163
|
+
"""::
|
|
164
|
+
|
|
165
|
+
promise = resource.read_p()
|
|
166
|
+
promise = resource.read_p(context)
|
|
167
|
+
|
|
168
|
+
Call the function, and return a :mod:`Pyjo.Promise` of the result of
|
|
169
|
+
the resource, as ``resources/read`` gives it, without its
|
|
170
|
+
``resultType``, or rejected with an exception of the function. ::
|
|
171
|
+
|
|
172
|
+
# {'contents': [{'uri': 'file:///project/README.md', 'mimeType': 'text/markdown', 'text': '# Pyjo-MCP'}]}
|
|
173
|
+
resource.read_p().then(print).wait()
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
value = self.cb(*([context] if self.context else []))
|
|
177
|
+
except Exception as e:
|
|
178
|
+
return Pyjo.Promise.reject(e)
|
|
179
|
+
if inspect.iscoroutine(value):
|
|
180
|
+
value = Pyjo.Promise.new(value)
|
|
181
|
+
if isinstance(value, Pyjo.Promise.object):
|
|
182
|
+
return value.then(self.result)
|
|
183
|
+
return Pyjo.Promise.resolve(self.result(value))
|
|
184
|
+
|
|
185
|
+
def result(self, value: Any = None) -> dict[str, Any]:
|
|
186
|
+
"""::
|
|
187
|
+
|
|
188
|
+
result = resource.result('# Pyjo-MCP')
|
|
189
|
+
result = resource.result(b'\\x89PNG')
|
|
190
|
+
|
|
191
|
+
The result of the resource of what the function returns.
|
|
192
|
+
"""
|
|
193
|
+
if isinstance(value, dict) and 'contents' in value:
|
|
194
|
+
return cast(dict[str, Any], value)
|
|
195
|
+
contents: dict[str, Any] = {'uri': self.uri, 'mimeType': self.mime_type}
|
|
196
|
+
if isinstance(value, bytes | bytearray):
|
|
197
|
+
contents['blob'] = base64.b64encode(value).decode()
|
|
198
|
+
else:
|
|
199
|
+
contents['text'] = value if isinstance(value, str) else Pyjo.JSON.to_json(cast(Any, value))
|
|
200
|
+
return {'contents': [contents]}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
new = Pyjo_MCP_Resource.new
|
|
204
|
+
object = Pyjo_MCP_Resource
|
|
205
|
+
Resource = Pyjo_MCP_Resource
|