Decorating Methods
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
orcls
, 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 namedself
.@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 Hyperia
mcp = Hyperia("Broken")
class Calc:
@mcp.tool()
def add(self, a: int, b: int): # <- self leaks
return a + b
Correct — register after instantiation:
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)
You can also shortcut with functools.partial
if you need to pre‑bind only some args.
2.2 Class Methods
Decorator order pitfalls:
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)
Fix: define normally, then register:
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
2.3 Static Methods
No binding needed → direct decoration is safe:
class Math:
@staticmethod
@mcp.tool()
def mul(a: int, b: int): return a * b
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__
:
class ReportModule:
def __init__(self, mcp: Hyperia):
mcp.add_tool(self.generate)
mcp.add_resource_fn(self.status, uri="resource://report/status")
def generate(self, title: str): ...
def status(self): return "idle"
ReportModule(mcp)
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:
class HyperiaMixin:
def register(self, mcp: Hyperia):
for name in dir(self):
attr = getattr(self, name)
if getattr(attr, "_as_hyperia_tool", False):
mcp.add_tool(attr)
class Tasks(HyperiaMixin):
def __init__(self):
self.counter = 0
@property
def _as_hyperia_tool(self): return True
def ping(self): return "pong"
4 Decorator Helper Cheatsheet
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:
from functools import wraps
def instance_tool():
def wrapper(fn):
fn._is_instance_tool = True # mark it
return fn
return wrapper
class Greeter:
@instance_tool()
def hello(self, name: str):
return f"Hi {name}!"
g = Greeter()
for name, attr in Greeter.__dict__.items():
if getattr(attr, "_is_instance_tool", False):
mcp.add_tool(getattr(g, name))
5.2 Partial Application for Stateful Helpers
Sometimes you need to bind some state but keep others open:
from functools import partial
config = {...}
def fetch(url, cfg): ...
mcp.add_tool(partial(fetch, cfg=config))
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.
Last updated