{"slug": "i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraph-crewai", "title": "I built a mini agent-tool framework to actually understand how LangGraph/CrewAI register tools", "summary": "A developer built a mini agent-tool framework to understand how LangGraph and CrewAI register tools, implementing a global tool registry using Python's __init_subclass__ method. The framework automatically registers tools when subclasses are defined, eliminating the need for manual tool lists and catching missing tool names at definition time.", "body_md": "I'm going through a 16-week Agentic AI syllabus right now, and Week 1 is \"Python for Agentic Systems\" — OOP, typing, decorators. Instead of just reading about it, I built a small CLI toolkit that mimics how real agent frameworks register and run tools.\n\nThis post is about one piece of it: **how a global tool registry works using __init_subclass__**, and why agent frameworks need this pattern at all.\n\nRepo's at the bottom. Code below is real, from the actual project — not pseudocode.\n\nAgent frameworks (LangGraph, CrewAI, PydanticAI) all need the same thing: a way for an LLM or planner to discover \"what tools exist\" without you manually maintaining a list somewhere.\n\nThe naive way is a dict you update by hand:\n\n```\nTOOLS = {\n    \"search\": SearchTool,\n    \"summarize\": SummarizeTool,\n}\n```\n\nThis works until you forget to add an entry. Then your planner silently can't find a tool that exists in your codebase.\n\nHere's the actual registry from my project:\n\n```\nclass ToolRegistry:\n    __slots__ = ()\n    _tools: dict[str, type[Any]] = {}\n\n    @classmethod\n    def register(cls, name: str, tool_cls: type[Any]) -> None:\n        existing = cls._tools.get(name)\n        if existing is not None and existing is not tool_cls:\n            raise DuplicateToolError(\n                f\"tool name {name!r} is already registered by {existing.__name__}\"\n            )\n        cls._tools[name] = tool_cls\n```\n\nAnd the part that actually calls `register()`\n\n— `__init_subclass__`\n\non the base class:\n\n```\nclass BaseTool(LoggingMixin, RetryMixin, MetricsMixin, ABC):\n    def __init_subclass__(\n        cls,\n        *,\n        tool_name: str | None = None,\n        description: str = \"\",\n        streamable: bool = False,\n        abstract: bool = False,\n        **kwargs: Any,\n    ) -> None:\n        super().__init_subclass__(**kwargs)\n        if abstract:\n            return\n\n        if tool_name is None:\n            raise TypeError(f\"{cls.__name__} must define tool_name='...'\")\n\n        cls._tool_name = tool_name.strip().lower()\n        cls.description = description.strip()\n        cls._streamable = streamable\n        ToolRegistry.register(cls._tool_name, cls)\n```\n\n`__init_subclass__`\n\nfires automatically the moment Python *defines* a subclass — before you ever instantiate it. So a tool just declares itself:\n\n```\nclass SearchTool(\n    BaseTool,\n    tool_name=\"search\",\n    description=\"Searches a small in-memory knowledge base.\",\n    streamable=True,\n):\n    def execute(self, context: ToolContext) -> str:\n        ...\n```\n\nThe moment this class body is parsed, `SearchTool`\n\nis in the registry. No manual list. No import-time side-effect hacks. Forget `tool_name=`\n\nand you get a `TypeError`\n\nimmediately — not a silent miss three files away.\n\nOnce tools self-register, a CLI (or a planner LLM) can just ask \"what do you have\":\n\n``` php\ndef _list_tools() -> None:\n    for name, tool_cls in ToolRegistry.items():\n        tool = tool_cls()\n        print(f\"{name:<12} {tool.metadata['description']}\")\nbash\n$ python main.py list-tools\nsearch       Searches a small in-memory knowledge base and returns ranked notes.\nsummarize    Creates a compact extractive summary of user-provided text.\ntranslate    Translates common demo phrases to Spanish or Urdu using a local lexicon.\n```\n\nThis is structurally the same problem LangGraph and CrewAI solve with their own tool-discovery mechanisms. Different implementation, same underlying need: *a single source of truth that updates itself.*\n\nThis registry is one piece. The same codebase has:\n\n`ValidatedField`\n\n, `IdentifierField`\n\n, `IntegerRange`\n\n) validating `ToolConfig`\n\nat assignment time`BaseTool(LoggingMixin, RetryMixin, MetricsMixin, ABC)`\n\ncomposes logging, retries, and metrics without inheritance spaghetti`ParamSpec`\n\n-based decorators`@log_execution`\n\n, `@measure_time`\n\n) that wrap methods without breaking their signatures for mypy`stream()`\n\nyields tokens instead of faking it with string slicingI'll cover each of these in upcoming posts as I move through the syllabus.\n\n```\ngit clone https://github.com/Sajid0875/agentic-systems-bootcamp\ncd agent-ready-cli-toolkit\npython main.py list-tools\npython main.py describe search\npython main.py run summarize \"Agent frameworks register tools and stream results.\" --stream\n```\n\nIf you're also learning agentic systems and want to compare notes on how different frameworks (CrewAI, PydanticAI, LangGraph) handle tool registration internally, drop it in the comments — genuinely curious how close/far off this mental model is from the real implementations.", "url": "https://wpnews.pro/news/i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraph-crewai", "canonical_source": "https://dev.to/sajid0875/i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraphcrewai-register-tools-15if", "published_at": "2026-06-21 10:52:00+00:00", "updated_at": "2026-06-21 11:07:17.022525+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "ai-agents", "large-language-models"], "entities": ["LangGraph", "CrewAI", "PydanticAI", "Python"], "alternates": {"html": "https://wpnews.pro/news/i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraph-crewai", "markdown": "https://wpnews.pro/news/i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraph-crewai.md", "text": "https://wpnews.pro/news/i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraph-crewai.txt", "jsonld": "https://wpnews.pro/news/i-built-a-mini-agent-tool-framework-to-actually-understand-how-langgraph-crewai.jsonld"}}