-
Notifications
You must be signed in to change notification settings - Fork 893
[MCP] Tiny Agents in Python #3098
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
Co-authored-by: Julien Chaumond <[email protected]>
setup.py
Outdated
|
|
||
| extras["mcp"] = [ | ||
| "mcp>=1.8.0", | ||
| "colorama", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(probably not worth the extra dependency if all you need are a few colors characters sequences)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be bullish to switch to Typer instead of argparse to be honest...
It has a much greater UX, easier to maintain, auto-completion out of the box, etc. It adds a dependency but only an optional one (on huggingface_hub[mcp])
Co-authored-by: Julien Chaumond <[email protected]>
julien-c
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks super cool already! 🔥
Wauplin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! I'm pre-approving with some nits. Once we have SSE/HTTP servers implemented we should be on-par with the JS CLI to make some comms about it :)
(btw, is this a problem that we'll have two tiny-agents CLIs conflicting? Hopefully not but 🤷)
| else: | ||
| raise KeyboardInterrupt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't know how to solve that but when I launch the CLI + wait for the tools to be loaded, I need to type 4 times on CTRL+C to exit and it exits with a ton of messages. Would be good to improve DX, either here or in a follow-up PR:
> tiny-agents run julien-c/local-coder
Fetching 2 files: 100%|███████████████████████████████████████████████████████| 2/2 [00:00<00:00, 64527.75it/s]
Agent loaded with 25 tools:
• browser_close
• browser_resize
• browser_console_messages
• browser_handle_dialog
• browser_file_upload
• browser_install
• browser_press_key
• browser_navigate
• browser_navigate_back
• browser_navigate_forward
• browser_network_requests
• browser_pdf_save
• browser_take_screenshot
• browser_snapshot
• browser_click
• browser_drag
• browser_hover
• browser_type
• browser_select_option
• browser_tab_list
• browser_tab_new
• browser_tab_select
• browser_tab_close
• browser_generate_playwright_test
• browser_wait_for
» ^CInterrupted – press Ctrl+C again to quit
^Cunhandled exception during asyncio.run() shutdown
task: <Task finished name='Task-1' coro=<run_agent() done, defined at /home/wauplin/projects/huggingface_hub/src/huggingface_hub/inference/_mcp/cli.py:33> exception=ExceptionGroup('unhandled errors in a TaskGroup', [ProcessLookupError()])>
Traceback (most recent call last):
File "/home/wauplin/projects/huggingface_hub/src/huggingface_hub/inference/_mcp/cli.py", line 63, in run_agent
async with Agent(
File "/home/wauplin/projects/huggingface_hub/src/huggingface_hub/inference/_mcp/mcp_client.py", line 65, in __aexit__
await self.cleanup()
File "/home/wauplin/projects/huggingface_hub/src/huggingface_hub/inference/_mcp/mcp_client.py", line 241, in cleanup
await self.exit_stack.aclose()
File "/usr/lib/python3.10/contextlib.py", line 656, in aclose
await self.__aexit__(None, None, None)
File "/usr/lib/python3.10/contextlib.py", line 714, in __aexit__
raise exc_details[1]
File "/usr/lib/python3.10/contextlib.py", line 697, in __aexit__
cb_suppress = await cb(*exc_details)
File "/usr/lib/python3.10/contextlib.py", line 206, in __aexit__
await anext(self.gen)
File "/home/wauplin/projects/huggingface_hub/.venv310/lib/python3.10/site-packages/mcp/client/stdio/__init__.py", line 170, in stdio_client
async with (
File "/home/wauplin/projects/huggingface_hub/.venv310/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 772, in __aexit__
raise BaseExceptionGroup(
exceptiongroup.ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
^C^CException ignored in: <module 'threading' from '/usr/lib/python3.10/threading.py'>
Traceback (most recent call last):
File "/usr/lib/python3.10/threading.py", line 1537, in _shutdown
atexit_call()
File "/usr/lib/python3.10/concurrent/futures/thread.py", line 31, in _python_exit
t.join()
File "/usr/lib/python3.10/threading.py", line 1096, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.10/threading.py", line 1116, in _wait_for_tstate_lock
if lock.acquire(block, timeout):
KeyboardInterrupt:
(I do get similar output no matter when I exit but reporting this one is the most reproducible example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as discussed in private, it's a bit complicated to "exit" properly an asyncio script in this context 😕 I pushed a better way to exit in f09b70a using os._exit() but still, I believe it's not the best solution..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into it. Let's do that and leave it for a later PR then...
I thought about it but i don't think so, given i doubt many people will install both globally. |
|
let's merge! 🔥 Screen.Recording.2025-05-21.at.19.36.22.mov |
|
(failing tests unrelated) |
julien-c
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❗ 🔥 🚒
As discussed offline, this PR moves the curated collection of tiny-agents to the Hub. Agents must now be contributed to https://huggingface.co/datasets/huggingface/tiny-agents. This will be helpful to maintain the collection independently from the `@huggingface/tiny-agents` releases. It also makes them available for the incoming Python equivalent (huggingface/huggingface_hub#3098). File resolution is still the same: 1. if agent_id is a file => load it as a `agent.json` file 2. if agent_id is a directory => load from it 3. if agent_id is a subdirectory in https://huggingface.co/datasets/huggingface/tiny-agents/tree/main => load from it 4. otherwise raise exception Usage is still: ```bash npx @huggingface/tiny-agents run "julien-c/flux-schnell-generator" ```
This is an early version of Tiny Agents (https://huggingface.co/blog/tiny-agents) in Python, inspired by @julien-c's work in JS: https://github.com/huggingface/huggingface.js/tree/main/packages/tiny-agents.
What's in the PR?
tiny-agents runcommand (seesrc/huggingface_hub/inference/_mcp/cli.py) that mirros the JS CLI and supports: a folder, a single agent.json, or a builtin agent name.src/huggingface_hub/inference/_mcp/tiny_agent.py– very small orchestrator that keeps a history, enforces a max-turn limit, and plugs MCP tools into the LLM.What's missing?