Sparround

Writing your own MCP server

There are official SDKs in several languages for writing your own server. The SDK handles the JSON-RPC exchange, the transport and the protocol details; your job is defining the tools.

Each tool needs three things:

1. A name — a unique identifier.

2. A description — the only explanation the model ever sees.

3. An input schema — JSON Schema; parameter types and which are required.

Then the tool's actual implementation: it makes the request and returns the result.

DecisionRecommendationReason
Tool granularityOne tool, one clear jobA universal tool with 20 parameters confuses the model
Result sizeFilter and summariseResults enter the context — 5 MB of JSON eats the budget
Error handlingReturn a readable messageThe model can read the error and adapt
Write operationsA separate, explicitly named tool"I read" and "I changed" must not blur
PermissionsLeast privilege in the server itselfThe server's own permissions, not the agent's, set the real boundary
typescript
// Conceptual shape — see the official SDK docs for exact API names.
// The points that matter: the description, the schema, the result size.

const GET_BUILD_STATUS = {
  name: "get_build_status",
  // This is ALL the model sees — what it does, what it returns, when to use it
  description:
    "Returns the status of the latest build for a branch from the CI system: " +
    "result (success/failure), duration, and the names of failing tests. " +
    "Use when the user asks about build status or investigates a CI failure.",
  inputSchema: {
    type: "object",
    properties: {
      branch: {
        type: "string",
        description: "Branch name, for example 'main' or 'feature/orders'",
      },
    },
    required: ["branch"],
  },
};

async function handleGetBuildStatus({ branch }) {
  const res = await ci.getLatestBuild(branch);

  // IMPORTANT: do not return the whole response. The result enters the context.
  // Return only what the agent needs in order to decide.
  return {
    status: res.status,
    durationSeconds: res.duration,
    failedTests: res.failures?.slice(0, 20).map((f) => f.name) ?? [],
    totalFailures: res.failures?.length ?? 0,
    url: res.webUrl,
  };
}

The essential parts of a tool definition. The most common mistake is not filtering the result: returning the service's full JSON response can eat the context budget in one call.

Plan to test with the MCP Inspector before wiring anything up: it is the official debugging tool and lets you exercise a server without an AI client. Whether the tool list is right, the schema correct, the result in the expected shape — all of that can be checked before connecting to Claude Code.

Areas worth writing a server for on an Android team:

  • An internal feature-flag system — which flags exist, their values, who changed them.
  • Internal API documentation — the backend's real schema (so the agent reads it rather than guessing).
  • The release/deployment system — which version is on which channel (reading; writing is a separate decision).
  • The internal design system — tokens, component rules.

What they share: this information is not in the model's training data and gets copied by hand every time.

📚 Sources and documentation

  • Build an MCP serverofficialmodelcontextprotocol.io

    The official guide to SDKs, defining tools and running a server.

  • MCP reference serversofficialgithub.com

    Official example implementations — a good reference for tool design.