Building Your Own MCP Server for AI
Build a Useful MCP Server
Turn several REST endpoints into a small, named MCP interface instead of a 1:1 API copy.
A useful server is opinionated. Three tools that match how people talk beat thirty tools that match how the backend was generated.
Start from jobs, not paths
Write the sentences a teammate would type: “What’s open on my board?” “File a bug with this title.” “Show the last failed deploy.” Those sentences become tools. The URLs stay inside the functions.
A thin board, not every Jira field
from typing import Any
import httpx
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("tiny-board")
API = "https://httpbin.org" # stand-in; swap for your tracker
@mcp.tool()
def list_open_issues(limit: int = 5) -> dict[str, Any]:
'''List a few open issues. Keep limit small so the model is not flooded.'''
limit = max(1, min(limit, 10))
# Real code: GET /issues?state=open&per_page=limit
return {"ok": True, "issues": [{"id": 1, "title": "Example", "state": "open"}][:limit]}
@mcp.tool()
def file_bug(title: str, body: str) -> dict[str, Any]:
'''Create one bug. Title is required; body can be short.'''
title = title.strip()
if len(title) < 8:
return {"ok": False, "error": "title must be at least 8 characters"}
# Real code: POST /issues with {title, body, labels: ["bug"]}
return {"ok": True, "id": 99, "title": title}You skipped search syntax, custom fields, and delete. That is design, not laziness. Designing MCP for AI is the longer argument.
Shared HTTP client
Create one httpx.Client (or a helper) with base URL, token, and timeout. Do not open a new client with a slightly different User-Agent in every tool.
Check it
- List tools in the host. You should see two names a human would pick, not
get_issuesandpost_issues. - Call
file_bugwith a two-word title and confirm the validation error. - Write a third tool only if you have a third job. Resist “and also list closed.”