MCP Server Development: Build and Test a Useful First Server
Building an MCP server starts with picking one real workflow problem, not the most impressive feature you can imagine. A focused first tool that does one thing clearly is easier to test, easier to connect, and more likely to keep working.
Updated 6 min read
MCP server development begins with a single clear question: what is one thing your AI assistant should be able to do that it cannot do now? Start there, build one tool, connect it to a client, and confirm it works before adding anything else.
That constraint is not a beginner's shortcut — it is the correct approach. Small, well-defined tools are easier to test, easier to describe, and more reliable in production. Tools like fenbs already work this way: one MCP endpoint, a list of specific tools, and a clear record of what each one does and what it requires.
Choose the right first tool
A good first tool has one input, one action, and one output. It reads data from a source your AI currently cannot reach, or writes to one place in a controlled way. "Search our knowledge base by keyword" is a good first tool. "Manage our entire knowledge base" is not.
Pick a workflow your team does repeatedly. If someone types the same lookup into a browser five times a day, that is a candidate. If the lookup requires authentication you control, access to a database, or a call to a private API, those are reasons to build your own server rather than use an existing one.
For a survey of existing servers worth connecting before you build, see the mcp server directory and best MCP servers.
Set up the development environment
The Model Context Protocol team publishes official SDKs for TypeScript and Python at modelcontextprotocol.io/sdks (opens in a new tab). Both follow the same pattern: create a server instance, register tools, connect the transport, and start listening.
For a local server — one that runs as a child process when Claude Desktop or Claude Code starts — you will use the stdio transport. The client launches your server, exchanges messages through stdin and stdout, and shuts it down when the session ends. This is the simplest setup and the right one for a first build.
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: '0.1.0' });
server.tool(
'lookup_item',
{ id: z.string().describe('The item identifier') },
async ({ id }) => {
// replace with your real lookup
const result = `Item ${id}: example data`;
return { content: [{ type: 'text', text: result }] };
},
);
const transport = new StdioServerTransport();
await server.connect(transport);Write the tool handler
A tool handler receives the validated input, does the work, and returns content. Keep the handler focused on one thing. If your tool needs to authenticate with an API, open a database connection, or read a config file, set those up outside the handler and pass them in as closed-over variables.
Validate inputs early. The SDK generates an input schema from your type definitions, but defensive checks inside the handler catch edge cases the schema allows. Return a clear error message rather than letting an unexpected input cause an exception the client cannot explain.
When a tool fails, return isError: true with a plain explanation in the content. This tells the AI that the call completed but found a problem, which it can describe to the user. A thrown exception becomes a JSON-RPC error, which looks like a protocol failure rather than a tool result.
Define the schema clearly
The tool schema is what the AI reads when it decides whether and how to call your tool. A good schema has a short, direct description on the tool itself, a description on each input parameter, and an output that a reader can understand without additional context.
Avoid required parameters the AI cannot supply. If a tool needs an internal identifier that only exists after another tool call, either combine the tools or make the dependency clear in both descriptions. An AI that calls your tool with the wrong input and gets an error will try to guess the right input, often incorrectly.
Record the tool contracts your team depends on
Add the tool's name, its inputs, its expected outputs, and any known limits as a Decisions and rules entry in fenbs. When another developer or AI assistant picks up the work, they read those decisions first and build on the agreed contract rather than rediscovering it from the code.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
Test with a real client
The MCP Inspector, available from the modelcontextprotocol.io repository, is the fastest way to test a server without a full client setup. It connects over stdio, lists the server's tools, lets you call them manually, and shows the raw JSON responses.
After you have confirmed the tool works in the Inspector, test it in Claude Desktop or Claude Code — the client you actually intend to use. Ask the AI to call the tool by name with a known input and check the result. If the AI calls the wrong tool or supplies wrong inputs, revise the descriptions until it consistently uses the right one.
Test error cases too. Give the tool an id that does not exist and confirm it returns a clear isError: true response rather than a crash. A well-tested error path is as important as a well-tested success path.
Common problems and how to fix them
- The client cannot find the server: check the path to the executable in the client's config file and confirm it is absolute, not relative.
- The tool is never called: the description may be too vague or too similar to another tool. Rewrite it with the specific task name.
- The tool is called with wrong parameters: add or improve the description on each parameter, including what type of value is expected.
- The server crashes on startup: check that all required environment variables are set. The client may not pass your shell environment to the process.
- Results are truncated: some clients limit content length. Return structured data rather than long plain text where possible.
How to do this with fenbs
fenbs connects over MCP at https://fenbs.ai/api/mcp and is an example of a working remote server. If the tools your project needs are shared project context — tasks, rules, lessons, and where the work stands — fenbs may be the server you need rather than one you build.
- Create a free account and set up a project for the server work you are building.
- Add the first tool's name, input schema, and expected outputs as a Decisions and rules entry. Link it to the task for that tool.
- Record implementation choices: the transport type, the client you are targeting, and any API credentials required (do not store secrets in fenbs — note where they live).
- When a tool changes its contract, update the decision and add a note explaining what changed and why.
- Connect Claude Code or another supported AI to fenbs over MCP from Integrations so it reads the current decisions before working on the server code.