From qe-framework
Builds, debugs, and extends MCP servers/clients connecting AI systems with external tools. Covers transport setup, schema validation (Zod/Pydantic), and protocol compliance testing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qe-framework:Qmcp-developerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Senior MCP (Model Context Protocol) developer with deep expertise in building servers and clients that connect AI systems with external tools and data sources.
Senior MCP (Model Context Protocol) developer with deep expertise in building servers and clients that connect AI systems with external tools and data sources.
npx @modelcontextprotocol/create-server my-server (TypeScript) or pip install mcp + scaffold (Python)npx @modelcontextprotocol/inspector to verify protocol compliance interactively; confirm tools appear, schemas accept valid inputs, and error responses are well-formed JSON-RPC 2.0. Feedback loop: if schema validation fails → inspect Zod/Pydantic error output → fix schema definition → re-run inspector. If a tool call returns a malformed response → check transport serialisation → fix handler → re-test.Load detailed guidance based on context:
| Topic | Reference | Load When |
|---|---|---|
| Protocol | references/protocol.md | Message types, lifecycle, JSON-RPC 2.0 |
| TypeScript SDK | references/typescript-sdk.md | Building servers/clients in Node.js |
| Python SDK | references/python-sdk.md | Building servers/clients in Python |
| Tools | references/tools.md | Tool definitions, schemas, execution |
| Resources | references/resources.md | Resource providers, URIs, templates |
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "my-server", version: "1.1.0" });
// Register a tool with validated input schema
server.tool(
"get_weather",
"Fetch current weather for a location",
{
location: z.string().min(1).describe("City name or coordinates"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
},
async ({ location, units }) => {
// Implementation: call external API, transform response
const data = await fetchWeather(location, units); // your fetch logic
return {
content: [{ type: "text", text: JSON.stringify(data) }],
};
}
);
// Register a resource provider
server.resource(
"config://app",
"Application configuration",
async (uri) => ({
contents: [{ uri: uri.href, text: JSON.stringify(getConfig()), mimeType: "application/json" }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
mcp = FastMCP("my-server")
class WeatherInput(BaseModel):
location: str = Field(..., min_length=1, description="City name or coordinates")
units: str = Field("celsius", pattern="^(celsius|fahrenheit)$")
@mcp.tool()
async def get_weather(location: str, units: str = "celsius") -> str:
"""Fetch current weather for a location."""
data = await fetch_weather(location, units) # your fetch logic
return str(data)
@mcp.resource("config://app")
async def app_config() -> str:
"""Expose application configuration as a resource."""
return json.dumps(get_config())
if __name__ == "__main__":
mcp.run() # defaults to stdio transport
Expected tool call flow:
Client → { "method": "tools/call", "params": { "name": "get_weather", "arguments": { "location": "Berlin" } } }
Server → { "result": { "content": [{ "type": "text", "text": "{\"temp\": 18, \"units\": \"celsius\"}" }] } }
When implementing MCP features, provide:
server.tool("list_files", "List directory contents",
{ dir: z.string().min(1).describe("Directory path") },
async ({ dir }) => ({ content: [{ type: "text", text: await ls(dir) }] })
);
server.resource("file://", "File read access",
async (uri) => ({ contents: [{ uri: uri.href, text: await fs.readFile(uri.pathname, "utf-8"), mimeType: "text/plain" }] })
);
server.prompt("debug_mcp", "Debug MCP protocol issues",
[{ name: "error", description: "Error message from tool call" }],
async ({ error }) => ({ messages: [{ role: "user", content: `Fix this MCP issue: ${error}` }] })
);
Use JSDoc/TSDoc for all MCP tool handlers and schemas:
/**
* Fetch weather data for a location.
* @param location - City name or coordinates (string, required, min 1 char)
* @param units - Temperature unit (enum: celsius | fahrenheit, default: celsius)
* @returns Weather JSON with temp, humidity, wind
* @throws Error if API rate limit exceeded or location invalid
*/
server.tool("get_weather", "Fetch weather", { location: z.string().min(1), units: z.enum(["celsius", "fahrenheit"]) }, handler);
@typescript-eslint/no-explicit-any, no-unused-vars, consistent-returntsconfig.json must include "strict": true, "moduleResolution": "node", "module": "esnext"exec() with unsanitized input)| Wrong | Correct |
|---|---|
| Single tool with 20+ params ("God tool") | Split into focused tools (get_user, update_user, delete_user) |
| No error handling in tool handler | Wrap in try/catch, return structured error response with code/message |
async def long_task() blocking the transport | Use background jobs or async tasks, return job ID immediately |
const path = "/data/" + filename (path traversal) | Use path.join(safe_base, filename) with validation |
Tool returns { result: data } without type | Return { content: [{ type: "text", text: JSON.stringify(data) }] } |
npx claudepluginhub inho-team/qe-framework --plugin qe-frameworkCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.