How to safely expose instance, class, and static methods as tools, resources, or prompts in Hyperia Protocol.
TL;DR — bind first, decorate second. Hyperia decorators capture callables at import‑time; if those callables still expect self or cls, the LLM will also have to supply them—and it obviously can’t. The fix is to register bound callables after objects/classes exist.
1 Why Methods Are Tricky
Python applies decorators when the function object is created—i.e. during class body execution, before you have an instance. At that moment:
def foo(self, x): … is an unbound function with the first positional parameter named self.
@classmethod is applied after other decorators in the chain (because decorators stack bottom‑to‑top).
Hence decorating directly gives Hyperia a callable that still requires self or cls.
2 Working Patterns
2.1 Instance Methods
Wrong way — the LLM will see a self arg:
from hyperia import Hyperiamcp =Hyperia("Broken")classCalc:@mcp.tool()defadd(self,a:int,b:int):# <- self leaksreturn a + b
Correct — register after instantiation:
You can also shortcut with functools.partial if you need to pre‑bind only some args.
2.2 Class Methods
Decorator order pitfalls:
Fix: define normally, then register:
2.3 Static Methods
No binding needed → direct decoration is safe:
Under the hood staticmethod converts the function into a plain callable before Hyperia sees it.
3 Bulk Registration Patterns
3.1 Self‑Registering Components
Encapsulate registration inside __init__:
Keeps your feature module cohesive; useful when distributing as pip packages.
3.2 Mixin Helpers
Provide a base class with a register(self, mcp) helper:
4 Decorator Helper Cheatsheet
Desired Component
Inline Decorator Safe?
Alternate API
Function
✔
n/a
@staticmethod
✔
mcp.add_tool(cls.method)
@classmethod
✖
mcp.add_tool(Class.method)
Instance method
✖
obj = Class(); mcp.add_tool(obj.method)
Resource template
Only with staticmethod
mcp.add_resource_fn(...)
Prompt
same rules as tool
mcp.add_prompt(...)
5 Advanced Tricks
5.1 Decorator Factory to Keep Syntax Sugary
If you love @mcp.tool() syntax but need late binding:
5.2 Partial Application for Stateful Helpers
Sometimes you need to bind some state but keep others open:
6 Summary Checklist
Never expose a naked self/cls to the LLM.
Register bound callables using .add_tool, .add_resource_fn, or .add_prompt.
Static methods are safe to decorate inline; others are not.
Use helper patterns (self‑registering classes, mixins, partials) to keep code DRY.
Following these patterns keeps your OOP design tidy and ensures Hyperia surfaces clean, intuitive APIs to LLMs and other clients.
mcp = Hyperia("CalcAPI")
class Calc:
def add(self, a: int, b: int): return a + b
calc = Calc()
# Bound method; self already captured
mcp.add_tool(calc.add)
class Doc:
@classmethod # applied last -> ok for Python, bad for Hyperia
@mcp.tool() # captures raw function BEFORE cls binding
def from_json(cls, js): return cls(**js)
class Doc:
@classmethod
def from_json(cls, js): return cls(**js)
mcp.add_tool(Doc.from_json) # Doc.from_json is a descriptor bound to class
class Math:
@staticmethod
@mcp.tool()
def mul(a: int, b: int): return a * b