Publishing and the MCP Registry
บททดสอบบรรทัดเดียว
หัวข้อที่มีชื่อว่า “บททดสอบบรรทัดเดียว”local MCP server ถูกติดตั้งด้วย config entry ของ host ไม่ใช่ด้วยคนที่รัน npm install Claude Code, Claude Desktop และ editor ส่วนใหญ่ยินดีรันคำสั่งอะไรก็ตามที่คุณให้ ดังนั้นเกณฑ์ของคำว่า “ติดตั้งได้” คือ:
npx -y @acme/mcp-weather# oruvx mcp-weatherถ้าคำสั่งนั้นเริ่ม server ของคุณบน stdio และไม่มีอะไรอื่น print ลง stdout คุณก็ติดตั้งได้แล้ว ทุกอย่างในบทนี้รับใช้บรรทัดนั้น แล้วค่อยทำให้บรรทัดนั้น หาเจอได้
flowchart LR r["your repo"] --> p["npm or PyPI package"] p --> reg["MCP Registry: metadata only"] reg --> h["host config: claude mcp add ..."] p --> h h --> run["npx -y or uvx starts the server"]
แพ็กสำหรับ npm
หัวข้อที่มีชื่อว่า “แพ็กสำหรับ npm”{ "name": "@acme/mcp-weather", "version": "1.2.0", "description": "An MCP server for weather information.", "mcpName": "io.github.acme/weather", "type": "module", "bin": { "mcp-weather": "dist/index.js" }, "files": ["dist"], "repository": { "type": "git", "url": "https://github.com/acme/mcp-weather.git" }, "engines": { "node": ">=22" }, "dependencies": { "@modelcontextprotocol/server": "^2.0.0", "zod": "^4.0.0" }}#!/usr/bin/env node// dist/index.js — the bin entryimport { McpServer } from '@modelcontextprotocol/server';import { serveStdio } from '@modelcontextprotocol/server/stdio';import * as z from 'zod/v4';
serveStdio(() => { const server = new McpServer({ name: 'weather', version: '1.2.0' }); server.registerTool( 'get-forecast', { description: 'Get the weather forecast for a city', inputSchema: z.object({ city: z.string() }) }, async ({ city }) => ({ content: [{ type: 'text', text: `Sunny in ${city} all week.` }] }), ); return server;});[project]name = "mcp-weather"version = "1.2.0"description = "An MCP server for weather information."requires-python = ">=3.11"dependencies = ["mcp>=2.0"]
[project.scripts]mcp-weather = "mcp_weather.server:main"
[build-system]requires = ["hatchling"]build-backend = "hatchling.build"from mcp.server import MCPServer
mcp = MCPServer("weather")
@mcp.tool()def get_forecast(city: str) -> str: """Get the weather forecast for a city.""" return f"Sunny in {city} all week."
def main() -> None: mcp.run() # stdio by defaultchecklist สำหรับทั้งสอง ecosystem:
- entry point รัน stdio เป็น default โดยไม่ต้องใส่ flag เพื่อให้
npx -y/uvxใช้ได้เลย - มี shebang และ executable bit (
binscript บน npm;[project.scripts]บน PyPI) - ไม่มี output ลง stdout นอกจาก protocol message banner ตอน startup ไป stderr หรือไม่ต้องมีเลย
- config มาจาก environment variable (
WEATHER_API_KEY) ที่ทุก host inject ได้ ไม่ใช่จาก prompt แบบ interactive mcpNameในpackage.jsonคือชื่อใน registry ที่คุณจะใช้ทีหลัง registry จะตรวจว่า package ที่ publish มี field นี้
publish ตามปกติ: npm publish --access public หรือ uv build && uv publish
README คือ UI สำหรับติดตั้ง
หัวข้อที่มีชื่อว่า “README คือ UI สำหรับติดตั้ง”host ไม่อ่าน README ของคุณ แต่คนที่ตั้งค่า host อ่าน และเขาจะ paste จากมัน ให้วาง config block ที่พร้อมรันไว้ใกล้ ๆ ด้านบนสำหรับ host หลักแต่ละตัว:
# Claude Codeclaude mcp add --transport stdio weather \ --env WEATHER_API_KEY=your-key \ -- npx -y @acme/mcp-weather{ "mcpServers": { "weather": { "command": "npx", "args": ["-y", "@acme/mcp-weather"], "env": { "WEATHER_API_KEY": "your-key" } } }}จากนั้นระบุ environment variable ทุกตัวว่าจำเป็นหรือไม่และเป็นความลับหรือไม่, tool ที่ server expose (ชื่อและคำอธิบายหนึ่งบรรทัด) และ scope หรือ permission ที่ตัว remote ต้องการ ผู้อ่านควรตัดสินใจได้ว่า จะติดตั้งหรือไม่ จาก README อย่างเดียว
MCP Registry ทางการ
หัวข้อที่มีชื่อว่า “MCP Registry ทางการ”registry ที่ https://registry.modelcontextprotocol.io คือ catalogue ของ metadata ตอนนี้ยังอยู่ในสถานะ preview: มันเก็บ server.json ที่บอกว่า server อยู่ที่ไหน ไม่ได้เก็บ artifact เอง package ของคุณยังอยู่บน npm, PyPI หรือ container registry host และ directory ต่าง ๆ query registry เพื่อค้นหา server
1. ติดตั้ง mcp-publisher
หัวข้อที่มีชื่อว่า “1. ติดตั้ง mcp-publisher”brew install mcp-publisher# or the release binary:curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" \ | tar xz mcp-publisher && sudo mv mcp-publisher /usr/local/bin/2. สร้างและแก้ server.json
หัวข้อที่มีชื่อว่า “2. สร้างและแก้ server.json”mcp-publisher init{ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.acme/weather", "description": "An MCP server for weather information.", "repository": { "url": "https://github.com/acme/mcp-weather", "source": "github" }, "version": "1.2.0", "packages": [ { "registryType": "npm", "identifier": "@acme/mcp-weather", "version": "1.2.0", "transport": { "type": "stdio" }, "environmentVariables": [ { "name": "WEATHER_API_KEY", "description": "API key for the weather provider", "isRequired": true, "isSecret": true, "format": "string" } ] } ]}กฎที่คนมักสะดุด:
nameเป็นแบบ reverse-DNS และต้องตรงกับmcpNameในpackage.json- ถ้าใช้ GitHub authentication ชื่อ ต้อง ขึ้นต้นด้วย
io.github.<your-username>/ versionด้านบนและใน package แต่ละตัวควรตาม release ของคุณregistryTypeเป็นได้ทั้งnpm,pypiหรือoci(container image) ใส่มากกว่าหนึ่ง package ได้ถ้าคุณ ship ทั้ง npm และ PyPI- server แบบ hosted เพิ่ม array
remotesแทน (หรือควบคู่กับ)packagesโดยใส่ URL ของ Streamable HTTP เพื่อให้ host เชื่อมต่อได้โดยไม่ต้องติดตั้งอะไร
3. login แล้ว publish
หัวข้อที่มีชื่อว่า “3. login แล้ว publish”mcp-publisher login github # device-code flow in the browsermcp-publisher publish4. ตรวจสอบ
หัวข้อที่มีชื่อว่า “4. ตรวจสอบ”curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.acme/weather"response คือ {"servers":[{ ... "name":"io.github.acme/weather" ... }]}
error ที่พบบ่อย: Registry validation failed for package แปลว่า package ที่ publish ไม่มี mcpName; Invalid or expired Registry JWT token แปลว่าต้อง login ใหม่; You do not have permission to publish this server แปลว่า namespace ไม่ตรงกับวิธี authentication ของคุณ
namespace แบบ custom
หัวข้อที่มีชื่อว่า “namespace แบบ custom”GitHub auth ให้ io.github.<user>/ ถ้าอยาก publish ใต้ domain ของตัวเอง (com.acme/weather) ให้ใช้ DNS authentication: พิสูจน์ว่าควบคุม domain ได้ด้วย TXT record แล้ว registry จะให้ publish ชื่ออะไรก็ได้ใต้ com.acme/ docs ของ registry ยังครอบคลุมการ publish จาก GitHub Actions เพื่อ automate release ด้วย
แจกจ่ายภายในทีม
หัวข้อที่มีชื่อว่า “แจกจ่ายภายในทีม”ไม่ใช่ทุก server ที่ควรอยู่ใน registry สาธารณะ สำหรับ tool ภายใน ให้ commit .mcp.json ไว้ที่ root ของ repository ที่ต้องใช้มัน Claude Code จะหยิบมันขึ้นมาด้วย semantic แบบ --scope project และเพื่อนร่วมทีมทุกคนได้ server ชุดเดียวกันทันทีที่ checkout:
{ "mcpServers": { "shared-server": { "type": "http", "url": "https://mcp.internal.example.com/mcp" }, "local-db": { "type": "stdio", "command": "npx", "args": ["-y", "@acme/mcp-db", "--config", "${CLAUDE_PROJECT_DIR}/db.json"], "env": { "DB_URL": "${DB_URL:-postgresql://localhost/dev}" } } }}${VAR} และ ${VAR:-default} ขยายได้ใน command, args, env และ headers ความลับจึงอยู่ใน environment ของ developer แต่ละคน ขณะที่ไฟล์เอง commit ได้อย่างปลอดภัย