aiohttp-tiny-mcp 0.3.0__tar.gz → 0.3.2__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.
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/LICENSE +47 -17
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/PKG-INFO +7 -2
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/README.md +4 -1
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/pyproject.toml +13 -2
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/pyproject.toml.orig +13 -2
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/__init__.py +2 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/adapter.py +40 -9
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/client_base.py +16 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/console/console.css +12 -5
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/console/console.js +172 -25
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/core.py +1 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/dispatcher.py +40 -5
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/endpoint.py +1 -1
- aiohttp_tiny_mcp-0.3.2/src/aiohttp_tiny_mcp/extensions.py +143 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/models.py +5 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/v2026_07_28.py +12 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/registry.py +53 -3
- aiohttp_tiny_mcp-0.3.2/src/aiohttp_tiny_mcp/skills.py +223 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/specs.py +4 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/stdio.py +1 -1
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/auth.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/client.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/console/__init__.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/console/index.html +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/exchange.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/http_sse.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/hub.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/namespaces.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/postgres.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/__init__.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/selection.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/v2024_11_05.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/v2025_03_26.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/v2025_06_18.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/protocol/v2025_11_25.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/py.typed +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/redis.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/request_state.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/schema.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/sessions.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/sqlite.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/sse.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/stdio_client.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/subscriptions.py +0 -0
- {aiohttp_tiny_mcp-0.3.0 → aiohttp_tiny_mcp-0.3.2}/src/aiohttp_tiny_mcp/testing.py +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -86,21 +87,22 @@
|
|
|
86
87
|
granted to You under this License for that Work shall terminate
|
|
87
88
|
as of the date such litigation is filed.
|
|
88
89
|
|
|
89
|
-
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
-
or Derivative Works thereof in any medium, with or without
|
|
91
|
-
modifications, and in Source or Object form, provided that You
|
|
92
|
-
the following conditions:
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
93
94
|
|
|
94
|
-
(a) You must give any other recipients of the Work or
|
|
95
|
-
a copy of this License; and
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
96
97
|
|
|
97
98
|
(b) You must cause any modified files to carry prominent notices
|
|
98
99
|
stating that You changed the files; and
|
|
99
100
|
|
|
100
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
-
You distribute, all copyright, patent, trademark, and
|
|
102
|
-
notices from the Source form of the Work,
|
|
103
|
-
that do not pertain to any part of
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
104
106
|
|
|
105
107
|
(d) If the Work includes a "NOTICE" text file as part of its
|
|
106
108
|
distribution, then any Derivative Works that You distribute must
|
|
@@ -130,11 +132,14 @@
|
|
|
130
132
|
any Contribution intentionally submitted for inclusion in the Work
|
|
131
133
|
by You to the Licensor shall be under the terms and conditions of
|
|
132
134
|
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
133
138
|
|
|
134
139
|
6. Trademarks. This License does not grant permission to use the trade
|
|
135
140
|
names, trademarks, service marks, or product names of the Licensor,
|
|
136
|
-
except as required for reasonable and customary use in describing
|
|
137
|
-
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
138
143
|
|
|
139
144
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
140
145
|
agreed to in writing, Licensor provides the Work (and each
|
|
@@ -152,11 +157,11 @@
|
|
|
152
157
|
negligent acts) or agreed to in writing, shall any Contributor be
|
|
153
158
|
liable to You for damages, including any direct, indirect, special,
|
|
154
159
|
incidental, or consequential damages of any character arising as a
|
|
155
|
-
result of this License or out of the use or inability to use the
|
|
156
|
-
(including but not limited to damages for loss of goodwill,
|
|
157
|
-
stoppage, computer failure or malfunction, or any and all
|
|
158
|
-
commercial damages or losses), even if such Contributor
|
|
159
|
-
advised of the possibility of such damages.
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
160
165
|
|
|
161
166
|
9. Accepting Warranty or Additional Liability. While redistributing
|
|
162
167
|
the Work or Derivative Works thereof, You may choose to offer,
|
|
@@ -170,3 +175,28 @@
|
|
|
170
175
|
of your accepting any such warranty or additional liability.
|
|
171
176
|
|
|
172
177
|
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiohttp-tiny-mcp
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Remote MCP server and client library for aiohttp
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,6 +28,7 @@ Requires-Dist: aiohttp>=3.14
|
|
|
28
28
|
Requires-Dist: pydantic>=2.13
|
|
29
29
|
Requires-Dist: psycopg[binary,pool]>=3 ; extra == 'postgres'
|
|
30
30
|
Requires-Dist: redis>=5 ; extra == 'redis'
|
|
31
|
+
Requires-Dist: pyyaml>=6 ; extra == 'skills'
|
|
31
32
|
Requires-Dist: aiosqlite>=0.20 ; extra == 'sqlite'
|
|
32
33
|
Requires-Python: >=3.10
|
|
33
34
|
Project-URL: Documentation, https://mosquito.github.io/aiohttp-tiny-mcp/
|
|
@@ -35,12 +36,14 @@ Project-URL: Repository, https://github.com/mosquito/aiohttp-tiny-mcp
|
|
|
35
36
|
Project-URL: Issues, https://github.com/mosquito/aiohttp-tiny-mcp/issues
|
|
36
37
|
Provides-Extra: postgres
|
|
37
38
|
Provides-Extra: redis
|
|
39
|
+
Provides-Extra: skills
|
|
38
40
|
Provides-Extra: sqlite
|
|
39
41
|
Description-Content-Type: text/markdown
|
|
40
42
|
|
|
41
43
|
# aiohttp-tiny-mcp
|
|
42
44
|
|
|
43
45
|
[](https://github.com/mosquito/aiohttp-tiny-mcp/actions/workflows/tests.yml)
|
|
46
|
+
[](https://coveralls.io/github/mosquito/aiohttp-tiny-mcp?branch=master)
|
|
44
47
|
[](https://github.com/mosquito/aiohttp-tiny-mcp/releases)
|
|
45
48
|
[](https://github.com/mosquito/aiohttp-tiny-mcp/blob/master/LICENSE)
|
|
46
49
|
[](https://mosquito.github.io/aiohttp-tiny-mcp/)
|
|
@@ -230,6 +233,7 @@ Start with [the documentation overview](https://mosquito.github.io/aiohttp-tiny-
|
|
|
230
233
|
4. [Using Exchange](https://mosquito.github.io/aiohttp-tiny-mcp/guide/exchange.html): request context, progress, questions, and state.
|
|
231
234
|
5. [Authentication](https://mosquito.github.io/aiohttp-tiny-mcp/guide/auth.html): bearer-token verification and OAuth resource metadata.
|
|
232
235
|
6. [Stores and hubs](https://mosquito.github.io/aiohttp-tiny-mcp/deployment/stores.html): shared backend contracts and deployment requirements.
|
|
236
|
+
7. [Extensions and skills](https://mosquito.github.io/aiohttp-tiny-mcp/guide/extensions.html): custom methods, skill directories, and resources for older clients.
|
|
233
237
|
|
|
234
238
|
The server supports `2026-07-28`, `2025-11-25`, `2025-06-18`, `2025-03-26`
|
|
235
239
|
and `2024-11-05`.
|
|
@@ -240,6 +244,7 @@ Delivery mechanisms and client support differ; see the
|
|
|
240
244
|
Documentation examples are checked by the test suite. From a source checkout:
|
|
241
245
|
|
|
242
246
|
```bash
|
|
247
|
+
uv sync --all-groups --all-extras
|
|
243
248
|
uv run pytest docs README.md
|
|
244
|
-
uv run
|
|
249
|
+
uv run sphinx-build -W -b html docs docs/_build
|
|
245
250
|
```
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# aiohttp-tiny-mcp
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mosquito/aiohttp-tiny-mcp/actions/workflows/tests.yml)
|
|
4
|
+
[](https://coveralls.io/github/mosquito/aiohttp-tiny-mcp?branch=master)
|
|
4
5
|
[](https://github.com/mosquito/aiohttp-tiny-mcp/releases)
|
|
5
6
|
[](https://github.com/mosquito/aiohttp-tiny-mcp/blob/master/LICENSE)
|
|
6
7
|
[](https://mosquito.github.io/aiohttp-tiny-mcp/)
|
|
@@ -190,6 +191,7 @@ Start with [the documentation overview](https://mosquito.github.io/aiohttp-tiny-
|
|
|
190
191
|
4. [Using Exchange](https://mosquito.github.io/aiohttp-tiny-mcp/guide/exchange.html): request context, progress, questions, and state.
|
|
191
192
|
5. [Authentication](https://mosquito.github.io/aiohttp-tiny-mcp/guide/auth.html): bearer-token verification and OAuth resource metadata.
|
|
192
193
|
6. [Stores and hubs](https://mosquito.github.io/aiohttp-tiny-mcp/deployment/stores.html): shared backend contracts and deployment requirements.
|
|
194
|
+
7. [Extensions and skills](https://mosquito.github.io/aiohttp-tiny-mcp/guide/extensions.html): custom methods, skill directories, and resources for older clients.
|
|
193
195
|
|
|
194
196
|
The server supports `2026-07-28`, `2025-11-25`, `2025-06-18`, `2025-03-26`
|
|
195
197
|
and `2024-11-05`.
|
|
@@ -200,6 +202,7 @@ Delivery mechanisms and client support differ; see the
|
|
|
200
202
|
Documentation examples are checked by the test suite. From a source checkout:
|
|
201
203
|
|
|
202
204
|
```bash
|
|
205
|
+
uv sync --all-groups --all-extras
|
|
203
206
|
uv run pytest docs README.md
|
|
204
|
-
uv run
|
|
207
|
+
uv run sphinx-build -W -b html docs docs/_build
|
|
205
208
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "aiohttp-tiny-mcp"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Remote MCP server and client library for aiohttp"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -39,12 +39,14 @@ Repository = "https://github.com/mosquito/aiohttp-tiny-mcp"
|
|
|
39
39
|
Issues = "https://github.com/mosquito/aiohttp-tiny-mcp/issues"
|
|
40
40
|
|
|
41
41
|
[project.optional-dependencies]
|
|
42
|
+
skills = ["PyYAML>=6"]
|
|
42
43
|
sqlite = ["aiosqlite>=0.20"]
|
|
43
44
|
redis = ["redis>=5"]
|
|
44
45
|
postgres = ["psycopg[binary,pool]>=3"]
|
|
45
46
|
|
|
46
47
|
[dependency-groups]
|
|
47
48
|
dev = [
|
|
49
|
+
"PyYAML>=6",
|
|
48
50
|
"PyJWT>=2.13",
|
|
49
51
|
"aiosqlite>=0.20",
|
|
50
52
|
"furo>=2025.12.19",
|
|
@@ -54,6 +56,7 @@ dev = [
|
|
|
54
56
|
"psycopg[binary,pool]>=3",
|
|
55
57
|
"pytest",
|
|
56
58
|
"pytest-asyncio",
|
|
59
|
+
"pytest-cov>=7.1.0",
|
|
57
60
|
"pytest-timeout",
|
|
58
61
|
"redis>=5",
|
|
59
62
|
"ruff",
|
|
@@ -69,6 +72,14 @@ source-include = [
|
|
|
69
72
|
"src/aiohttp_tiny_mcp/console/*.js",
|
|
70
73
|
]
|
|
71
74
|
|
|
75
|
+
[tool.coverage.run]
|
|
76
|
+
source = ["aiohttp_tiny_mcp"]
|
|
77
|
+
branch = true
|
|
78
|
+
|
|
79
|
+
[tool.coverage.report]
|
|
80
|
+
skip_covered = true
|
|
81
|
+
show_missing = true
|
|
82
|
+
|
|
72
83
|
[tool.pytest.ini_options]
|
|
73
84
|
asyncio_mode = "auto"
|
|
74
85
|
timeout = 30
|
|
@@ -102,5 +113,5 @@ root = [
|
|
|
102
113
|
exclude = ["examples/docker-mcp"]
|
|
103
114
|
|
|
104
115
|
[build-system]
|
|
105
|
-
requires = ["uv_build>=0.
|
|
116
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
106
117
|
build-backend = "uv_build"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "aiohttp-tiny-mcp"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Remote MCP server and client library for aiohttp"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -39,6 +39,7 @@ Repository = "https://github.com/mosquito/aiohttp-tiny-mcp"
|
|
|
39
39
|
Issues = "https://github.com/mosquito/aiohttp-tiny-mcp/issues"
|
|
40
40
|
|
|
41
41
|
[project.optional-dependencies]
|
|
42
|
+
skills = ["PyYAML>=6"]
|
|
42
43
|
# A hub and a session store on one SQLite file: aiohttp_tiny_mcp.sqlite.
|
|
43
44
|
sqlite = ["aiosqlite>=0.20"]
|
|
44
45
|
redis = [
|
|
@@ -50,6 +51,7 @@ postgres = [
|
|
|
50
51
|
|
|
51
52
|
[dependency-groups]
|
|
52
53
|
dev = [
|
|
54
|
+
"PyYAML>=6",
|
|
53
55
|
"PyJWT>=2.13",
|
|
54
56
|
"aiosqlite>=0.20",
|
|
55
57
|
"furo>=2025.12.19",
|
|
@@ -59,6 +61,7 @@ dev = [
|
|
|
59
61
|
"psycopg[binary,pool]>=3",
|
|
60
62
|
"pytest",
|
|
61
63
|
"pytest-asyncio",
|
|
64
|
+
"pytest-cov>=7.1.0",
|
|
62
65
|
"pytest-timeout",
|
|
63
66
|
"redis>=5",
|
|
64
67
|
"ruff",
|
|
@@ -72,9 +75,17 @@ dev = [
|
|
|
72
75
|
source-include = ["src/aiohttp_tiny_mcp/console/*.html", "src/aiohttp_tiny_mcp/console/*.css", "src/aiohttp_tiny_mcp/console/*.js"]
|
|
73
76
|
|
|
74
77
|
[build-system]
|
|
75
|
-
requires = ["uv_build>=0.
|
|
78
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
76
79
|
build-backend = "uv_build"
|
|
77
80
|
|
|
81
|
+
[tool.coverage.run]
|
|
82
|
+
source = ["aiohttp_tiny_mcp"]
|
|
83
|
+
branch = true
|
|
84
|
+
|
|
85
|
+
[tool.coverage.report]
|
|
86
|
+
skip_covered = true
|
|
87
|
+
show_missing = true
|
|
88
|
+
|
|
78
89
|
[tool.pytest.ini_options]
|
|
79
90
|
asyncio_mode = "auto"
|
|
80
91
|
# The guard against a hung test, not a measure of how fast the machine is. A
|
|
@@ -15,6 +15,7 @@ from .core import (
|
|
|
15
15
|
)
|
|
16
16
|
from .endpoint import Endpoint
|
|
17
17
|
from .exchange import Exchange
|
|
18
|
+
from .extensions import Extension
|
|
18
19
|
from .http_sse import SseEndpoint
|
|
19
20
|
from .hub import Hub, MemoryHub
|
|
20
21
|
from .models import (
|
|
@@ -55,6 +56,7 @@ __all__ = [
|
|
|
55
56
|
"Endpoint",
|
|
56
57
|
"SSEResponse",
|
|
57
58
|
"Exchange",
|
|
59
|
+
"Extension",
|
|
58
60
|
"GetPromptResult",
|
|
59
61
|
"Hint",
|
|
60
62
|
"Hub",
|
|
@@ -26,6 +26,7 @@ from .core import (
|
|
|
26
26
|
answer_actions,
|
|
27
27
|
decode_failure_target,
|
|
28
28
|
)
|
|
29
|
+
from .extensions import ExtensionSpec
|
|
29
30
|
from .hub import Hub
|
|
30
31
|
from .models import (
|
|
31
32
|
CallToolParams,
|
|
@@ -72,6 +73,8 @@ class RegistryProtocol(Protocol):
|
|
|
72
73
|
def prompts(self) -> Mapping[str, PromptSpec]: ...
|
|
73
74
|
@property
|
|
74
75
|
def completer(self) -> Bound | None: ...
|
|
76
|
+
@property
|
|
77
|
+
def extensions(self) -> Mapping[str, ExtensionSpec]: ...
|
|
75
78
|
def match_resource(self, uri: str) -> tuple[ResourceSpec, dict[str, str]] | None: ...
|
|
76
79
|
|
|
77
80
|
|
|
@@ -113,7 +116,9 @@ class Adapter(ABC):
|
|
|
113
116
|
"""Raise `Rejected` on a revision-specific HTTP binding violation."""
|
|
114
117
|
return None # noqa: B027 -- concrete default, most revisions check nothing
|
|
115
118
|
|
|
116
|
-
def decode(
|
|
119
|
+
def decode(
|
|
120
|
+
self, pre: Preamble, registry: RegistryProtocol | None = None
|
|
121
|
+
) -> Sequence[Call | DecodeFailure]:
|
|
117
122
|
"""Decode messages independently; one invalid item does not abort the batch."""
|
|
118
123
|
if pre.parse_error:
|
|
119
124
|
raise Rejected(Failure(FailureKind.PARSE, "invalid JSON body"))
|
|
@@ -122,10 +127,12 @@ class Adapter(ABC):
|
|
|
122
127
|
raise Rejected(Failure(FailureKind.MALFORMED, "batching not supported"))
|
|
123
128
|
if not pre.body: # `is_batch` already means the body is a list
|
|
124
129
|
raise Rejected(Failure(FailureKind.MALFORMED, "empty batch"))
|
|
125
|
-
return [self.decode_item(item) for item in pre.body]
|
|
126
|
-
return [self.decode_item(pre.body)]
|
|
130
|
+
return [self.decode_item(item, registry) for item in pre.body]
|
|
131
|
+
return [self.decode_item(pre.body, registry)]
|
|
127
132
|
|
|
128
|
-
def decode_item(
|
|
133
|
+
def decode_item(
|
|
134
|
+
self, item: object, registry: RegistryProtocol | None = None
|
|
135
|
+
) -> Call | DecodeFailure:
|
|
129
136
|
"""Preserve the request id on failure without aborting sibling batch items."""
|
|
130
137
|
if not isinstance(item, dict):
|
|
131
138
|
return DecodeFailure(
|
|
@@ -135,12 +142,12 @@ class Adapter(ABC):
|
|
|
135
142
|
)
|
|
136
143
|
body = cast(dict[str, Any], item)
|
|
137
144
|
try:
|
|
138
|
-
return self.decode_one(body)
|
|
145
|
+
return self.decode_one(body, registry)
|
|
139
146
|
except Rejected as e:
|
|
140
147
|
call_id, must_respond = decode_failure_target(body)
|
|
141
148
|
return DecodeFailure(id=call_id, failure=e.failure, must_respond=must_respond)
|
|
142
149
|
|
|
143
|
-
def decode_one(self, body: dict[str, Any]) -> Call:
|
|
150
|
+
def decode_one(self, body: dict[str, Any], registry: RegistryProtocol | None = None) -> Call:
|
|
144
151
|
if "id" in body and body["id"] is None:
|
|
145
152
|
raise Rejected(Failure(FailureKind.MALFORMED, "id must not be null"))
|
|
146
153
|
try:
|
|
@@ -148,6 +155,12 @@ class Adapter(ABC):
|
|
|
148
155
|
except ValidationError as e:
|
|
149
156
|
raise Rejected(Failure(FailureKind.MALFORMED, str(e))) from None
|
|
150
157
|
operation = self.operation_for(msg.method)
|
|
158
|
+
if operation is None and registry is not None and self.supports_extensions:
|
|
159
|
+
if any(
|
|
160
|
+
msg.method in spec.methods and spec.min_revision <= self.version
|
|
161
|
+
for spec in registry.extensions.values()
|
|
162
|
+
):
|
|
163
|
+
operation = Operation.EXTENSION
|
|
151
164
|
if operation is None:
|
|
152
165
|
raise Rejected(Failure(FailureKind.UNKNOWN_METHOD, f"unknown method: {msg.method}"))
|
|
153
166
|
self.check_message(operation, msg)
|
|
@@ -156,7 +169,17 @@ class Adapter(ABC):
|
|
|
156
169
|
except ValidationError as e:
|
|
157
170
|
raise Rejected(Failure(FailureKind.INVALID_PARAMS, str(e))) from None
|
|
158
171
|
self.check_params(params)
|
|
159
|
-
|
|
172
|
+
call = self.build_call(operation, msg, params)
|
|
173
|
+
if operation is Operation.EXTENSION:
|
|
174
|
+
if msg.is_notification:
|
|
175
|
+
raise Rejected(Failure(FailureKind.MALFORMED, "extension requests require an id"))
|
|
176
|
+
call.target = msg.method
|
|
177
|
+
call.arguments = {
|
|
178
|
+
key: value
|
|
179
|
+
for key, value in msg.params.items()
|
|
180
|
+
if key not in {"_meta", "inputResponses", "requestState"}
|
|
181
|
+
}
|
|
182
|
+
return call
|
|
160
183
|
|
|
161
184
|
def build_call(self, operation: Operation, msg: Incoming, params: Params) -> Call:
|
|
162
185
|
target: str | None = None
|
|
@@ -283,7 +306,10 @@ class Adapter(ABC):
|
|
|
283
306
|
caps: dict[str, Any] = {}
|
|
284
307
|
if registry.tools:
|
|
285
308
|
caps["tools"] = {"listChanged": True}
|
|
286
|
-
if
|
|
309
|
+
if any(
|
|
310
|
+
self.describe_resource(spec) is not None
|
|
311
|
+
for spec in (*registry.resources_fixed.values(), *registry.resources_templated)
|
|
312
|
+
):
|
|
287
313
|
caps["resources"] = {"listChanged": True, "subscribe": True}
|
|
288
314
|
if registry.prompts:
|
|
289
315
|
caps["prompts"] = {"listChanged": True}
|
|
@@ -300,7 +326,11 @@ class Adapter(ABC):
|
|
|
300
326
|
def describe_tool(self, spec: ToolSpec) -> ToolDef | None: ...
|
|
301
327
|
|
|
302
328
|
def describe_resource(self, spec: ResourceSpec) -> ResourceDef | ResourceTemplateDef | None:
|
|
303
|
-
"""
|
|
329
|
+
"""Project extension resources onto the URI understood by this revision."""
|
|
330
|
+
if spec.legacy_only and self.supports_extensions:
|
|
331
|
+
return None
|
|
332
|
+
if spec.definition is not None and spec.legacy_uri and not self.supports_extensions:
|
|
333
|
+
return spec.definition.model_copy(update={"uri": spec.legacy_uri})
|
|
304
334
|
return spec.definition if spec.definition is not None else spec.template
|
|
305
335
|
|
|
306
336
|
def describe_prompt(self, spec: PromptSpec) -> PromptDef | None:
|
|
@@ -319,3 +349,4 @@ class Adapter(ABC):
|
|
|
319
349
|
allows_batch: ClassVar[bool] = False
|
|
320
350
|
#: Negotiate once via initialize; retain the revision and capabilities in a session.
|
|
321
351
|
has_handshake: ClassVar[bool] = False
|
|
352
|
+
supports_extensions: ClassVar[bool] = False
|
|
@@ -144,6 +144,22 @@ class BaseClient(ABC):
|
|
|
144
144
|
method = self.adapter.method_for(operation)
|
|
145
145
|
if method is None:
|
|
146
146
|
raise ValueError(f"{self.adapter.version} does not expose {operation}")
|
|
147
|
+
return await self.request_method(method, params, name=name)
|
|
148
|
+
|
|
149
|
+
async def request_method(
|
|
150
|
+
self,
|
|
151
|
+
method: str,
|
|
152
|
+
params: Params | Mapping[str, Any] | None = None,
|
|
153
|
+
*,
|
|
154
|
+
name: str | None = None,
|
|
155
|
+
) -> dict[str, Any]:
|
|
156
|
+
"""Call a named method, including extensions, with protocol metadata and headers.
|
|
157
|
+
|
|
158
|
+
Check server discovery for the extension before using its methods.
|
|
159
|
+
Results are returned as wire dictionaries; RPC errors raise ClientError.
|
|
160
|
+
"""
|
|
161
|
+
if not isinstance(params, Params):
|
|
162
|
+
params = Params.model_validate(dict(params or {}))
|
|
147
163
|
params = self.adapter.client_decorate_params(params, self.profile)
|
|
148
164
|
envelope = {
|
|
149
165
|
"jsonrpc": "2.0",
|
|
@@ -151,7 +151,7 @@ button.quiet { color: var(--ink-soft); }
|
|
|
151
151
|
@media (max-width: 900px) {
|
|
152
152
|
/* Avoid auto rows distributing spare height to collapsed panels. */
|
|
153
153
|
.layout {
|
|
154
|
-
grid-template-columns: 1fr;
|
|
154
|
+
grid-template-columns: minmax(0, 1fr);
|
|
155
155
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
156
156
|
}
|
|
157
157
|
.layout > .panel { min-height: 0; }
|
|
@@ -164,6 +164,8 @@ button.quiet { color: var(--ink-soft); }
|
|
|
164
164
|
display: flex;
|
|
165
165
|
flex-direction: column;
|
|
166
166
|
min-height: 0;
|
|
167
|
+
min-width: 0;
|
|
168
|
+
overflow-wrap: anywhere;
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
.panel-head {
|
|
@@ -183,12 +185,14 @@ button.quiet { color: var(--ink-soft); }
|
|
|
183
185
|
color: var(--ink-soft);
|
|
184
186
|
margin: 0;
|
|
185
187
|
flex: 1;
|
|
188
|
+
min-width: 0;
|
|
186
189
|
overflow: hidden;
|
|
187
190
|
text-overflow: ellipsis;
|
|
188
191
|
white-space: nowrap;
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
.work .panel-head h2 { text-transform: none; letter-spacing: 0; font-size: 14px; color: var(--ink); font-family: var(--mono); }
|
|
195
|
+
.panel-head > button { flex-shrink: 0; }
|
|
192
196
|
|
|
193
197
|
.scroll { flex: 1; overflow: auto; padding: 12px 14px; min-height: 0; }
|
|
194
198
|
|
|
@@ -246,6 +250,8 @@ button.quiet { color: var(--ink-soft); }
|
|
|
246
250
|
.item {
|
|
247
251
|
display: block;
|
|
248
252
|
width: 100%;
|
|
253
|
+
min-width: 0;
|
|
254
|
+
overflow-wrap: anywhere;
|
|
249
255
|
text-align: left;
|
|
250
256
|
border: 1px solid transparent;
|
|
251
257
|
background: transparent;
|
|
@@ -301,7 +307,7 @@ button.quiet { color: var(--ink-soft); }
|
|
|
301
307
|
border: 1px solid var(--line);
|
|
302
308
|
border-radius: 4px;
|
|
303
309
|
padding: 1px 4px;
|
|
304
|
-
white-space:
|
|
310
|
+
white-space: normal;
|
|
305
311
|
}
|
|
306
312
|
|
|
307
313
|
.about pre {
|
|
@@ -371,10 +377,10 @@ button.quiet { color: var(--ink-soft); }
|
|
|
371
377
|
button.tiny { font-size: 11px; padding: 1px 7px; }
|
|
372
378
|
|
|
373
379
|
|
|
374
|
-
/*
|
|
380
|
+
/* Cap long keys so values and copy controls keep usable space. */
|
|
375
381
|
.fields {
|
|
376
382
|
display: grid;
|
|
377
|
-
grid-template-columns:
|
|
383
|
+
grid-template-columns: fit-content(35%) minmax(0, 1fr);
|
|
378
384
|
gap: 3px 14px;
|
|
379
385
|
align-items: baseline;
|
|
380
386
|
}
|
|
@@ -385,7 +391,8 @@ button.tiny { font-size: 11px; padding: 1px 7px; }
|
|
|
385
391
|
.field-name {
|
|
386
392
|
color: var(--ink-soft);
|
|
387
393
|
font-size: 12px;
|
|
388
|
-
|
|
394
|
+
min-width: 0;
|
|
395
|
+
white-space: normal;
|
|
389
396
|
overflow-wrap: anywhere;
|
|
390
397
|
}
|
|
391
398
|
|