{"slug": "how-to-create-an-mcp-server-tutorial", "title": "How to Create an MCP Server: Tutorial", "summary": "A developer has published a tutorial explaining how to create a Model Context Protocol (MCP) server, which allows AI assistants like Kiro, Codex, and Claude to interact with external systems. The tutorial uses a fictional TodoHub API to demonstrate how MCP servers provide an AI-friendly abstraction over REST APIs, translating simple tool inputs into complex API calls. It includes configuration examples using an mcp.json file and explains the architecture of AI agent, MCP client, and MCP server.", "body_md": "**Model Context Protocol (MCP)** allows an AI assistant such as Kiro, Codex, Claude, or another MCP-compatible agent to interact with external systems in a structured way.\n\nA useful mental model is:\n\n``` php\nAI Agent --> MCP Client --> MCP Server -->  \n\nExternal API / Database / Application\n```\n\nFor this example, assume we have an internal Todo Management API called **TodoHub**.\n\nTodoHub provides REST APIs such as:\n\n```\nGET  /todos/123\nPOST /todos\nPUT  /todos/123\nPOST /todos/123/comments\n```\n\nWe want an AI agent to understand requests such as:\n\nShow todo 123\n\nor:\n\nCreate a high-priority todo for fixing the login issue.\n\nOur MCP server acts as the bridge between the AI agent and the TodoHub API.\n\nOur architecture will look like this:\n\n``` php\nUser  ----> \"Show todo 123\"\n\nAI Agent\n(Kiro / Codex / Claude)  ---->  MCP tool call\n\nTodoHub MCP Server ---->  HTTP REST call  ---> TodoHub API\n```\n\nThe MCP server exposes tools such as:\n\n```\nget_todo\ncreate_todo\nupdate_todo\nadd_comment\n```\n\nThese are **MCP tools**.\n\nThe AI does not need to know exactly how the underlying REST API works.\n\nIt only needs to understand the tool and its input:\n\n```\nTool: get_todo\n\nInput:\n  todo_id\n```\n\nThe MCP server handles the actual API communication.\n\nFor example:\n\n``` php\nAI  ----> get_todo(todo_id=123)\n |\n |-----> MCP Server ---> GET /api/todos/123\n ----> TodoHub\n```\n\nThis distinction is important.\n\nYou might wonder:\n\nWhy don't we simply give the AI our REST API?\n\nThe reason is that an MCP server provides the AI with a **cleaner, AI-friendly abstraction** over the underlying API.\n\nYour REST API might require something like:\n\n```\nPOST /api/v2/workitems\n```\n\nwith a request body:\n\n```\n{\n  \"subject\": \"...\",\n  \"type_id\": 7,\n  \"priority_id\": 3,\n  \"workspace_id\": 19,\n  \"creator\": 758\n}\n```\n\nHowever, exposing all these internal implementation details to the AI is unnecessary.\n\nInstead, the MCP tool could expose a much simpler interface:\n\n```\ncreate_todo(\n    title,\n    description,\n    priority\n)\n```\n\nThe MCP server translates the AI-friendly parameters into the parameters required by the internal application.\n\nFor example:\n\n```\npriority = \"high\"\n        │\n        ▼\n   MCP Server\n        │\n        ▼\npriority_id = 3\n```\n\nSo the architecture becomes:\n\n```\nAI-friendly parameters\n        │\n        ▼\n    MCP Server\n        │\n        ▼\nInternal application parameters\n        │\n        ▼\n     REST API\n```\n\nThis keeps implementation details away from the AI and gives the AI a simpler interface to work with.\n\nAn MCP server can expose different tools for different operations.\n\nFor our TodoHub example:\n\n| MCP Tool | Purpose |\n|---|---|\n`get_todo` |\nRetrieve a todo |\n`create_todo` |\nCreate a new todo |\n`update_todo` |\nUpdate an existing todo |\n`add_comment` |\nAdd a comment to a todo |\n\nFor example:\n\n`get_todo`\n\n```\nInput:\n  todo_id: integer\n```\n\n`create_todo`\n\n```\nInput:\n  title: string\n  description: string\n  priority: string\n```\n\nThe AI can then select the appropriate tool based on the user's request.\n\nThe AI agent needs to know how to start and communicate with the MCP server.\n\nFor example, we can create an `mcp.json`\n\nconfiguration file:\n\n```\n{\n  \"$schema\": \"https://agent-plugins.org/schemas/1.0.0/mcp.schema.json\",\n\n  \"mcpServers\": {\n    \"todohub\": {\n      \"type\": \"stdio\",\n\n      \"command\": \"uvx\",\n\n      \"args\": [\n        \"--from\",\n        \"mcp-todohub\",\n        \"mcp-todohub\"\n      ],\n\n      \"env\": {\n        \"TODOHUB_URL\": \"https://todos.example.com\",\n        \"TODOHUB_API_KEY\": \"xxxxx\"\n      }\n    }\n  }\n}\n```\n\nThe important parts are:\n\n```\nmcpServers\n    │\n    └── todohub\n          │\n          |___ SKILL.md\n          ├── type\n          ├── command\n          ├── args\n          └── env\n```\n\nThe configuration tells the AI client:\n\n`todohub`\n\nis available.`stdio`\n\n.`uvx`\n\nis used to start the server.Let's follow one complete request.\n\nShow me todo 123.\n\nThe AI determines that the user wants information about a todo.\n\n```\nIntent:\nRetrieve todo information\n\nTodo ID:\n123\n```\n\nThe MCP server has advertised tools such as:\n\n```\nget_todo(todo_id: int)\ncreate_todo(...)\nupdate_todo(...)\nadd_comment(...)\n```\n\nThe AI chooses:\n\n```\nget_todo\n```\n\nwith:\n\n```\ntodo_id = 123\n```\n\nConceptually, the request looks like:\n\n```\n{\n  \"name\": \"get_todo\",\n  \"arguments\": {\n    \"todo_id\": 123\n  }\n}\n```\n\nThe MCP server receives the request and executes something equivalent to:\n\n```\nget_todo(123)\n```\n\nThe MCP server then communicates with TodoHub:\n\n```\nGET https://todos.example.com/api/todos/123\n```\n\nTodoHub returns:\n\n```\n{\n  \"id\": 123,\n  \"title\": \"Payment timeout\",\n  \"status\": \"In Progress\"\n}\n```\n\nThe MCP server sends the result back to the AI:\n\n```\nTodoHub\n   ↓\nMCP Server\n   ↓\nAI\n```\n\nThe AI can now respond to the user:\n\nTask #123 is\n\n\"Payment timeout\"and is currentlyIn Progress.\n\nPutting everything together:\n\n```\nUser\n │\n │ \"Show me todo 123\"\n ▼\nAI Agent\n │\n │ Understands intent\n ▼\nSelects MCP Tool\n │\n │ get_todo(todo_id=123)\n ▼\nMCP Server\n │\n │ Translates tool input\n ▼\nREST API\n │\n │ GET /api/todos/123\n ▼\nTodoHub\n │\n │ Returns JSON\n ▼\nMCP Server\n │\n │ Returns structured result\n ▼\nAI Agent\n │\n │ Generates natural-language response\n ▼\nUser\n```\n\nThe key idea is:\n\nMCP provides a standardized bridge between an AI agent and external systems.\n\nThe AI works with meaningful tools such as `get_todo`\n\nand `create_todo`\n\n, while the MCP server takes care of authentication, API calls, parameter translation, and other implementation details.\n\nThat is the complete MCP cycle.\n\n`SKILL.md`\n\nAdditionally, we can have a `SKILL.md`\n\nfile under the MCP project directory structure mentioned above.\n\nThis becomes particularly useful when the MCP tool needs **business context or parameter-building guidance that cannot be expressed cleanly through the tool schema alone**.\n\n`SKILL.md`\n\nvs MCP Server\nAn important distinction is:\n\n| Component | Purpose |\n|---|---|\nMCP Tool Definition |\nTells the AI what the tool does and what parameters it accepts. |\n`SKILL.md` |\nProvides additional instructions, context, rules, examples, and parameter-building guidance for the agent. |\nMCP Server Code |\nValidates and translates the parameters before making the actual REST API call. |\n\nA `SKILL.md`\n\ngenerally contains:\n\n```\n→ Business context\n→ How to construct parameters\n→ Business rules\n→ Examples\n```\n\nFor example, the MCP tool might simply define:\n\n```\ncreate_task(\n    title,\n    description,\n    priority\n)\n```\n\nWhile `SKILL.md`\n\ncan explain **how the AI should derive those parameters from the user's request**, including business rules and examples.\n\n`SKILL.md`\n\nMaintainable\nIf `SKILL.md`\n\nbecomes too large, we can split the content into multiple Markdown files and organize them under a `references`\n\ndirectory.\n\nFor example:\n\n```\ntaskhub-mcp/\n├── SKILL.md\n├── server.py\n├── tools/\n│   ├── get_task.py\n│   ├── create_task.py\n│   ├── update_task.py\n│   └── add_comment.py\n└── references/\n    ├── task-creation.md\n    ├── priority-rules.md\n    └── business-rules.md\n```\n\nThis keeps the main `SKILL.md`\n\nconcise while allowing more detailed business context to be maintained separately.\n\n**Happy reading!**", "url": "https://wpnews.pro/news/how-to-create-an-mcp-server-tutorial", "canonical_source": "https://dev.to/santattech/how-to-create-an-mcp-server-tutorial-5dj5", "published_at": "2026-08-23 05:27:07+00:00", "updated_at": "2026-08-23 06:13:28.268767+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "artificial-intelligence"], "entities": ["Kiro", "Codex", "Claude", "TodoHub", "MCP"], "alternates": {"html": "https://wpnews.pro/news/how-to-create-an-mcp-server-tutorial", "markdown": "https://wpnews.pro/news/how-to-create-an-mcp-server-tutorial.md", "text": "https://wpnews.pro/news/how-to-create-an-mcp-server-tutorial.txt", "jsonld": "https://wpnews.pro/news/how-to-create-an-mcp-server-tutorial.jsonld"}}