tinyagent-py 0.0.1__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.1/LICENSE +21 -0
- tinyagent_py-0.0.1/PKG-INFO +79 -0
- tinyagent_py-0.0.1/README.md +60 -0
- tinyagent_py-0.0.1/pyproject.toml +31 -0
- tinyagent_py-0.0.1/setup.cfg +4 -0
- tinyagent_py-0.0.1/tinyagent/__init__.py +4 -0
- tinyagent_py-0.0.1/tinyagent/mcp_client.py +52 -0
- tinyagent_py-0.0.1/tinyagent/tiny_agent.py +247 -0
- tinyagent_py-0.0.1/tinyagent_py.egg-info/PKG-INFO +79 -0
- tinyagent_py-0.0.1/tinyagent_py.egg-info/SOURCES.txt +11 -0
- tinyagent_py-0.0.1/tinyagent_py.egg-info/dependency_links.txt +1 -0
- tinyagent_py-0.0.1/tinyagent_py.egg-info/requires.txt +6 -0
- tinyagent_py-0.0.1/tinyagent_py.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 askbudi
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,79 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: tinyagent-py
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Tiny Agent with MCP Client
|
5
|
+
Author-email: Mahdi Golchin <golchin@askdev.ai>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/askbudi/tinyagent
|
8
|
+
Project-URL: Bug Tracker, https://github.com/askbudi/tinyagent/issues
|
9
|
+
Project-URL: Chat, https://askdev.ai/github/askbudi/tinyagent
|
10
|
+
Requires-Python: >=3.8
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: mcp
|
14
|
+
Requires-Dist: litellm
|
15
|
+
Requires-Dist: openai
|
16
|
+
Provides-Extra: dev
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
18
|
+
Dynamic: license-file
|
19
|
+
|
20
|
+
# tinyagent
|
21
|
+
Tiny Agent: 100 lines Agent with MCP
|
22
|
+
|
23
|
+
Inspired by:
|
24
|
+
- [Tiny Agents blog post](https://huggingface.co/blog/tiny-agents)
|
25
|
+
- [12-factor-agents repository](https://github.com/humanlayer/12-factor-agents)
|
26
|
+
- Created by chatting to the source code of JS Tiny Agent using [AskDev.ai](https://askdev.ai/search)
|
27
|
+
|
28
|
+
## Quick Links
|
29
|
+
- [Build your own Tiny Agent](https://askdev.ai/github/askbudi/tinyagent)
|
30
|
+
|
31
|
+
## Overview
|
32
|
+
This is a tiny agent that uses MCP and LiteLLM to interact with a model. You have full control over the agent, you can add any tools you like from MCP and extend the agent using its event system.
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
### Using pip
|
37
|
+
```bash
|
38
|
+
pip install tinyagent
|
39
|
+
```
|
40
|
+
|
41
|
+
### Using uv
|
42
|
+
```bash
|
43
|
+
uv pip install tinyagent
|
44
|
+
```
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
```python
|
49
|
+
from tinyagent import TinyAgent
|
50
|
+
from textwrap import dedent
|
51
|
+
import asyncio
|
52
|
+
import os
|
53
|
+
|
54
|
+
async def test_agent(task, model="o4-mini", api_key=None):
|
55
|
+
# Initialize the agent with model and API key
|
56
|
+
agent = TinyAgent(
|
57
|
+
model=model, # Or any model supported by LiteLLM
|
58
|
+
api_key=os.environ.get("OPENAI_API_KEY") if not api_key else api_key # Set your API key as an env variable
|
59
|
+
)
|
60
|
+
|
61
|
+
try:
|
62
|
+
# Connect to an MCP server
|
63
|
+
# Replace with your actual server command and args
|
64
|
+
await agent.connect_to_server("npx", ["@openbnb/mcp-server-airbnb", "--ignore-robots-txt"])
|
65
|
+
|
66
|
+
# Run the agent with a user query
|
67
|
+
result = await agent.run(task)
|
68
|
+
print("\nFinal result:", result)
|
69
|
+
return result
|
70
|
+
finally:
|
71
|
+
# Clean up resources
|
72
|
+
await agent.close()
|
73
|
+
|
74
|
+
# Example usage
|
75
|
+
task = dedent("""
|
76
|
+
I need accommodation in Toronto between 15th to 20th of May. Give me 5 options for 2 adults.
|
77
|
+
""")
|
78
|
+
await test_agent(task, model="gpt-4.1-mini")
|
79
|
+
```
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# tinyagent
|
2
|
+
Tiny Agent: 100 lines Agent with MCP
|
3
|
+
|
4
|
+
Inspired by:
|
5
|
+
- [Tiny Agents blog post](https://huggingface.co/blog/tiny-agents)
|
6
|
+
- [12-factor-agents repository](https://github.com/humanlayer/12-factor-agents)
|
7
|
+
- Created by chatting to the source code of JS Tiny Agent using [AskDev.ai](https://askdev.ai/search)
|
8
|
+
|
9
|
+
## Quick Links
|
10
|
+
- [Build your own Tiny Agent](https://askdev.ai/github/askbudi/tinyagent)
|
11
|
+
|
12
|
+
## Overview
|
13
|
+
This is a tiny agent that uses MCP and LiteLLM to interact with a model. You have full control over the agent, you can add any tools you like from MCP and extend the agent using its event system.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
### Using pip
|
18
|
+
```bash
|
19
|
+
pip install tinyagent
|
20
|
+
```
|
21
|
+
|
22
|
+
### Using uv
|
23
|
+
```bash
|
24
|
+
uv pip install tinyagent
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
```python
|
30
|
+
from tinyagent import TinyAgent
|
31
|
+
from textwrap import dedent
|
32
|
+
import asyncio
|
33
|
+
import os
|
34
|
+
|
35
|
+
async def test_agent(task, model="o4-mini", api_key=None):
|
36
|
+
# Initialize the agent with model and API key
|
37
|
+
agent = TinyAgent(
|
38
|
+
model=model, # Or any model supported by LiteLLM
|
39
|
+
api_key=os.environ.get("OPENAI_API_KEY") if not api_key else api_key # Set your API key as an env variable
|
40
|
+
)
|
41
|
+
|
42
|
+
try:
|
43
|
+
# Connect to an MCP server
|
44
|
+
# Replace with your actual server command and args
|
45
|
+
await agent.connect_to_server("npx", ["@openbnb/mcp-server-airbnb", "--ignore-robots-txt"])
|
46
|
+
|
47
|
+
# Run the agent with a user query
|
48
|
+
result = await agent.run(task)
|
49
|
+
print("\nFinal result:", result)
|
50
|
+
return result
|
51
|
+
finally:
|
52
|
+
# Clean up resources
|
53
|
+
await agent.close()
|
54
|
+
|
55
|
+
# Example usage
|
56
|
+
task = dedent("""
|
57
|
+
I need accommodation in Toronto between 15th to 20th of May. Give me 5 options for 2 adults.
|
58
|
+
""")
|
59
|
+
await test_agent(task, model="gpt-4.1-mini")
|
60
|
+
```
|
@@ -0,0 +1,31 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "tinyagent-py"
|
7
|
+
version = "0.0.1"
|
8
|
+
description = "Tiny Agent with MCP Client"
|
9
|
+
readme = "README.md"
|
10
|
+
license = {text = "MIT"}
|
11
|
+
authors = [
|
12
|
+
{name="Mahdi Golchin", email="golchin@askdev.ai"}
|
13
|
+
]
|
14
|
+
requires-python = ">=3.8"
|
15
|
+
dependencies = [
|
16
|
+
"mcp",
|
17
|
+
"litellm",
|
18
|
+
"openai"
|
19
|
+
# add whatever else you need…
|
20
|
+
]
|
21
|
+
|
22
|
+
[project.optional-dependencies]
|
23
|
+
dev = [
|
24
|
+
"pytest",
|
25
|
+
|
26
|
+
|
27
|
+
]
|
28
|
+
[project.urls]
|
29
|
+
"Homepage" = "https://github.com/askbudi/tinyagent"
|
30
|
+
"Bug Tracker" = "https://github.com/askbudi/tinyagent/issues"
|
31
|
+
"Chat" = "https://askdev.ai/github/askbudi/tinyagent"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import asyncio
|
2
|
+
import json
|
3
|
+
import logging
|
4
|
+
from typing import Dict, List, Optional, Any, Tuple
|
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
|
+
class MCPClient:
|
15
|
+
def __init__(self):
|
16
|
+
self.session = None
|
17
|
+
self.exit_stack = AsyncExitStack()
|
18
|
+
|
19
|
+
async def connect(self, command: str, args: list[str]):
|
20
|
+
"""
|
21
|
+
Launches the MCP server subprocess and initializes the client session.
|
22
|
+
:param command: e.g. "python" or "node"
|
23
|
+
:param args: list of args to pass, e.g. ["my_server.py"] or ["build/index.js"]
|
24
|
+
"""
|
25
|
+
# Prepare stdio transport parameters
|
26
|
+
params = StdioServerParameters(command=command, args=args)
|
27
|
+
# Open the stdio client transport
|
28
|
+
self.stdio, self.sock_write = await self.exit_stack.enter_async_context(
|
29
|
+
stdio_client(params)
|
30
|
+
)
|
31
|
+
# Create and initialize the MCP client session
|
32
|
+
self.session = await self.exit_stack.enter_async_context(
|
33
|
+
ClientSession(self.stdio, self.sock_write)
|
34
|
+
)
|
35
|
+
await self.session.initialize()
|
36
|
+
|
37
|
+
async def list_tools(self):
|
38
|
+
resp = await self.session.list_tools()
|
39
|
+
print("Available tools:")
|
40
|
+
for tool in resp.tools:
|
41
|
+
print(f" • {tool.name}: {tool.description}")
|
42
|
+
|
43
|
+
async def call_tool(self, name: str, arguments: dict):
|
44
|
+
"""
|
45
|
+
Invokes a named tool and returns its raw content list.
|
46
|
+
"""
|
47
|
+
resp = await self.session.call_tool(name, arguments)
|
48
|
+
return resp.content
|
49
|
+
|
50
|
+
async def close(self):
|
51
|
+
# Clean up subprocess and streams
|
52
|
+
await self.exit_stack.aclose()
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# Import LiteLLM for model interaction
|
2
|
+
import litellm
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
from typing import Dict, List, Optional, Any, Tuple
|
6
|
+
from .mcp_client import MCPClient
|
7
|
+
|
8
|
+
# Set up logging
|
9
|
+
logging.basicConfig(level=logging.DEBUG)
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
#litellm.callbacks = ["arize_phoenix"]
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
class TinyAgent:
|
16
|
+
"""
|
17
|
+
A minimal implementation of an agent powered by MCP and LiteLLM.
|
18
|
+
This agent is literally just a while loop on top of MCPClient.
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self, model: str = "gpt-4o", api_key: Optional[str] = None, system_prompt: Optional[str] = None):
|
22
|
+
"""
|
23
|
+
Initialize the Tiny Agent.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
model: The model to use with LiteLLM
|
27
|
+
api_key: The API key for the model provider
|
28
|
+
system_prompt: Custom system prompt for the agent
|
29
|
+
"""
|
30
|
+
# Create the MCPClient
|
31
|
+
self.mcp_client = MCPClient()
|
32
|
+
|
33
|
+
# LiteLLM configuration
|
34
|
+
self.model = model
|
35
|
+
self.api_key = api_key
|
36
|
+
if api_key:
|
37
|
+
litellm.api_key = api_key
|
38
|
+
|
39
|
+
# Conversation state
|
40
|
+
self.messages = [{
|
41
|
+
"role": "system",
|
42
|
+
"content": system_prompt or (
|
43
|
+
"You are a helpful AI assistant with access to a variety of tools. "
|
44
|
+
"Use the tools when appropriate to accomplish tasks. "
|
45
|
+
"If a tool you need isn't available, just say so."
|
46
|
+
)
|
47
|
+
}]
|
48
|
+
|
49
|
+
# Available tools (will be populated after connecting to MCP servers)
|
50
|
+
self.available_tools = []
|
51
|
+
|
52
|
+
# Control flow tools
|
53
|
+
self.exit_loop_tools = [
|
54
|
+
{
|
55
|
+
"type": "function",
|
56
|
+
"function": {
|
57
|
+
"name": "task_complete",
|
58
|
+
"description": "Call this tool when the task given by the user is complete",
|
59
|
+
"parameters": {"type": "object", "properties": {}}
|
60
|
+
}
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"type": "function",
|
64
|
+
"function": {
|
65
|
+
"name": "ask_question",
|
66
|
+
"description": "Ask a question to the user to get more info required to solve or clarify their problem.",
|
67
|
+
"parameters": {
|
68
|
+
"type": "object",
|
69
|
+
"properties": {
|
70
|
+
"question": {
|
71
|
+
"type": "string",
|
72
|
+
"description": "The question to ask the user"
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"required": ["question"]
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
]
|
80
|
+
|
81
|
+
async def connect_to_server(self, command: str, args: List[str]) -> None:
|
82
|
+
"""
|
83
|
+
Connect to an MCP server and fetch available tools.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
command: The command to run the server
|
87
|
+
args: List of arguments for the server
|
88
|
+
"""
|
89
|
+
await self.mcp_client.connect(command, args)
|
90
|
+
|
91
|
+
# Get available tools from the server and format them for LiteLLM
|
92
|
+
resp = await self.mcp_client.session.list_tools()
|
93
|
+
|
94
|
+
tool_descriptions = []
|
95
|
+
for tool in resp.tools:
|
96
|
+
tool_descriptions.append({
|
97
|
+
"type": "function",
|
98
|
+
"function": {
|
99
|
+
"name": tool.name,
|
100
|
+
"description": tool.description,
|
101
|
+
"parameters": tool.inputSchema
|
102
|
+
}
|
103
|
+
})
|
104
|
+
|
105
|
+
logger.info(f"Added {len(tool_descriptions)} tools from MCP server")
|
106
|
+
self.available_tools.extend(tool_descriptions)
|
107
|
+
|
108
|
+
async def run(self, user_input: str, max_turns: int = 10) -> str:
|
109
|
+
"""
|
110
|
+
Run the agent with user input.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
user_input: The user's request
|
114
|
+
max_turns: Maximum number of turns before giving up
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
The final agent response
|
118
|
+
"""
|
119
|
+
# Add user message to conversation
|
120
|
+
self.messages.append({"role": "user", "content": user_input})
|
121
|
+
|
122
|
+
# Initialize loop control variables
|
123
|
+
num_turns = 0
|
124
|
+
next_turn_should_call_tools = True
|
125
|
+
|
126
|
+
# The main agent loop
|
127
|
+
while True:
|
128
|
+
# Get all available tools including exit loop tools
|
129
|
+
all_tools = self.available_tools + self.exit_loop_tools
|
130
|
+
|
131
|
+
# Call LLM with messages and tools
|
132
|
+
try:
|
133
|
+
logger.info(f"Calling LLM with {len(self.messages)} messages and {len(all_tools)} tools")
|
134
|
+
response = await litellm.acompletion(
|
135
|
+
model=self.model,
|
136
|
+
messages=self.messages,
|
137
|
+
tools=all_tools,
|
138
|
+
tool_choice="auto"
|
139
|
+
)
|
140
|
+
|
141
|
+
# Process the response - properly handle the object
|
142
|
+
response_message = response.choices[0].message
|
143
|
+
logger.debug(f"🔥🔥🔥🔥🔥🔥 Response : {response_message}")
|
144
|
+
|
145
|
+
# Create a proper message dictionary from the response object's attributes
|
146
|
+
assistant_message = {
|
147
|
+
"role": "assistant",
|
148
|
+
"content": response_message.content if hasattr(response_message, "content") else ""
|
149
|
+
}
|
150
|
+
|
151
|
+
# Check if the message has tool_calls attribute and it's not empty
|
152
|
+
has_tool_calls = hasattr(response_message, "tool_calls") and response_message.tool_calls
|
153
|
+
|
154
|
+
if has_tool_calls:
|
155
|
+
# Add tool_calls to the message if present
|
156
|
+
assistant_message["tool_calls"] = response_message.tool_calls
|
157
|
+
|
158
|
+
# Add the properly formatted assistant message to conversation
|
159
|
+
self.messages.append(assistant_message)
|
160
|
+
|
161
|
+
# Process tool calls if they exist
|
162
|
+
if has_tool_calls:
|
163
|
+
tool_calls = response_message.tool_calls
|
164
|
+
logger.info(f"Tool calls detected: {len(tool_calls)}")
|
165
|
+
|
166
|
+
# Process each tool call one by one
|
167
|
+
for tool_call in tool_calls:
|
168
|
+
tool_call_id = tool_call.id
|
169
|
+
function_info = tool_call.function
|
170
|
+
tool_name = function_info.name
|
171
|
+
|
172
|
+
# Create a tool message
|
173
|
+
tool_message = {
|
174
|
+
"role": "tool",
|
175
|
+
"tool_call_id": tool_call_id,
|
176
|
+
"name": tool_name,
|
177
|
+
"content": "" # Default empty content
|
178
|
+
}
|
179
|
+
|
180
|
+
try:
|
181
|
+
# Parse tool arguments
|
182
|
+
try:
|
183
|
+
tool_args = json.loads(function_info.arguments)
|
184
|
+
except json.JSONDecodeError:
|
185
|
+
logger.error(f"Could not parse tool arguments: {function_info.arguments}")
|
186
|
+
tool_args = {}
|
187
|
+
|
188
|
+
# Handle control flow tools
|
189
|
+
if tool_name == "task_complete":
|
190
|
+
# Add a response for this tool call before returning
|
191
|
+
tool_message["content"] = "Task has been completed successfully."
|
192
|
+
self.messages.append(tool_message)
|
193
|
+
return "Task completed."
|
194
|
+
elif tool_name == "ask_question":
|
195
|
+
question = tool_args.get("question", "Could you provide more details?")
|
196
|
+
# Add a response for this tool call before returning
|
197
|
+
tool_message["content"] = f"Question asked: {question}"
|
198
|
+
self.messages.append(tool_message)
|
199
|
+
return f"I need more information: {question}"
|
200
|
+
else:
|
201
|
+
# Call the actual tool using MCPClient
|
202
|
+
try:
|
203
|
+
content_list = await self.mcp_client.call_tool(tool_name, tool_args)
|
204
|
+
|
205
|
+
# Safely extract text from the content
|
206
|
+
if content_list:
|
207
|
+
# Try different ways to extract the content
|
208
|
+
if hasattr(content_list[0], 'text'):
|
209
|
+
tool_message["content"] = content_list[0].text
|
210
|
+
elif isinstance(content_list[0], dict) and 'text' in content_list[0]:
|
211
|
+
tool_message["content"] = content_list[0]['text']
|
212
|
+
else:
|
213
|
+
tool_message["content"] = str(content_list)
|
214
|
+
else:
|
215
|
+
tool_message["content"] = "Tool returned no content"
|
216
|
+
except Exception as e:
|
217
|
+
logger.error(f"Error calling tool {tool_name}: {str(e)}")
|
218
|
+
tool_message["content"] = f"Error executing tool {tool_name}: {str(e)}"
|
219
|
+
except Exception as e:
|
220
|
+
# If any error occurs during tool call processing, make sure we still have a tool response
|
221
|
+
logger.error(f"Unexpected error processing tool call {tool_call_id}: {str(e)}")
|
222
|
+
tool_message["content"] = f"Error processing tool call: {str(e)}"
|
223
|
+
|
224
|
+
# Always add the tool message to ensure each tool call has a response
|
225
|
+
self.messages.append(tool_message)
|
226
|
+
|
227
|
+
next_turn_should_call_tools = False
|
228
|
+
else:
|
229
|
+
# No tool calls in this message
|
230
|
+
if next_turn_should_call_tools and num_turns > 0:
|
231
|
+
# If we expected tool calls but didn't get any, we're done
|
232
|
+
return assistant_message["content"] or ""
|
233
|
+
|
234
|
+
next_turn_should_call_tools = True
|
235
|
+
|
236
|
+
num_turns += 1
|
237
|
+
if num_turns >= max_turns:
|
238
|
+
return "Max turns reached. Task incomplete."
|
239
|
+
|
240
|
+
except Exception as e:
|
241
|
+
logger.error(f"Error in agent loop: {str(e)}")
|
242
|
+
return f"Error: {str(e)}"
|
243
|
+
|
244
|
+
|
245
|
+
async def close(self):
|
246
|
+
"""Clean up resources."""
|
247
|
+
await self.mcp_client.close()
|
@@ -0,0 +1,79 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: tinyagent-py
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Tiny Agent with MCP Client
|
5
|
+
Author-email: Mahdi Golchin <golchin@askdev.ai>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/askbudi/tinyagent
|
8
|
+
Project-URL: Bug Tracker, https://github.com/askbudi/tinyagent/issues
|
9
|
+
Project-URL: Chat, https://askdev.ai/github/askbudi/tinyagent
|
10
|
+
Requires-Python: >=3.8
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: mcp
|
14
|
+
Requires-Dist: litellm
|
15
|
+
Requires-Dist: openai
|
16
|
+
Provides-Extra: dev
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
18
|
+
Dynamic: license-file
|
19
|
+
|
20
|
+
# tinyagent
|
21
|
+
Tiny Agent: 100 lines Agent with MCP
|
22
|
+
|
23
|
+
Inspired by:
|
24
|
+
- [Tiny Agents blog post](https://huggingface.co/blog/tiny-agents)
|
25
|
+
- [12-factor-agents repository](https://github.com/humanlayer/12-factor-agents)
|
26
|
+
- Created by chatting to the source code of JS Tiny Agent using [AskDev.ai](https://askdev.ai/search)
|
27
|
+
|
28
|
+
## Quick Links
|
29
|
+
- [Build your own Tiny Agent](https://askdev.ai/github/askbudi/tinyagent)
|
30
|
+
|
31
|
+
## Overview
|
32
|
+
This is a tiny agent that uses MCP and LiteLLM to interact with a model. You have full control over the agent, you can add any tools you like from MCP and extend the agent using its event system.
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
### Using pip
|
37
|
+
```bash
|
38
|
+
pip install tinyagent
|
39
|
+
```
|
40
|
+
|
41
|
+
### Using uv
|
42
|
+
```bash
|
43
|
+
uv pip install tinyagent
|
44
|
+
```
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
```python
|
49
|
+
from tinyagent import TinyAgent
|
50
|
+
from textwrap import dedent
|
51
|
+
import asyncio
|
52
|
+
import os
|
53
|
+
|
54
|
+
async def test_agent(task, model="o4-mini", api_key=None):
|
55
|
+
# Initialize the agent with model and API key
|
56
|
+
agent = TinyAgent(
|
57
|
+
model=model, # Or any model supported by LiteLLM
|
58
|
+
api_key=os.environ.get("OPENAI_API_KEY") if not api_key else api_key # Set your API key as an env variable
|
59
|
+
)
|
60
|
+
|
61
|
+
try:
|
62
|
+
# Connect to an MCP server
|
63
|
+
# Replace with your actual server command and args
|
64
|
+
await agent.connect_to_server("npx", ["@openbnb/mcp-server-airbnb", "--ignore-robots-txt"])
|
65
|
+
|
66
|
+
# Run the agent with a user query
|
67
|
+
result = await agent.run(task)
|
68
|
+
print("\nFinal result:", result)
|
69
|
+
return result
|
70
|
+
finally:
|
71
|
+
# Clean up resources
|
72
|
+
await agent.close()
|
73
|
+
|
74
|
+
# Example usage
|
75
|
+
task = dedent("""
|
76
|
+
I need accommodation in Toronto between 15th to 20th of May. Give me 5 options for 2 adults.
|
77
|
+
""")
|
78
|
+
await test_agent(task, model="gpt-4.1-mini")
|
79
|
+
```
|
@@ -0,0 +1,11 @@
|
|
1
|
+
LICENSE
|
2
|
+
README.md
|
3
|
+
pyproject.toml
|
4
|
+
tinyagent/__init__.py
|
5
|
+
tinyagent/mcp_client.py
|
6
|
+
tinyagent/tiny_agent.py
|
7
|
+
tinyagent_py.egg-info/PKG-INFO
|
8
|
+
tinyagent_py.egg-info/SOURCES.txt
|
9
|
+
tinyagent_py.egg-info/dependency_links.txt
|
10
|
+
tinyagent_py.egg-info/requires.txt
|
11
|
+
tinyagent_py.egg-info/top_level.txt
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
tinyagent
|