Result classifier
from fastmcp_feedback.instrumentation import DEFAULT_RESULT_CLASSIFIER, soft_error_classifierContract
Section titled “Contract”A result classifier is (tool: str, result: Any) -> str | None.
- It runs only for calls that did not raise.
resultis the payload the record stores: the tool’s structured content when present, otherwise its content items (texts, or{"type": ...}for other kinds).- When the tool marked its result as an MCP error (
isError) without raising,resultarrives as a dict with"isError": True, alongside the payload’s own keys or with the payload under"content". Nonemeans ok. A string marks a soft error:outcome="soft_error",ok=False,error_type="SoftError", and the string, redacted and cut tomax_error_chars, becomeserror_message.- If it raises, the error is logged and the call is recorded as
ok. - It never changes what the client receives.
soft_error_classifier
Section titled “soft_error_classifier”soft_error_classifier(*, statuses=("error", "failed", "failure"), status_keys=("status", "state"), flag_keys=("ok", "success"), extra_statuses=(), error_keys=("error",)) -> ResultClassifier| Parameter | Description |
|---|---|
statuses | Values that mean failure at a status key, compared case-insensitively. |
status_keys | Keys holding a status. |
flag_keys | Keys holding a success boolean. |
extra_statuses | Added to statuses. |
error_keys | Keys whose non-empty string value marks a failure on its own. () turns the check off. |
Non-dict results are always ok. For a dict, checks run in this order and the first that applies decides:
| Order | Condition | Result | Message |
|---|---|---|---|
| 1 | a status key holds a failing status | soft error | status=failed: <detail> |
| 2 | a flag key is exactly False | soft error | ok=False: <detail> |
| 3 | isError or is_error is True | soft error | isError: <detail or content text> |
| 4 | a flag key is exactly True | ok | |
| 5 | an error key holds a non-blank string | soft error | error: <string> |
| 6 | none of the above | ok |
<detail> is the first non-empty value of error, message, detail and
reason; a dict there is searched one level down for the same keys. Lists are
skipped. A label without a detail is used alone, such as status=failed.
DEFAULT_RESULT_CLASSIFIER is soft_error_classifier() with these defaults.
classify = soft_error_classifier(extra_statuses=("unknown_target",))assert classify("render", {"status": "unknown_target", "error": "no such object"}) == "status=unknown_target: no such object"assert classify("render", {"status": "failed", "error": {"code": 7, "message": "disk full"}}) == "status=failed: disk full"assert classify("render", {"error": {"code": 1}}) is Noneassert DEFAULT_RESULT_CLASSIFIER("t", {"isError": True, "content": ["boom"]}) == "isError: boom"History
Section titled “History”| Release | Change |
|---|---|
| 2026.09.27.4 | Classification added; outcome column. |
| 2026.10.01 | error_keys check, on by default. |
| 2026.10.01.1 | An explicit True flag wins over an error string. |