I built a mini agent-tool framework to actually understand how LangGraph/CrewAI register tools 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. 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. This 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. Repo's at the bottom. Code below is real, from the actual project — not pseudocode. Agent 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. The naive way is a dict you update by hand: TOOLS = { "search": SearchTool, "summarize": SummarizeTool, } This works until you forget to add an entry. Then your planner silently can't find a tool that exists in your codebase. Here's the actual registry from my project: class ToolRegistry: slots = tools: dict str, type Any = {} @classmethod def register cls, name: str, tool cls: type Any - None: existing = cls. tools.get name if existing is not None and existing is not tool cls: raise DuplicateToolError f"tool name {name r} is already registered by {existing. name }" cls. tools name = tool cls And the part that actually calls register — init subclass on the base class: class BaseTool LoggingMixin, RetryMixin, MetricsMixin, ABC : def init subclass cls, , tool name: str | None = None, description: str = "", streamable: bool = False, abstract: bool = False, kwargs: Any, - None: super . init subclass kwargs if abstract: return if tool name is None: raise TypeError f"{cls. name } must define tool name='...'" cls. tool name = tool name.strip .lower cls.description = description.strip cls. streamable = streamable ToolRegistry.register cls. tool name, cls init subclass fires automatically the moment Python defines a subclass — before you ever instantiate it. So a tool just declares itself: class SearchTool BaseTool, tool name="search", description="Searches a small in-memory knowledge base.", streamable=True, : def execute self, context: ToolContext - str: ... The moment this class body is parsed, SearchTool is in the registry. No manual list. No import-time side-effect hacks. Forget tool name= and you get a TypeError immediately — not a silent miss three files away. Once tools self-register, a CLI or a planner LLM can just ask "what do you have": php def list tools - None: for name, tool cls in ToolRegistry.items : tool = tool cls print f"{name:<12} {tool.metadata 'description' }" bash $ python main.py list-tools search Searches a small in-memory knowledge base and returns ranked notes. summarize Creates a compact extractive summary of user-provided text. translate Translates common demo phrases to Spanish or Urdu using a local lexicon. This 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. This registry is one piece. The same codebase has: ValidatedField , IdentifierField , IntegerRange validating ToolConfig at assignment time BaseTool LoggingMixin, RetryMixin, MetricsMixin, ABC composes logging, retries, and metrics without inheritance spaghetti ParamSpec -based decorators @log execution , @measure time that wrap methods without breaking their signatures for mypy stream yields tokens instead of faking it with string slicingI'll cover each of these in upcoming posts as I move through the syllabus. git clone https://github.com/Sajid0875/agentic-systems-bootcamp cd agent-ready-cli-toolkit python main.py list-tools python main.py describe search python main.py run summarize "Agent frameworks register tools and stream results." --stream If 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.