Loading...
Loading...
MCP Server Construction Methodology — Systematically build production-grade MCP tools to connect AI assistants with external capabilities
npx skill4agent add jnmetacode/superpowers-zh mcp-builderusers://{id}/profilemy-mcp-server/
├── src/
│ ├── index.ts # Entry point, register tools/resources
│ ├── tools/ # Split by functionality
│ ├── resources/
│ └── lib/ # Client encapsulation, validation logic
├── tests/
├── package.json
└── tsconfig.json@modelcontextprotocol/sdkzodmy-mcp-server/
├── src/my_mcp_server/
│ ├── server.py
│ ├── tools/
│ └── lib/
├── tests/
└── pyproject.tomlmcppydanticsnake_casesearch_userscreate_issuedelete_file.describe()server.tool("search_issues", {
query: z.string().describe("Search keywords"),
status: z.enum(["open", "closed", "all"]).default("open").describe("Status filter"),
limit: z.number().min(1).max(100).default(20).describe("Return limit"),
}, async ({ query, status, limit }) => { /* ... */ });server.tool("search_users",
"Search users by name or email. Returns list of ID, name, email. Fuzzy matching, maximum 50 entries.",
schema, handler);content: [{ type: "text", text: "..." }]server.tool("get_user", { id: z.string() }, async ({ id }) => {
try {
const user = await db.getUser(id);
if (!user) {
return {
content: [{ type: "text", text: `User ${id} does not exist, please check the ID.` }],
isError: true,
};
}
return { content: [{ type: "text", text: JSON.stringify(user, null, 2) }] };
} catch (err) {
return {
content: [{ type: "text", text: `Query failed: ${err.message}` }],
isError: true,
};
}
});isError: true// Resource registration
server.resource("user-profile", "users://{userId}/profile", async (uri) => {
const profile = await db.getProfile(extractId(uri));
return { contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(profile) }] };
});
// Lifecycle: Initialize first → then connect → listen for shutdown signals
const db = await Database.connect(config.dbUrl);
await server.connect(new StdioServerTransport());
process.on("SIGINT", async () => { await db.disconnect(); await server.close(); process.exit(0); });// tools/search.ts exports pure functions
export async function searchUsers(query: string, limit: number) { /* ... */ }
// search.test.ts independent testing
test("Return matching results", async () => {
const results = await searchUsers("alice", 10);
expect(results[0].name).toContain("Alice");
});const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await server.connect(serverTransport);
const client = new Client({ name: "test", version: "1.0.0" });
await client.connect(clientTransport);
const result = await client.callTool("search_users", { query: "test" });
expect(result.isError).toBeFalsy();npx @modelcontextprotocol/inspector node dist/index.jsconfirm: true../execFileexec{ "bin": { "mcp-server-myservice": "dist/index.js" }, "files": ["dist"] }{ "mcpServers": { "myservice": { "command": "npx", "args": ["@yourorg/mcp-server-myservice"], "env": { "API_KEY": "xxx" } } } }[project.scripts]
mcp-server-myservice = "my_mcp_server.server:main"FROM node:20-slim
WORKDIR /app
COPY package*.json ./ && RUN npm ci --production
COPY dist ./dist
ENTRYPOINT ["node", "dist/index.js"]console.log// Wrong
console.log("debug");
// Correct
console.error("[DEBUG]", info);
// Better
server.sendLoggingMessage({ level: "info", data: "Processing" });| Symptom | Cause | Solution |
|---|---|---|
| No response on startup | Transport not connected | Check |
| Tool does not appear | Registered after connect | Register first then connect |
| AI does not call Tool | Unclear description | Improve name and description |
| Parameter errors always occur | Ambiguous Schema | Add |
| Call timeout | Slow external service | Add timeout and caching |
verb_nounisError: trueconsole.log