MCPSkill
MCP and Skill MarketMCP vs Skill
MCP

MCP Server

The component that exposes tools, resources, or prompts to AI clients through a controlled protocol boundary.

Server role

An MCP server turns a real system into a capability that an AI client can discover and call. The server owns integration details, validates inputs, performs the action, and returns a structured result.

Design a good capability

1
Name

Use a verb-noun name such as search_docs, create_ticket, or validate_invoice.

2
Description

Explain when the client should call it and what result it returns.

3
Input schema

Make required fields explicit and avoid free-form blobs when structured fields are possible.

4
Result schema

Return predictable data that the agent can use without parsing prose.

Copyable tool definition

This definition is intentionally framework-neutral. Real MCP SDK code can expose the same contract through protocol APIs.

export const searchDocsTool = {
  name: "search_docs",
  description: "Search indexed documentation pages for beginner questions about MCP and Skills.",
  inputSchema: {
    type: "object",
    required: ["query"],
    properties: {
      query: {
        type: "string",
        minLength: 1,
        description: "Search phrase from the user's question."
      },
      limit: {
        type: "number",
        minimum: 1,
        maximum: 10,
        default: 3
      }
    }
  }
}

Write the handler

The handler is the server-owned part. Keep validation close to execution and make the response shape stable.

export function handleSearchDocs(input, pages) {
  const query = input?.query
  const limit = input?.limit ?? 3

  if (!query || typeof query !== "string") {
    return capabilityError("INVALID_QUERY", "query must be a non-empty string", false)
  }

  if (!Number.isInteger(limit) || limit < 1 || limit > 10) {
    return capabilityError("INVALID_LIMIT", "limit must be an integer from 1 to 10", false)
  }

  const q = query.toLowerCase()
  return {
    results: pages
      .filter((page) => `${page.title} ${page.body}`.toLowerCase().includes(q))
      .slice(0, limit)
      .map(({ title, url, body }) => ({ title, url, snippet: body }))
  }
}

function capabilityError(code, message, retryable) {
  return { error: { code, message, retryable } }
}

Error behavior

Errors are part of the API. A good server explains what failed and whether the client can retry, ask the user for missing information, or stop.

{
  "error": {
    "code": "MISSING_REQUIRED_FIELD",
    "message": "The customer_id field is required.",
    "retryable": false,
    "user_action": "Ask the user for a customer id."
  }
}

Three errors beginners should test

handleSearchDocs({}, pages)
// INVALID_QUERY: the client should ask for a search phrase.

handleSearchDocs({ query: "MCP", limit: 99 }, pages)
// INVALID_LIMIT: the client should retry with a valid limit.

handleSearchDocs({ query: "billing" }, pages)
// Not an error. Return { "results": [] } so the agent can say nothing matched.

Operational checklist

PermissionsSeparate read-only actions from write or destructive actions.
Rate limitsProtect backing APIs and communicate retry limits clearly.
ObservabilityLog tool calls, latency, failures, and result size.
VersioningAvoid breaking clients when input or output shapes change.