Defining tools in an MCP server
Tools are the primitive the model actually calls, so they deserve the most care. A well-defined tool is discoverable, unambiguous, and safe to invoke.
The anatomy of a tool #
Every tool needs three things: a name the model references, a description that tells the model what it does and when to use it, and an input schema (JSON Schema) that constrains the arguments.
python@mcp.tool()
def create_issue(repo: str, title: str, body: str = "") -> str:
"""Open a GitHub issue.
Use this when the user asks to file a bug or feature request.
repo must be in "owner/name" form.
"""
...
Write the description for the model #
The model chooses tools from their descriptions alone. Be prescriptive about when to call the tool, not just what it does. "Open a GitHub issue. Use this when the user asks to file a bug" beats "creates an issue." Name parameters clearly and describe each one.
Return useful results and errors #
Return content the model can act on. When something fails, return an error the model can recover from: "repo not found, check owner/name" is far more useful than "400." Good error text turns a dead end into a retry.
One tool, one job. A narrow tool with a tight schema is easier for the model to select correctly than a broad multi-purpose one. Split "manage_repo" into "create_issue", "list_issues", and so on.
Resources & further reading
- MCP specification MCPThe authoritative protocol spec: messages, capabilities, transports, and lifecycle.
- Python SDK GitHubThe official Python SDK for building MCP servers and clients.