MCP Server Boilerplate: A Minimal, Testable Starting Point
A minimal MCP server template with tool schemas, error handling, and permission boundaries — and when to use an existing MCP connection like fenbs instead of building one.
Updated 4 min read
An MCP server boilerplate is a minimal, working starting point that you can extend into a real server without spending time on protocol plumbing. It handles the transport, the request routing, and the error responses so you can focus on writing tool handlers. If what you need is a shared record of project context — tasks, decisions, where you left off — tools like fenbs already connect over MCP and do that work without any code.
A good boilerplate has three properties: it works out of the box, it fails clearly when something is wrong, and it is small enough to read in one sitting. The goal is not to be clever; it is to give you a clean starting point that you understand completely before you add anything to it.
When to use a boilerplate versus an existing server
Before starting from a boilerplate, decide whether you actually need a custom server. If you want to give an AI assistant access to a specific external service, check the MCP server directory first — an official or community server may already exist. If you want to share project context, rules, and task state across AI sessions, a purpose-built tool is a better starting point than a custom server you maintain yourself.
Build a custom server when you need to expose something specific to your organization that no existing server covers: an internal API, a proprietary database, or a workflow system unique to your team. That is when the boilerplate earns its place.
The minimal structure
A working MCP server needs four things: a server instance, one or more tool definitions, a transport connection, and a process entry point. Everything else is optional at the start. The boilerplate below is in TypeScript using the official SDK, but the same structure applies in any language with an MCP SDK. See the MCP server development guide for language-specific variants. Keep the boilerplate as small as possible: a shorter starting point is easier to understand completely before you add logic.
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.0.0' });
server.tool(
'hello',
'Returns a greeting for a given name.',
{ name: z.string().describe('Name to greet') },
async ({ name }) => ({
content: [{ type: 'text', text: `Hello, ${name}!` }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);Add error handling from the start
Every handler should return a structured error when something goes wrong rather than throwing an exception that crashes the server. Return an isError: true content block with a clear message. This lets the AI client read the error and explain it to the user rather than treating the call as a protocol failure.
server.tool(
'get_record',
'Retrieves a record by ID.',
{ id: z.string() },
async ({ id }) => {
try {
const record = await fetchRecord(id);
return { content: [{ type: 'text', text: JSON.stringify(record) }] };
} catch (err) {
return {
isError: true,
content: [{ type: 'text', text: `Could not fetch record ${id}: ${err.message}` }],
};
}
}
);Set permission boundaries in the tool design
Decide what each tool may and may not do before you write the handler. Tools that read data should not be able to write it. Tools that call an external API should use credentials with the minimum necessary scope. Tools that accept user-supplied strings should validate them before using them in a query or a file path. Bake these constraints into the handler, not into a comment.
- Use a read-only connection for any tool that only needs to retrieve data.
- Validate path arguments to prevent directory traversal in file-system tools.
- Log the arguments and outcome of any tool that modifies state so the change is auditable.
- Name tools to reflect their scope:
read_ordermakes the boundary clearer thanget_order.
Handle the process lifecycle correctly
A common problem with stdio MCP servers is that they exit unexpectedly or hang when the client disconnects. Make sure your server listens for the close event on the transport and exits cleanly when it fires. Unhandled promise rejections are another frequent cause of unexpected exits — add a top-level rejection handler during development so you see the error rather than a silent process crash. For production servers, a process manager such as systemd or PM2 can restart the server if it exits unexpectedly.
Test the boilerplate before extending it
Run the MCP Inspector against the boilerplate before adding any real logic. Confirm the tool appears, call it with valid input, call it with invalid input, and confirm the server handles both correctly. Then commit this tested starting point to version control before extending it. A known-good baseline is worth more than a more complete server you have never fully tested.
How to use this boilerplate with fenbs
Keep the design decisions for the server in fenbs from the start. The boilerplate is the code; the decisions about what it does and who may use it belong in a place your team can read.
- Create a free account and add a project for the MCP server. Describe what it will connect to and who will use it.
- Write the first Decisions and rules before writing any handlers: what the server may read, what it may write, and what is out of scope.
- Add tasks for each tool you plan to build. Include the tool description text so it can be reviewed and updated as the server evolves.
- Connect your AI assistant via Integrations and ask it to read the project rules before making changes to the server.
- After each session, save progress and record any new decisions about the tool API so the next session starts from the current agreed design.
Keep the server's design decisions where every assistant reads them
fenbs stores the tool API decisions, permission boundaries, and maintenance context for your MCP server as AI context and Decisions and rules. When you connect your coding assistant, it reads those decisions before the session, so it does not propose tool changes that violate agreed scope. Create a free account and add the first decision before you add the first real handler.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more