How to Build an MCP Server: Complete Developer Guide
To build an MCP server, start with one useful tool, define the input schema, add authentication, test the call path, and deploy it behind a platform that can govern production usage.
Start with a real tool
The easiest way to build an MCP server is to avoid building a generic connector first. Pick one workflow an agent should perform, then expose a small tool with a clear schema and a predictable result.
Good first tools are narrow: search a knowledge base, create a draft, enrich an account, summarize a document, open a ticket, or read deployment status. The tool should do one thing well enough that an agent can decide when to call it.
Define the contract
Your tool contract should explain the action, the required inputs, and the result shape. Keep descriptions plain and operational. The model does not need your internal architecture; it needs to know when the tool is appropriate and what arguments are valid.
Illustrative example from the original guide; verify commands against your installed version.
{
"name": "lookup_customer_contract",
"description": "Find the active contract summary for a customer account.",
"inputSchema": {
"type": "object",
"required": ["accountId"],
"properties": {
"accountId": { "type": "string" }
}
}
}Implement the server
The server receives a tool call, validates the arguments, checks authorization, calls the backing system, and returns structured output. Keep side effects obvious. If a tool writes data, name it like a write and return the created or changed record.
Illustrative example from the original guide; verify commands against your installed version.
async function lookupCustomerContract({ accountId }, context) {
requireScope(context, "contracts:read");
const contract = await contractsApi.getActiveContract(accountId);
if (!contract) {
return { found: false };
}
return {
found: true,
plan: contract.plan,
renewalDate: contract.renewalDate,
terms: contract.summary,
};
}Add MCP server authentication
MCP server authentication should happen before the tool handler runs. The server or gateway should identify the agent, user, workspace, and environment. Then it should translate that identity into scopes like contracts:read or tickets:write.
Avoid one shared key for every agent. Use short-lived tokens, OAuth grants, or signed requests. If the server is private, still authenticate it. Network location is not a permission model.
- Validate the caller before parsing sensitive arguments.
- Use scopes per tool instead of a single all-access role.
- Pass user and workspace identity into the handler context.
- Return clear authorization errors without exposing secret details.
Test and deploy
Test the tool schema, valid calls, missing arguments, unauthorized calls, upstream failures, and result shape. Then deploy the MCP server behind a gateway or platform so production agents get routing, permissions, logs, and secret handling.
With AgentDojo, the deploy path is: register the server, map tools to a profile, attach authentication, run a test call, and promote the profile to the environment your agents use.
Illustrative example from the original guide; verify commands against your installed version.
agentdojo server test contracts \
--tool lookup_customer_contract \
--input '{"accountId":"acct_123"}'
agentdojo profile promote support-staging support-prod