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.
@@ -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
+ [![PyPI](https://img.shields.io/pypi/v/ovos-tool-adapters)](https://pypi.org/project/ovos-tool-adapters/)
39
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
40
+ [![Tests](https://github.com/OpenVoiceOS/ovos-tool-adapters/actions/workflows/build-tests.yml/badge.svg)](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
+ [![NGI0 Commons Fund](./ngi.png)](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
+ [![PyPI](https://img.shields.io/pypi/v/ovos-tool-adapters)](https://pypi.org/project/ovos-tool-adapters/)
4
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
5
+ [![Tests](https://github.com/OpenVoiceOS/ovos-tool-adapters/actions/workflows/build-tests.yml/badge.svg)](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
+ [![NGI0 Commons Fund](./ngi.png)](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
+ )