tinyagent-py 0.0.4__tar.gz → 0.0.6__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.
- {tinyagent_py-0.0.4/tinyagent/tinyagent_py.egg-info → tinyagent_py-0.0.6}/PKG-INFO +19 -3
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/README.md +18 -2
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/pyproject.toml +3 -2
- tinyagent_py-0.0.6/tinyagent/__init__.py +4 -0
- tinyagent_py-0.0.6/tinyagent/mcp_client.py +162 -0
- tinyagent_py-0.0.6/tinyagent/tiny_agent.py +1003 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6/tinyagent_py.egg-info}/PKG-INFO +19 -3
- {tinyagent_py-0.0.4/tinyagent → tinyagent_py-0.0.6}/tinyagent_py.egg-info/SOURCES.txt +8 -5
- tinyagent_py-0.0.6/tinyagent_py.egg-info/top_level.txt +1 -0
- tinyagent_py-0.0.4/tinyagent/tinyagent_py.egg-info/top_level.txt +0 -2
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/LICENSE +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/setup.cfg +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/hooks/__init__.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/hooks/agno_storage_hook.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/hooks/gradio_callback.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/hooks/logging_manager.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/hooks/rich_ui_callback.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/__init__.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/agno_storage.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/base.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/json_file_storage.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/postgres_storage.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/redis_storage.py +0 -0
- {tinyagent_py-0.0.4 → tinyagent_py-0.0.6}/tinyagent/storage/sqlite_storage.py +0 -0
- {tinyagent_py-0.0.4/tinyagent → tinyagent_py-0.0.6}/tinyagent_py.egg-info/dependency_links.txt +0 -0
- {tinyagent_py-0.0.4/tinyagent → tinyagent_py-0.0.6}/tinyagent_py.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: tinyagent-py
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.6
|
4
4
|
Summary: Tiny Agent with MCP Client
|
5
5
|
Author-email: Mahdi Golchin <golchin@askdev.ai>
|
6
6
|
Project-URL: Homepage, https://github.com/askbudi/tinyagent
|
@@ -31,11 +31,17 @@ Requires-Dist: aiosqlite>=0.18.0; extra == "all"
|
|
31
31
|
Requires-Dist: gradio>=3.50.0; extra == "all"
|
32
32
|
Dynamic: license-file
|
33
33
|
|
34
|
-
#
|
35
|
-
Tiny Agent: 100 lines Agent with MCP
|
34
|
+
# TinyAgent
|
35
|
+
Tiny Agent: 100 lines Agent with MCP and extendable hook system
|
36
|
+
|
37
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
38
|
+
|
39
|
+
|
36
40
|

