2.2 KiB
description, paths
| description | paths | |
|---|---|---|
| FastMCP tool rules — sync def, string-table returns, premium gating, _log, dual dict shapes |
|
MCP tools
-
Tools return plain formatted
str, not JSON and not Pydantic models. They build human-readable ASCII blocks/tables for the LLM ("=== {name} ===", aligned standings columns, grouped rosters). This is Nike's convention — don't convert a tool to return JSON or a model to match other estate servers. Match the existing formatting. -
Tools are synchronous
def.sportsdb(requests) anddb(psycopg2) are blocking; FastMCP runs sync tools in a threadpool. Don't make themasync. Keep them plain module-level callables —/api/runinvokestool.fn(**args)directly. -
Standard tool shape:
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))(every tool here is read-only), a docstring written for Claude, resolve the team via_resolve_teamif needed, call thesportsdbclient, format text, and_log(tool, args, duration_ms)before returning. Not-found returns a friendly string ("Team '…' not found."), never an exception. -
Premium gating is two things kept in sync: the runtime check
config.SPORTSDB_KEY in ('3', '')(→ return a clear "requires a premium key" message, or degrade to cached data likeget_rosterdoes) and thetags={"premium"}on the decorator (so/api/toolsand the dashboard can flag it). Add both when a new tool needs premium data. -
Rows come in two shapes — handle both. A team/player dict is either a raw TheSportsDB dict (
strTeam,idTeam,strLeague) or a cached DB row (name,id,league_name). Read withx.get("strTeam") or x.get("name"). A cache hit and a cache miss must render identically — don't assume one shape. -
Per-external-call
try/except … passis intentional so a partial API failure yields partial results. Keep each API call individually guarded; don't merge them into one try or turn them into hard failures. -
When you add/rename a tool, update the hand-written strings too: the
mcpinstructions=and thefootball_analyst()prompt enumerate tools manually and do not auto-update (unlike/api/tools, which derives frommcp.list_tools()).