ovos-tool-adapters 0.1.0a2__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.
- ovos_tool_adapters-0.1.0a2/LICENSE +132 -0
- ovos_tool_adapters-0.1.0a2/PKG-INFO +128 -0
- ovos_tool_adapters-0.1.0a2/README.md +93 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters/__init__.py +16 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters/_async_runner.py +60 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters/_schema.py +75 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters/mcp.py +213 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters/utcp.py +168 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters/version.py +8 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters.egg-info/PKG-INFO +128 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters.egg-info/SOURCES.txt +19 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters.egg-info/dependency_links.txt +1 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters.egg-info/entry_points.txt +3 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters.egg-info/requires.txt +13 -0
- ovos_tool_adapters-0.1.0a2/ovos_tool_adapters.egg-info/top_level.txt +1 -0
- ovos_tool_adapters-0.1.0a2/pyproject.toml +51 -0
- ovos_tool_adapters-0.1.0a2/setup.cfg +4 -0
- ovos_tool_adapters-0.1.0a2/test/test_async_runner.py +41 -0
- ovos_tool_adapters-0.1.0a2/test/test_mcp.py +130 -0
- ovos_tool_adapters-0.1.0a2/test/test_schema.py +62 -0
- ovos_tool_adapters-0.1.0a2/test/test_utcp.py +97 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship made available under
|
|
36
|
+
the License, as indicated by a copyright notice that is included in
|
|
37
|
+
or attached to the work (an example is provided in the Appendix below).
|
|
38
|
+
|
|
39
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
|
+
form, that is based on (or derived from) the Work and for which the
|
|
41
|
+
editorial revisions, annotations, elaborations, or other transformations
|
|
42
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
43
|
+
of this License, Derivative Works shall not include works that remain
|
|
44
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
45
|
+
the Work and Derivative Works thereof.
|
|
46
|
+
|
|
47
|
+
"Contribution" shall mean, as submitted to the Licensor for inclusion
|
|
48
|
+
in the Work by the copyright owner or by an individual or Legal Entity
|
|
49
|
+
authorized to submit on behalf of the copyright owner. For the purposes
|
|
50
|
+
of this definition, "submitted" means any form of electronic, verbal,
|
|
51
|
+
or written communication sent to the Licensor or its representatives,
|
|
52
|
+
including but not limited to communication on electronic mailing lists,
|
|
53
|
+
source code control systems, and issue tracking systems that are managed
|
|
54
|
+
by, or on behalf of, the Licensor for the purpose of discussing and
|
|
55
|
+
improving the Work, but excluding communication that is conspicuously
|
|
56
|
+
marked or designated in writing by the copyright owner as "Not a
|
|
57
|
+
Contribution."
|
|
58
|
+
|
|
59
|
+
"Contributor" shall mean Licensor and any Legal Entity on behalf of
|
|
60
|
+
whom a Contribution has been received by the Licensor and included
|
|
61
|
+
within the Work.
|
|
62
|
+
|
|
63
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
64
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
65
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
66
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
67
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
68
|
+
Work and such Derivative Works in Source or Object form.
|
|
69
|
+
|
|
70
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
71
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
72
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
73
|
+
(except as stated in this section) patent license to make, have made,
|
|
74
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
75
|
+
where such license applies only to those patent contributions
|
|
76
|
+
Licensable by such Contributor that are necessarily infringed by their
|
|
77
|
+
Contributor contribution(s) alone or by the combination of their
|
|
78
|
+
Contribution(s) with the Work to which such Contribution(s) was submitted.
|
|
79
|
+
|
|
80
|
+
4. Redistribution. You may reproduce and distribute copies of the Work
|
|
81
|
+
or Derivative Works thereof in any medium, with or without
|
|
82
|
+
modifications, and in Source or Object form, provided that You meet
|
|
83
|
+
the following conditions:
|
|
84
|
+
|
|
85
|
+
(a) You must give any other recipients of the Work or Derivative Works
|
|
86
|
+
a copy of this License; and
|
|
87
|
+
|
|
88
|
+
(b) You must cause any modified files to carry prominent notices
|
|
89
|
+
stating that You changed the files; and
|
|
90
|
+
|
|
91
|
+
(c) You must retain, in the Source form of any Derivative Works that
|
|
92
|
+
You distribute, all copyright, patent, trademark, and attribution
|
|
93
|
+
notices from the Source form of the Work, excluding those notices
|
|
94
|
+
that do not pertain to any part of the Derivative Works; and
|
|
95
|
+
|
|
96
|
+
(d) If the Work includes a "NOTICE" text file, as part of the
|
|
97
|
+
distribution, you must include a readable copy of the attribution
|
|
98
|
+
notices contained within such NOTICE file.
|
|
99
|
+
|
|
100
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
101
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
102
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
103
|
+
this License, without any additional terms or conditions.
|
|
104
|
+
|
|
105
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
106
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
107
|
+
except as required for reasonable and customary use in describing the
|
|
108
|
+
origin of the Work.
|
|
109
|
+
|
|
110
|
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed
|
|
111
|
+
to in writing, Licensor provides the Work (and each Contributor provides
|
|
112
|
+
its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
|
113
|
+
OF ANY KIND, either express or implied.
|
|
114
|
+
|
|
115
|
+
8. Limitation of Liability. In no event and under no legal theory shall
|
|
116
|
+
any Contributor be liable to You for damages, including any direct,
|
|
117
|
+
indirect, special, incidental, or exemplary damages of any character
|
|
118
|
+
arising as a result of this License or out of the use or inability to
|
|
119
|
+
use the Work.
|
|
120
|
+
|
|
121
|
+
9. Accepting Warranty or Additional Liability. While redistributing the
|
|
122
|
+
Work or Derivative Works thereof, You may choose to offer acceptance
|
|
123
|
+
of warranty, support, indemnity, or other liability obligations consistent
|
|
124
|
+
with this License.
|
|
125
|
+
|
|
126
|
+
Copyright 2024 OpenVoiceOS
|
|
127
|
+
|
|
128
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
129
|
+
you may not use this file except in compliance with the License.
|
|
130
|
+
You may obtain a copy of the License at
|
|
131
|
+
|
|
132
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ovos-tool-adapters
|
|
3
|
+
Version: 0.1.0a2
|
|
4
|
+
Summary: MCP and UTCP ToolBox plugins for the OVOS agentic loop
|
|
5
|
+
Author-email: OpenVoiceOS <jarbasai@mailfence.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/OpenVoiceOS/ovos-tool-adapters
|
|
8
|
+
Project-URL: Repository, https://github.com/OpenVoiceOS/ovos-tool-adapters
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/OpenVoiceOS/ovos-tool-adapters/issues
|
|
10
|
+
Keywords: ovos,mycroft,mcp,utcp,agent,toolbox,plugin
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: ovos-plugin-manager>=0.7.0
|
|
25
|
+
Requires-Dist: pydantic>=2.0
|
|
26
|
+
Provides-Extra: mcp
|
|
27
|
+
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
28
|
+
Provides-Extra: utcp
|
|
29
|
+
Requires-Dist: utcp>=1.1; extra == "utcp"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# ovos-tool-adapters
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/ovos-tool-adapters/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
[](https://github.com/OpenVoiceOS/ovos-tool-adapters/actions/workflows/build-tests.yml)
|
|
41
|
+
|
|
42
|
+
Bridges **MCP** (Model Context Protocol) and **UTCP** (Universal Tool Calling Protocol) servers into the [OVOS agentic loop](https://github.com/OpenVoiceOS/ovos-agentic-loop) as standard `ToolBox` plugins.
|
|
43
|
+
|
|
44
|
+
Configure an MCP or UTCP server in your persona JSON and the agent loop consumes it like any other toolbox — no protocol awareness required.
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install ovos-tool-adapters[mcp] # MCP support
|
|
50
|
+
pip install ovos-tool-adapters[utcp] # UTCP support
|
|
51
|
+
pip install ovos-tool-adapters[mcp,utcp]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
Add to your persona JSON:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"name": "researcher",
|
|
61
|
+
"chat_module": "ovos-react-loop",
|
|
62
|
+
"toolboxes": ["ovos-mcp-toolbox"],
|
|
63
|
+
"ovos-mcp-toolbox": {
|
|
64
|
+
"transport": "stdio",
|
|
65
|
+
"command": "uvx",
|
|
66
|
+
"args": ["mcp-server-fetch"],
|
|
67
|
+
"timeout": 30
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The agent now has access to every tool the MCP server exposes, with the real JSON Schema forwarded to the LLM.
|
|
73
|
+
|
|
74
|
+
## Supported transports
|
|
75
|
+
|
|
76
|
+
### MCP (`ovos-mcp-toolbox`)
|
|
77
|
+
|
|
78
|
+
| Transport | Config |
|
|
79
|
+
|---|---|
|
|
80
|
+
| stdio (subprocess) | `"transport": "stdio", "command": "uvx", "args": [...]` |
|
|
81
|
+
| SSE | `"transport": "sse", "url": "http://..."` |
|
|
82
|
+
| Streamable HTTP | `"transport": "http", "url": "http://..."` |
|
|
83
|
+
|
|
84
|
+
### UTCP (`ovos-utcp-toolbox`)
|
|
85
|
+
|
|
86
|
+
Any transport supported by the installed UTCP version (HTTP, SSE, CLI, WebSocket, MCP, …):
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"toolboxes": ["ovos-utcp-toolbox"],
|
|
91
|
+
"ovos-utcp-toolbox": {
|
|
92
|
+
"utcp_config": {
|
|
93
|
+
"tool_providers": [
|
|
94
|
+
{"name": "search", "provider_type": "http", "url": "http://localhost:8000/tools"}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## How it works
|
|
102
|
+
|
|
103
|
+
- A daemon-thread asyncio event loop keeps MCP/UTCP sessions alive between calls — no reconnect per tool call.
|
|
104
|
+
- Each server's JSON Schema is translated to a Pydantic model at discovery time, so the LLM sees the **actual** input schema.
|
|
105
|
+
- Missing `mcp`/`utcp` packages degrade gracefully: the toolbox returns an empty tool list and logs a warning — the agent loop is not affected.
|
|
106
|
+
|
|
107
|
+
Full documentation: [docs/](docs/index.md)
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Credits
|
|
112
|
+
|
|
113
|
+
Developed by [TigreGótico](https://tigregotico.pt) for
|
|
114
|
+
[OpenVoiceOS](https://openvoiceos.org).
|
|
115
|
+
|
|
116
|
+
[](https://nlnet.nl/project/OpenVoiceOS)
|
|
117
|
+
|
|
118
|
+
This project was funded through the [NGI0 Commons Fund](https://nlnet.nl/commonsfund),
|
|
119
|
+
a fund established by [NLnet](https://nlnet.nl) with financial support from the
|
|
120
|
+
European Commission's [Next Generation Internet](https://ngi.eu) programme, under
|
|
121
|
+
the aegis of [DG Communications Networks, Content and Technology](https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en)
|
|
122
|
+
under grant agreement No [101135429](https://cordis.europa.eu/project/id/101135429).
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ovos-tool-adapters
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/ovos-tool-adapters/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/OpenVoiceOS/ovos-tool-adapters/actions/workflows/build-tests.yml)
|
|
6
|
+
|
|
7
|
+
Bridges **MCP** (Model Context Protocol) and **UTCP** (Universal Tool Calling Protocol) servers into the [OVOS agentic loop](https://github.com/OpenVoiceOS/ovos-agentic-loop) as standard `ToolBox` plugins.
|
|
8
|
+
|
|
9
|
+
Configure an MCP or UTCP server in your persona JSON and the agent loop consumes it like any other toolbox — no protocol awareness required.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install ovos-tool-adapters[mcp] # MCP support
|
|
15
|
+
pip install ovos-tool-adapters[utcp] # UTCP support
|
|
16
|
+
pip install ovos-tool-adapters[mcp,utcp]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Add to your persona JSON:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"name": "researcher",
|
|
26
|
+
"chat_module": "ovos-react-loop",
|
|
27
|
+
"toolboxes": ["ovos-mcp-toolbox"],
|
|
28
|
+
"ovos-mcp-toolbox": {
|
|
29
|
+
"transport": "stdio",
|
|
30
|
+
"command": "uvx",
|
|
31
|
+
"args": ["mcp-server-fetch"],
|
|
32
|
+
"timeout": 30
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The agent now has access to every tool the MCP server exposes, with the real JSON Schema forwarded to the LLM.
|
|
38
|
+
|
|
39
|
+
## Supported transports
|
|
40
|
+
|
|
41
|
+
### MCP (`ovos-mcp-toolbox`)
|
|
42
|
+
|
|
43
|
+
| Transport | Config |
|
|
44
|
+
|---|---|
|
|
45
|
+
| stdio (subprocess) | `"transport": "stdio", "command": "uvx", "args": [...]` |
|
|
46
|
+
| SSE | `"transport": "sse", "url": "http://..."` |
|
|
47
|
+
| Streamable HTTP | `"transport": "http", "url": "http://..."` |
|
|
48
|
+
|
|
49
|
+
### UTCP (`ovos-utcp-toolbox`)
|
|
50
|
+
|
|
51
|
+
Any transport supported by the installed UTCP version (HTTP, SSE, CLI, WebSocket, MCP, …):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"toolboxes": ["ovos-utcp-toolbox"],
|
|
56
|
+
"ovos-utcp-toolbox": {
|
|
57
|
+
"utcp_config": {
|
|
58
|
+
"tool_providers": [
|
|
59
|
+
{"name": "search", "provider_type": "http", "url": "http://localhost:8000/tools"}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## How it works
|
|
67
|
+
|
|
68
|
+
- A daemon-thread asyncio event loop keeps MCP/UTCP sessions alive between calls — no reconnect per tool call.
|
|
69
|
+
- Each server's JSON Schema is translated to a Pydantic model at discovery time, so the LLM sees the **actual** input schema.
|
|
70
|
+
- Missing `mcp`/`utcp` packages degrade gracefully: the toolbox returns an empty tool list and logs a warning — the agent loop is not affected.
|
|
71
|
+
|
|
72
|
+
Full documentation: [docs/](docs/index.md)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Credits
|
|
77
|
+
|
|
78
|
+
Developed by [TigreGótico](https://tigregotico.pt) for
|
|
79
|
+
[OpenVoiceOS](https://openvoiceos.org).
|
|
80
|
+
|
|
81
|
+
[](https://nlnet.nl/project/OpenVoiceOS)
|
|
82
|
+
|
|
83
|
+
This project was funded through the [NGI0 Commons Fund](https://nlnet.nl/commonsfund),
|
|
84
|
+
a fund established by [NLnet](https://nlnet.nl) with financial support from the
|
|
85
|
+
European Commission's [Next Generation Internet](https://ngi.eu) programme, under
|
|
86
|
+
the aegis of [DG Communications Networks, Content and Technology](https://commission.europa.eu/about-european-commission/departments-and-executive-agencies/communications-networks-content-and-technology_en)
|
|
87
|
+
under grant agreement No [101135429](https://cordis.europa.eu/project/id/101135429).
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
"""ovos-tool-adapters — MCP and UTCP ToolBox plugins for the OVOS agentic loop."""
|
|
13
|
+
|
|
14
|
+
from ovos_tool_adapters.version import __version__
|
|
15
|
+
|
|
16
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
"""Async bridge: runs a persistent asyncio event loop in a daemon thread."""
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import threading
|
|
16
|
+
from typing import Any, Coroutine
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _AsyncRunner:
|
|
20
|
+
"""
|
|
21
|
+
Owns a daemon thread running a private asyncio event loop.
|
|
22
|
+
|
|
23
|
+
Provides a synchronous ``run`` method that submits a coroutine to that loop
|
|
24
|
+
and blocks until completion. This avoids ``asyncio.run()`` (which destroys
|
|
25
|
+
the loop and all managed resources after each call) and is safe to call
|
|
26
|
+
from any synchronous context.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
"""Start the daemon thread and its event loop."""
|
|
31
|
+
self._loop: asyncio.AbstractEventLoop = asyncio.new_event_loop()
|
|
32
|
+
self._thread: threading.Thread = threading.Thread(
|
|
33
|
+
target=self._loop.run_forever,
|
|
34
|
+
daemon=True,
|
|
35
|
+
name="ovos-adapter-async",
|
|
36
|
+
)
|
|
37
|
+
self._thread.start()
|
|
38
|
+
|
|
39
|
+
def run(self, coro: Coroutine[Any, Any, Any], timeout: int = 30) -> Any:
|
|
40
|
+
"""
|
|
41
|
+
Submit *coro* to the runner loop and block until it completes.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
coro: An unawaited coroutine object.
|
|
45
|
+
timeout: Maximum seconds to wait for the result.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Whatever the coroutine returns.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
TimeoutError: If the coroutine does not finish within *timeout* seconds.
|
|
52
|
+
Exception: Any exception raised inside the coroutine.
|
|
53
|
+
"""
|
|
54
|
+
future = asyncio.run_coroutine_threadsafe(coro, self._loop)
|
|
55
|
+
return future.result(timeout=timeout)
|
|
56
|
+
|
|
57
|
+
def close(self) -> None:
|
|
58
|
+
"""Stop the event loop and wait for the daemon thread to finish."""
|
|
59
|
+
self._loop.call_soon_threadsafe(self._loop.stop)
|
|
60
|
+
self._thread.join(timeout=5)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
2
|
+
# you may not use this file except in compliance with the License.
|
|
3
|
+
# You may obtain a copy of the License at
|
|
4
|
+
#
|
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
#
|
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10
|
+
# See the License for the specific language governing permissions and
|
|
11
|
+
# limitations under the License.
|
|
12
|
+
"""JSON Schema → Pydantic bridge and shared output type for adapters."""
|
|
13
|
+
|
|
14
|
+
from typing import Any, Dict, List, Optional, Type
|
|
15
|
+
|
|
16
|
+
from ovos_plugin_manager.templates.agent_tools import ToolArguments, ToolOutput
|
|
17
|
+
from pydantic import Field, create_model
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Mapping from JSON Schema primitive types to Python types.
|
|
21
|
+
_TYPE_MAP: Dict[str, Any] = {
|
|
22
|
+
"string": str,
|
|
23
|
+
"integer": int,
|
|
24
|
+
"number": float,
|
|
25
|
+
"boolean": bool,
|
|
26
|
+
"array": list,
|
|
27
|
+
"object": dict,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _schema_to_pydantic(model_name: str, json_schema: Dict[str, Any]) -> Type[ToolArguments]:
|
|
32
|
+
"""
|
|
33
|
+
Build a dynamic Pydantic ``ToolArguments`` subclass from a JSON Schema dict.
|
|
34
|
+
|
|
35
|
+
Handles ``string``, ``integer``, ``number``, ``boolean``, ``array``, and
|
|
36
|
+
``object`` types. Falls back to ``Any`` for unrecognised types.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
model_name: Name for the generated class (used in error messages).
|
|
40
|
+
json_schema: A JSON Schema object with at least a ``properties`` key.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A ``ToolArguments`` subclass with fields matching the schema.
|
|
44
|
+
"""
|
|
45
|
+
properties: Dict[str, Any] = json_schema.get("properties", {})
|
|
46
|
+
required: List[str] = json_schema.get("required", [])
|
|
47
|
+
field_definitions: Dict[str, Any] = {}
|
|
48
|
+
|
|
49
|
+
for prop_name, prop_schema in properties.items():
|
|
50
|
+
py_type: Any = _TYPE_MAP.get(prop_schema.get("type", ""), Any)
|
|
51
|
+
description: str = prop_schema.get("description", "")
|
|
52
|
+
if prop_name in required:
|
|
53
|
+
field_definitions[prop_name] = (py_type, Field(..., description=description))
|
|
54
|
+
else:
|
|
55
|
+
field_definitions[prop_name] = (Optional[py_type], Field(None, description=description))
|
|
56
|
+
|
|
57
|
+
return create_model(model_name, __base__=ToolArguments, **field_definitions) # type: ignore[call-overload]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AdapterToolOutput(ToolOutput):
|
|
61
|
+
"""
|
|
62
|
+
Generic output type for both MCP and UTCP tool calls.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
content: Concatenated text content from the server response.
|
|
66
|
+
is_error: ``True`` if the server reported an error.
|
|
67
|
+
raw: Original content blocks preserved for downstream inspection.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
content: str = Field("", description="Concatenated text content from the server response.")
|
|
71
|
+
is_error: bool = Field(False, description="True if the server reported an error.")
|
|
72
|
+
raw: List[Dict[str, Any]] = Field(
|
|
73
|
+
default_factory=list,
|
|
74
|
+
description="Original content blocks from the server response.",
|
|
75
|
+
)
|