|
37
41
|
|
38
42
|
|
43
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
44
|
+
|
39
45
|
|
40
46
|
Inspired by:
|
41
47
|
- [Tiny Agents blog post](https://huggingface.co/blog/tiny-agents)
|
@@ -91,6 +97,8 @@ uv pip install tinyagent-py[dev]
|
|
91
97
|
```
|
92
98
|
|
93
99
|
## Usage
|
100
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
101
|
+
|
94
102
|
|
95
103
|
```python
|
96
104
|
from tinyagent import TinyAgent
|
@@ -239,6 +247,14 @@ if __name__ == "__main__":
|
|
239
247
|
```
|
240
248
|
---
|
241
249
|
|
250
|
+
## Build your own TinyAgent
|
251
|
+
|
252
|
+
You can chat with TinyAgent and build your own TinyAgent for your use case.
|
253
|
+
|
254
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
255
|
+
|
256
|
+
---
|
257
|
+
|
242
258
|
## Contributing Hooks
|
243
259
|
|
244
260
|
- Place new hooks in the `tinyagent/hooks/` directory.
|
@@ -1,8 +1,14 @@
|
|
1
|
-
#
|
2
|
-
Tiny Agent: 100 lines Agent with MCP
|
1
|
+
# TinyAgent
|
2
|
+
Tiny Agent: 100 lines Agent with MCP and extendable hook system
|
3
|
+
|
4
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
5
|
+
|
6
|
+
|
3
7
|

|
4
8
|
|
5
9
|
|
10
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
11
|
+
|
6
12
|
|
7
13
|
Inspired by:
|
8
14
|
- [Tiny Agents blog post](https://huggingface.co/blog/tiny-agents)
|
@@ -58,6 +64,8 @@ uv pip install tinyagent-py[dev]
|
|
58
64
|
```
|
59
65
|
|
60
66
|
## Usage
|
67
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
68
|
+
|
61
69
|
|
62
70
|
```python
|
63
71
|
from tinyagent import TinyAgent
|
@@ -206,6 +214,14 @@ if __name__ == "__main__":
|
|
206
214
|
```
|
207
215
|
---
|
208
216
|
|
217
|
+
## Build your own TinyAgent
|
218
|
+
|
219
|
+
You can chat with TinyAgent and build your own TinyAgent for your use case.
|
220
|
+
|
221
|
+
[](https://askdev.ai/github/askbudi/tinyagent)
|
222
|
+
|
223
|
+
---
|
224
|
+
|
209
225
|
## Contributing Hooks
|
210
226
|
|
211
227
|
- Place new hooks in the `tinyagent/hooks/` directory.
|
@@ -3,12 +3,13 @@ requires = ["setuptools>=77.0", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
4
4
|
|
5
5
|
[tool.setuptools.packages.find]
|
6
|
-
where = ["
|
6
|
+
where = ["."]
|
7
|
+
include = ["tinyagent*"]
|
7
8
|
exclude = ["public", "public.*"]
|
8
9
|
|
9
10
|
[project]
|
10
11
|
name = "tinyagent-py"
|
11
|
-
version = "0.0.
|
12
|
+
version = "0.0.6"
|
12
13
|
description = "Tiny Agent with MCP Client"
|
13
14
|
readme = "README.md"
|
14
15
|
authors = [
|
@@ -0,0 +1,162 @@
|
|
1
|
+
import asyncio
|
2
|
+
import json
|
3
|
+
import logging
|
4
|
+
from typing import Dict, List, Optional, Any, Tuple, Callable
|
5
|
+
|
6
|
+
# Keep your MCPClient implementation unchanged
|
7
|
+
import asyncio
|
8
|
+
from contextlib import AsyncExitStack
|
9
|
+
|
10
|
+
# MCP core imports
|
11
|
+
from mcp import ClientSession, StdioServerParameters
|
12
|
+
from mcp.client.stdio import stdio_client
|
13
|
+
|
14
|
+
# Set up logging
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
class MCPClient:
|
18
|
+
def __init__(self, logger: Optional[logging.Logger] = None):
|
19
|
+
self.session = None
|
20
|
+
self.exit_stack = AsyncExitStack()
|
21
|
+
self.logger = logger or logging.getLogger(__name__)
|
22
|
+
|
23
|
+
# Simplified callback system
|
24
|
+
self.callbacks: List[callable] = []
|
25
|
+
|
26
|
+
self.logger.debug("MCPClient initialized")
|
27
|
+
|
28
|
+
def add_callback(self, callback: callable) -> None:
|
29
|
+
"""
|
30
|
+
Add a callback function to the client.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
callback: A function that accepts (event_name, client, **kwargs)
|
34
|
+
"""
|
35
|
+
self.callbacks.append(callback)
|
36
|
+
|
37
|
+
async def _run_callbacks(self, event_name: str, **kwargs) -> None:
|
38
|
+
"""
|
39
|
+
Run all registered callbacks for an event.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
event_name: The name of the event
|
43
|
+
**kwargs: Additional data for the event
|
44
|
+
"""
|
45
|
+
for callback in self.callbacks:
|
46
|
+
try:
|
47
|
+
logger.debug(f"Running callback: {callback}")
|
48
|
+
if asyncio.iscoroutinefunction(callback):
|
49
|
+
logger.debug(f"Callback is a coroutine function")
|
50
|
+
await callback(event_name, self, **kwargs)
|
51
|
+
else:
|
52
|
+
# Check if the callback is a class with an async __call__ method
|
53
|
+
if hasattr(callback, '__call__') and asyncio.iscoroutinefunction(callback.__call__):
|
54
|
+
logger.debug(f"Callback is a class with an async __call__ method")
|
55
|
+
await callback(event_name, self, **kwargs)
|
56
|
+
else:
|
57
|
+
logger.debug(f"Callback is a regular function")
|
58
|
+
callback(event_name, self, **kwargs)
|
59
|
+
except Exception as e:
|
60
|
+
logger.error(f"Error in callback for {event_name}: {str(e)}")
|
61
|
+
|
62
|
+
async def connect(self, command: str, args: list[str]):
|
63
|
+
"""
|
64
|
+
Launches the MCP server subprocess and initializes the client session.
|
65
|
+
:param command: e.g. "python" or "node"
|
66
|
+
:param args: list of args to pass, e.g. ["my_server.py"] or ["build/index.js"]
|
67
|
+
"""
|
68
|
+
# Prepare stdio transport parameters
|
69
|
+
params = StdioServerParameters(command=command, args=args)
|
70
|
+
# Open the stdio client transport
|
71
|
+
self.stdio, self.sock_write = await self.exit_stack.enter_async_context(
|
72
|
+
stdio_client(params)
|
73
|
+
)
|
74
|
+
# Create and initialize the MCP client session
|
75
|
+
self.session = await self.exit_stack.enter_async_context(
|
76
|
+
ClientSession(self.stdio, self.sock_write)
|
77
|
+
)
|
78
|
+
await self.session.initialize()
|
79
|
+
|
80
|
+
async def list_tools(self):
|
81
|
+
resp = await self.session.list_tools()
|
82
|
+
print("Available tools:")
|
83
|
+
for tool in resp.tools:
|
84
|
+
print(f" • {tool.name}: {tool.description}")
|
85
|
+
|
86
|
+
async def call_tool(self, name: str, arguments: dict):
|
87
|
+
"""
|
88
|
+
Invokes a named tool and returns its raw content list.
|
89
|
+
"""
|
90
|
+
# Notify tool start
|
91
|
+
await self._run_callbacks("tool_start", tool_name=name, arguments=arguments)
|
92
|
+
|
93
|
+
try:
|
94
|
+
resp = await self.session.call_tool(name, arguments)
|
95
|
+
|
96
|
+
# Notify tool end
|
97
|
+
await self._run_callbacks("tool_end", tool_name=name, arguments=arguments,
|
98
|
+
result=resp.content, success=True)
|
99
|
+
|
100
|
+
return resp.content
|
101
|
+
except Exception as e:
|
102
|
+
# Notify tool end with error
|
103
|
+
await self._run_callbacks("tool_end", tool_name=name, arguments=arguments,
|
104
|
+
error=str(e), success=False)
|
105
|
+
raise
|
106
|
+
|
107
|
+
async def close(self):
|
108
|
+
"""Clean up subprocess and streams."""
|
109
|
+
if self.exit_stack:
|
110
|
+
try:
|
111
|
+
await self.exit_stack.aclose()
|
112
|
+
except (RuntimeError, asyncio.CancelledError) as e:
|
113
|
+
# Log the error but don't re-raise it
|
114
|
+
self.logger.error(f"Error during client cleanup: {e}")
|
115
|
+
finally:
|
116
|
+
# Always reset these regardless of success or failure
|
117
|
+
self.session = None
|
118
|
+
self.exit_stack = AsyncExitStack()
|
119
|
+
|
120
|
+
async def run_example():
|
121
|
+
"""Example usage of MCPClient with proper logging."""
|
122
|
+
import sys
|
123
|
+
from tinyagent.hooks.logging_manager import LoggingManager
|
124
|
+
|
125
|
+
# Create and configure logging manager
|
126
|
+
log_manager = LoggingManager(default_level=logging.INFO)
|
127
|
+
log_manager.set_levels({
|
128
|
+
'tinyagent.mcp_client': logging.DEBUG, # Debug for this module
|
129
|
+
'tinyagent.tiny_agent': logging.INFO,
|
130
|
+
})
|
131
|
+
|
132
|
+
# Configure a console handler
|
133
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
134
|
+
log_manager.configure_handler(
|
135
|
+
console_handler,
|
136
|
+
format_string='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
137
|
+
level=logging.DEBUG
|
138
|
+
)
|
139
|
+
|
140
|
+
# Get module-specific logger
|
141
|
+
mcp_logger = log_manager.get_logger('tinyagent.mcp_client')
|
142
|
+
|
143
|
+
mcp_logger.debug("Starting MCPClient example")
|
144
|
+
|
145
|
+
# Create client with our logger
|
146
|
+
client = MCPClient(logger=mcp_logger)
|
147
|
+
|
148
|
+
try:
|
149
|
+
# Connect to a simple echo server
|
150
|
+
await client.connect("python", ["-m", "mcp.examples.echo_server"])
|
151
|
+
|
152
|
+
# List available tools
|
153
|
+
await client.list_tools()
|
154
|
+
|
155
|
+
# Call the echo tool
|
156
|
+
result = await client.call_tool("echo", {"message": "Hello, MCP!"})
|
157
|
+
mcp_logger.info(f"Echo result: {result}")
|
158
|
+
|
159
|
+
finally:
|
160
|
+
# Clean up
|
161
|
+
await client.close()
|
162
|
+
mcp_logger.debug("Example completed")
|