Skip to content

[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

Merged
merged 22 commits into from
May 21, 2025
Merged

[MCP] Tiny Agents in Python #3098

merged 22 commits into from
May 21, 2025

Conversation

hanouticelina
Copy link
Contributor

@hanouticelina hanouticelina commented May 21, 2025

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 run command (see src/huggingface_hub/inference/_mcp/cli.py) that mirros the JS CLI and supports: a folder, a single agent.json, or a builtin agent name.
  • Agent wrapper: 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?

  • - Only stdio MCP servers are handled right now, we need to add support for SSE and HTTP MCP servers. implemented in [MCP] add support for SSE + HTTP #3099
  • - Some possible refactoring: where and how do we want to implement the CLI? ✅

@HuggingFaceDocBuilderDev

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.

setup.py Outdated
@@ -66,6 +66,7 @@ def get_version() -> str:

extras["mcp"] = [
"mcp>=1.8.0",
"colorama",
Copy link
Member

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)

Copy link
Contributor

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])

Copy link
Member

@julien-c julien-c left a 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! 🔥

@hanouticelina hanouticelina changed the title Tiny Agents in Python [MCP] Tiny Agents in Python May 21, 2025
@hanouticelina hanouticelina marked this pull request as ready for review May 21, 2025 13:55
Copy link
Contributor

@Wauplin Wauplin left a 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 🤷)

Comment on lines 58 to 59
else:
raise KeyboardInterrupt
Copy link
Contributor

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)

Copy link
Contributor Author

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..

Copy link
Contributor

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...

@julien-c
Copy link
Member

(btw, is this a problem that we'll have two tiny-agents CLIs conflicting? Hopefully not but 🤷)

I thought about it but i don't think so, given i doubt many people will install both globally.

@hanouticelina
Copy link
Contributor Author

let's merge! 🔥

Screen.Recording.2025-05-21.at.19.36.22.mov

@hanouticelina
Copy link
Contributor Author

(failing tests unrelated)

Copy link
Member

@julien-c julien-c left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ 🔥 🚒

@hanouticelina hanouticelina merged commit aee73c4 into main May 21, 2025
25 checks passed
@hanouticelina hanouticelina deleted the tiny-agent branch May 21, 2025 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants