{"id":"bbdef8a5-6e63-4abe-ab45-f8f23b5209c9","shortId":"fmvky6","kind":"skill","title":"MCP Server Builder","tagline":"Step-by-step guidance for creating a new MCP server with FastMCP or the TypeScript SDK — tool definitions, resource handlers, error responses, and usage examples.","description":"# MCP Server Builder\n\n## What this skill does\n\nThis skill walks you through building a Model Context Protocol (MCP) server from scratch. MCP servers expose tools, resources, and prompts to AI agents (Claude, Cursor, Cline, etc.). This skill covers both Python (FastMCP) and TypeScript (official SDK) implementations, with production-ready patterns for error handling, input validation, and tool description writing.\n\n## How to use\n\n### Claude Code / Cline\n\nCopy this file to `.agents/skills/mcp-server-builder/SKILL.md` in your project root.\n\nThen ask:\n- *\"Use the MCP Server Builder to create a server that exposes our internal database as tools.\"*\n- *\"Build an MCP server in Python that wraps our REST API.\"*\n\nProvide:\n- What capabilities you want to expose (tools, resources, or both)\n- Language preference (Python or TypeScript)\n- Whether it will run locally (stdio) or as a remote server (HTTP)\n- What systems it needs to connect to\n\n### Cursor / Codex\n\nDescribe the tools you want to expose alongside these instructions.\n\n## The Prompt / Instructions for the Agent\n\nWhen asked to build an MCP server, produce the following:\n\n### Step 1 — Choose transport\n\n| Use case | Transport |\n|---|---|\n| Local tool for one developer | stdio (local process) |\n| Team-shared server | Streamable HTTP (remote) |\n| Claude Desktop integration | stdio |\n| Multi-user / production | HTTP with auth |\n\n### Step 2a — Python implementation (FastMCP)\n\n```python\n# Install: pip install fastmcp\nfrom fastmcp import FastMCP\nfrom pydantic import BaseModel, Field\n\nmcp = FastMCP(\"my-server\")\n\n# --- Tool definition ---\nclass SearchInput(BaseModel):\n    query: str = Field(description=\"The search query to run\")\n    limit: int = Field(default=10, ge=1, le=50, description=\"Max results to return\")\n\n@mcp.tool()\nasync def search_documents(input: SearchInput) -> str:\n    \"\"\"\n    Search the document database for relevant content.\n\n    Use this when the user asks to find, look up, or search for documents.\n    Returns a formatted list of matching documents with titles and summaries.\n    \"\"\"\n    try:\n        results = await db.search(input.query, limit=input.limit)\n        if not results:\n            return \"No documents found matching your query.\"\n        return \"\\n\".join(f\"- {r.title}: {r.summary}\" for r in results)\n    except Exception as e:\n        return f\"Error searching documents: {str(e)}\"\n\n# --- Resource definition (read-only data) ---\n@mcp.resource(\"config://app-settings\")\nasync def get_app_settings() -> str:\n    \"\"\"Returns the current application configuration.\"\"\"\n    return json.dumps(load_config(), indent=2)\n\n# Run with stdio (for local / Claude Desktop)\nif __name__ == \"__main__\":\n    mcp.run()\n\n# Run with HTTP (for remote / team use)\n# mcp.run(transport=\"streamable-http\", host=\"0.0.0.0\", port=8000)\n```\n\n### Step 2b — TypeScript implementation (official SDK)\n\n```typescript\n// Install: npm install @modelcontextprotocol/sdk zod\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nconst server = new Server(\n  { name: \"my-server\", version: \"1.0.0\" },\n  { capabilities: { tools: {} } }\n);\n\n// List available tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n  tools: [{\n    name: \"search_documents\",\n    description: \"Search the document database. Use when user asks to find or look up documents.\",\n    inputSchema: {\n      type: \"object\",\n      properties: {\n        query: { type: \"string\", description: \"Search query\" },\n        limit: { type: \"number\", description: \"Max results (1-50)\", default: 10 }\n      },\n      required: [\"query\"]\n    }\n  }]\n}));\n\n// Handle tool calls\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n  if (request.params.name === \"search_documents\") {\n    const { query, limit = 10 } = request.params.arguments as { query: string; limit?: number };\n\n    try {\n      const results = await searchDB(query, limit);\n      return {\n        content: [{ type: \"text\", text: formatResults(results) }]\n      };\n    } catch (error) {\n      return {\n        content: [{ type: \"text\", text: `Error: ${error.message}` }],\n        isError: true\n      };\n    }\n  }\n  throw new Error(`Unknown tool: ${request.params.name}`);\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n\n### Step 3 — Write effective tool descriptions\n\nTool descriptions are what the model reads to decide when and how to use your tool. Write them like documentation for a smart but unfamiliar developer:\n\n**Bad description:**\n```\n\"Searches documents\"\n```\n\n**Good description:**\n```\n\"Search the internal document database by keyword or semantic query.\nUse this when the user asks to find, look up, retrieve, or search for\nany documents, files, or records. Returns up to `limit` results with\ntitle, author, date, and a 2-sentence summary. For best results, use\nnatural language queries rather than boolean operators.\"\n```\n\nRules for good tool descriptions:\n- Say **when to use** the tool (\"Use this when...\")\n- Describe **what it returns** precisely\n- Mention **limitations** (max results, supported formats)\n- Use natural language — the model reads these, not a parser\n\n### Step 4 — Error handling pattern\n\nAlways return errors as text content, never throw unhandled exceptions:\n\n```python\n@mcp.tool()\nasync def get_user(user_id: str) -> str:\n    \"\"\"Fetch a user record by ID.\"\"\"\n    if not user_id.strip():\n        return \"Error: user_id cannot be empty\"\n\n    try:\n        user = await db.get_user(user_id)\n        if not user:\n            return f\"No user found with ID '{user_id}'\"\n        return user.to_json()\n    except DatabaseError as e:\n        return f\"Database error: {str(e)}\"\n    except Exception as e:\n        return f\"Unexpected error fetching user: {str(e)}\"\n```\n\n### Step 5 — Claude Desktop config\n\nTo test locally with Claude Desktop, add to `~/Library/Application Support/Claude/claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"my-server\": {\n      \"command\": \"python\",\n      \"args\": [\"/path/to/server.py\"],\n      \"env\": {\n        \"DATABASE_URL\": \"postgresql://...\"\n      }\n    }\n  }\n}\n```\n\nFor TypeScript: `\"command\": \"node\", \"args\": [\"/path/to/dist/server.js\"]`\n\n### Step 6 — Production checklist\n\nBefore deploying a shared MCP server:\n- [ ] All tool inputs validated with Pydantic / Zod schemas\n- [ ] All errors return descriptive text (no unhandled exceptions)\n- [ ] Tool descriptions explain when AND how to use each tool\n- [ ] Sensitive operations (write/delete) require explicit confirmation parameters\n- [ ] Rate limiting implemented if tools call external APIs\n- [ ] Authentication added if server is exposed over HTTP\n- [ ] Tool names are unique, lowercase, underscore-separated","tags":["mcp","server","builder","openagentskills","notysoty","agent-skills","claude","claude-code","claude-skills","cline","cursor","llm"],"capabilities":["skill","source-notysoty","skill-mcp-server-builder","topic-agent-skills","topic-claude","topic-claude-code","topic-claude-skills","topic-cline","topic-cursor","topic-llm","topic-llm-skills","topic-skills"],"categories":["openagentskills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/Notysoty/openagentskills/mcp-server-builder","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add Notysoty/openagentskills","source_repo":"https://github.com/Notysoty/openagentskills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (6,907 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:13:22.878Z","embedding":null,"createdAt":"2026-05-18T13:20:44.125Z","updatedAt":"2026-05-18T19:13:22.878Z","lastSeenAt":"2026-05-18T19:13:22.878Z","tsv":"'-50':497 '/library/application':786 '/path/to/dist/server.js':805 '/path/to/server.py':796 '0.0.0.0':411 '1':198,274,496 '1.0.0':452 '10':272,499,516 '2':386,639 '2a':231 '2b':415 '3':562 '4':689 '5':774 '50':276 '6':807 '8000':413 'ad':858 'add':784 'agent':60,186 'agents/skills/mcp-server-builder/skill.md':100 'ai':59 'alongsid':178 'alway':693 'api':133,856 'app':368,373 'app-set':367 'applic':379 'arg':795,804 'ask':106,188,302,473,614 'async':283,370,460,507,705 'auth':229 'authent':857 'author':635 'avail':456 'await':324,526,558,731 'bad':593 'basemodel':247,258 'best':643 'boolean':651 'build':42,123,190 'builder':3,32,111 'call':504,854 'calltoolrequestschema':435,506 'cannot':726 'capabl':136,453 'case':202 'catch':537 'checklist':809 'choos':199 'class':256 'claud':61,93,219,392,775,782 'cline':63,95 'code':94 'codex':170 'command':793,802 'config':384,777 'configur':380 'confirm':847 'connect':167 'const':443,513,524,554 'content':296,531,540,698 'context':45 'copi':96 'cover':67 'creat':10,113 'current':378 'cursor':62,169 'data':365 'databas':120,293,469,603,757,798 'databaseerror':752 'date':636 'db.get':732 'db.search':325 'decid':575 'def':284,371,706 'default':271,498 'definit':22,255,361 'deploy':811 'describ':171,667 'descript':88,262,277,465,487,493,566,568,594,598,657,827,833 'desktop':220,393,776,783 'develop':208,592 'document':286,292,310,317,334,357,464,468,479,512,586,596,602,624 'e':352,359,754,760,764,772 'effect':564 'empti':728 'env':797 'error':25,82,355,538,544,550,690,695,723,758,768,825 'error.message':545 'etc':64 'exampl':29 'except':349,350,702,751,761,762,831 'explain':834 'explicit':846 'expos':53,117,140,177,862 'extern':855 'f':342,354,740,756,766 'fastmcp':16,70,234,239,241,243,250 'fetch':713,769 'field':248,261,270 'file':98,625 'find':304,475,616 'follow':196 'format':313,677 'formatresult':535 'found':335,743 'ge':273 'get':372,707 'good':597,655 'guidanc':8 'handl':83,502,691 'handler':24 'host':410 'http':161,217,227,400,409,864 'id':710,718,725,735,745,747 'implement':75,233,417,851 'import':242,246,426,430,434,439 'indent':385 'input':84,287,818 'input.limit':328 'input.query':326 'inputschema':480 'instal':236,238,421,423 'instruct':180,183 'int':269 'integr':221 'intern':119,601 'iserror':546 'join':341 'json':750,788 'json.dumps':382 'keyword':605 'languag':145,647,680 'le':275 'like':585 'limit':268,327,490,515,521,529,631,673,850 'list':314,455 'listtoolsrequestschema':436,459 'load':383 'local':154,204,210,391,780 'look':305,477,617 'lowercas':869 'main':396 'match':316,336 'max':278,494,674 'mcp':1,13,30,47,51,109,125,192,249,814 'mcp.resource':366 'mcp.run':397,405 'mcp.tool':282,704 'mcpserver':789 'mention':672 'model':44,572,682 'modelcontextprotocol/sdk':424 'modelcontextprotocol/sdk/server/index.js':429 'modelcontextprotocol/sdk/server/stdio.js':433 'modelcontextprotocol/sdk/types.js':438 'multi':224 'multi-us':223 'my-serv':251,448,790 'n':340 'name':395,447,462,866 'natur':646,679 'need':165 'never':699 'new':12,445,549,556 'node':803 'npm':422 'number':492,522 'object':482 'offici':73,418 'one':207 'oper':652,843 'paramet':848 'parser':687 'pattern':80,692 'pip':237 'port':412 'precis':671 'prefer':146 'process':211 'produc':194 'product':78,226,808 'production-readi':77 'project':103 'prompt':57,182 'properti':483 'protocol':46 'provid':134 'pydant':245,821 'python':69,128,147,232,235,703,794 'queri':259,265,338,484,489,501,514,519,528,608,648 'r':346 'r.summary':344 'r.title':343 'rate':849 'rather':649 'read':363,573,683 'read-on':362 'readi':79 'record':627,716 'relev':295 'remot':159,218,402 'request':508 'request.params.arguments':517 'request.params.name':510,553 'requir':500,845 'resourc':23,55,142,360 'respons':26 'rest':132 'result':279,323,331,348,495,525,536,632,644,675 'retriev':619 'return':281,311,332,339,353,376,381,530,539,628,670,694,722,739,748,755,765,826 'root':104 'rule':653 'run':153,267,387,398 'say':658 'schema':823 'scratch':50 'sdk':20,74,419 'search':264,285,290,308,356,463,466,488,511,595,599,621 'searchdb':527 'searchinput':257,288 'semant':607 'sensit':842 'sentenc':640 'separ':872 'server':2,14,31,48,52,110,115,126,160,193,215,253,427,444,446,450,792,815,860 'server.connect':559 'server.setrequesthandler':458,505 'set':369,374 'share':214,813 'skill':35,38,66 'skill-mcp-server-builder' 'smart':589 'source-notysoty' 'stdio':155,209,222,389 'stdioservertransport':431,557 'step':5,7,197,230,414,561,688,773,806 'step-by-step':4 'str':260,289,358,375,711,712,759,771 'streamabl':216,408 'streamable-http':407 'string':486,520 'summari':321,641 'support':676 'support/claude/claude_desktop_config.json':787 'system':163 'team':213,403 'team-shar':212 'test':779 'text':533,534,542,543,697,828 'throw':548,700 'titl':319,634 'tool':21,54,87,122,141,173,205,254,454,457,461,503,552,565,567,582,656,663,817,832,841,853,865 'topic-agent-skills' 'topic-claude' 'topic-claude-code' 'topic-claude-skills' 'topic-cline' 'topic-cursor' 'topic-llm' 'topic-llm-skills' 'topic-skills' 'transport':200,203,406,555,560 'tri':322,523,729 'true':547 'type':481,485,491,532,541 'typescript':19,72,149,416,420,801 'underscor':871 'underscore-separ':870 'unexpect':767 'unfamiliar':591 'unhandl':701,830 'uniqu':868 'unknown':551 'url':799 'usag':28 'use':92,107,201,297,404,470,580,609,645,661,664,678,839 'user':225,301,472,613,708,709,715,724,730,733,734,738,742,746,770 'user.to':749 'user_id.strip':721 'valid':85,819 'version':451 'walk':39 'want':138,175 'whether':150 'wrap':130 'write':89,563,583 'write/delete':844 'z':440 'zod':425,442,822","prices":[{"id":"1d30fe64-039a-4b32-9262-2e278eb7a011","listingId":"bbdef8a5-6e63-4abe-ab45-f8f23b5209c9","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"Notysoty","category":"openagentskills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:20:44.125Z"}],"sources":[{"listingId":"bbdef8a5-6e63-4abe-ab45-f8f23b5209c9","source":"github","sourceId":"Notysoty/openagentskills/mcp-server-builder","sourceUrl":"https://github.com/Notysoty/openagentskills/tree/main/skills/mcp-server-builder","isPrimary":false,"firstSeenAt":"2026-05-18T13:20:44.125Z","lastSeenAt":"2026-05-18T19:13:22.878Z"}],"details":{"listingId":"bbdef8a5-6e63-4abe-ab45-f8f23b5209c9","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"Notysoty","slug":"mcp-server-builder","github":{"repo":"Notysoty/openagentskills","stars":8,"topics":["agent-skills","claude","claude-code","claude-skills","cline","cursor","llm","llm-skills","skills"],"license":"mit","html_url":"https://github.com/Notysoty/openagentskills","pushed_at":"2026-03-28T06:50:19Z","description":"A  community-driven library of reusable AI agent skills for Claude Code, Cursor, Codex, Cline, and more.","skill_md_sha":"3d0021bdb97032de50a14b5d70152e8b264990e5","skill_md_path":"skills/mcp-server-builder/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/Notysoty/openagentskills/tree/main/skills/mcp-server-builder"},"layout":"multi","source":"github","category":"openagentskills","frontmatter":{"name":"MCP Server Builder","description":"Step-by-step guidance for creating a new MCP server with FastMCP or the TypeScript SDK — tool definitions, resource handlers, error responses, and usage examples."},"skills_sh_url":"https://skills.sh/Notysoty/openagentskills/mcp-server-builder"},"updatedAt":"2026-05-18T19:13:22.878Z"}}