Between the model's tool call and the actual function sits a registry: a table that knows which tools exist, describes them to the model, and dispatches calls by name. This is the core idea of protocols such as the Model Context Protocol (MCP), where a server lists its tools and a client calls them by name with arguments. The dispatcher must be defensive. An unknown tool name is a normal event (models hallucinate), and a tool that raises an exception must not crash the agent. Both should come back as a structured error result the model can read and react to.
Implement the class ToolRegistry.
register(name, fn, description) stores the tool. Registering an existing name raises ValueError.list_tools() returns a list of {'name': ..., 'description': ...} dicts sorted by name.call(name, args) returns {'ok': True, 'result': fn(**args)} on success.name is not registered return {'ok': False, 'error': 'unknown tool: {name}'}. If the tool raises an exception e, return {'ok': False, 'error': 'tool error: {e}'} (using str(e)). Never let an exception escape call.Look the function up with dict.get and use try/except Exception around the invocation only.
A wrong argument name raises TypeError inside fn(**args), and that is also reported as a tool error.
No results yet
Run your code to check it against the examples, or submit to run every test.