To build an MCP server, first define the smallest capability boundary, choose tools, resources, and prompts, implement them with an official SDK, connect a local stdio or remote Streamable HTTP transport, enforce authorization in the server and downstream system, test with MCP Inspector, and deploy with versioning, monitoring, revocation, and failure handling.
The short answer
To build a production MCP server, define the user job and trust boundary first, then expose a small set of well-described resources and tools. Add input validation, least-privilege credentials, confirmation for consequential writes, structured errors, observability, and client-level testing before expanding the surface.
Do not start with the SDK
The hardest MCP server decisions are not language syntax.
Before writing code, answer:
Who will use the server?
Which system does it expose?
Which exact jobs should an agent complete?
What data may the server read?
What can it change?
Which identity does it act as?
Is it local or shared?
What requires approval?
How is access revoked?
What does success look like?
A server named “company tools” with generic API access is difficult to secure, explain, test, and trust.
A better first server might be:
Search and read documents from one workspace, then create a reviewable draft in a designated folder. No deletion, public sharing, member management, or cross-workspace access.
Step 1: Define the capability boundary
!A single orange capability module passing through a controlled boundary
Write a one-page server contract.
Contract field | Example |
|---|---|
Audience | Internal research agents |
Resource boundary | One assigned workspace |
Read actions | Search, list, read |
Write actions | Create draft, append evidence |
Forbidden actions | Delete, publish, share, change permissions |
Identity | Per-user OAuth connection |
Transport | Remote Streamable HTTP |
Approval | Required before external publication |
Audit | Tool, actor, target, result, timestamp |
Revocation | Revoke connector or user token |
This contract becomes the basis for tools, authorization tests, documentation, and incident response.
Step 2: Choose tools, resources, and prompts
Use tools for actions
A tool accepts structured arguments and performs an operation.
Good tools:
search_documents;
read_customer_record;
create_draft;
add_evidence_card;
calculate_metric.
Avoid:
execute_command;
call_any_api;
run_query;
manage_everything.
Tool names and descriptions should communicate real behavior, but security must come from enforcement rather than naming.
Use resources for contextual data
Resources fit stable or addressable context:
document content;
repository metadata;
schema;
report;
customer profile;
configuration reference.
Use a resource template when a URI has structured parameters.
Use prompts for reusable workflows
Prompts can package a known interaction:
review a research brief;
summarize a support thread;
prepare a launch retrospective.
Prompts help users start workflows. They should not hide permissions or override host policy.
Step 3: Choose the official SDK version deliberately
Official SDKs exist for TypeScript, Python, and other languages. Their release lines can evolve independently from the protocol.
As of 2026-07-12:
the Python SDK README documents v1.x as stable while v2 is moving through prerelease;
the TypeScript SDK main branch documents v2 development while recommending v1.x for production until the new stable release;
production dependencies should be pinned to an explicitly tested SDK version.
Do not copy the latest main-branch example into a production project without checking which package version it targets.
Record:
protocol revision;
SDK name;
SDK version;
runtime version;
transport;
client versions tested;
upgrade policy.
Step 4: Create the server and declare identity
Every server should expose clear identity metadata:
stable name;
semantic version;
human-readable description;
owner or website where supported;
protocol capabilities;
security contact in project documentation.
The version should change when behavior, schemas, permissions, or compatibility changes.
Avoid names that collide across several connected servers. A host may combine tools from many servers into one registry.
Step 5: Design tool schemas
A tool definition needs:
unique name;
precise description;
input schema;
optional output schema;
execution behavior;
error model;
security classification;
side-effect classification.
Input schema rules
Prefer:
explicit object types;
required fields;
enumerations;
length limits;
formats;
descriptions;
additional-properties restrictions for sensitive tools.
Example design:
Field | Type | Constraint |
|---|---|---|
workspace_id | string | Must match authenticated scope |
title | string | 1–160 characters |
content | string | Maximum accepted size |
folder | string | Approved destination only |
publish | boolean | Not accepted by this draft-only tool |
The server must derive authorization scope from authenticated context. It should not trust a model-provided workspace identifier by itself.
Output schema rules
Structured output helps clients validate and reuse results.
Return:
stable identifiers;
action outcome;
affected resource;
safe summary;
error category;
next permitted actions.
Do not return secrets, raw tokens, unrelated records, or internal stack traces.
Step 6: Implement business authorization inside handlers
Authentication middleware is not enough.
Every handler should:
load authenticated identity;
resolve tenant and resource scope;
authorize the operation;
validate input;
enforce business invariants;
call the downstream system with least privilege;
minimize output;
write audit events;
return a structured result.
For a document update, verify that:
the user belongs to the workspace;
the resource belongs to that workspace;
the role allows the requested write;
the document is not locked;
the operation does not cross an external-sharing boundary;
the write is recorded.
Never ask the model whether the user “seems authorized.”
Step 7: Make mutations safe
!A validation mechanism filtering inputs and blocking a risky mutation
A model may retry, a client may reconnect, and a network response may be lost.
For state-changing tools:
accept an idempotency key where appropriate;
return stable operation IDs;
detect duplicate requests;
use transactions;
validate current version or revision;
support dry-run for complex actions;
require confirmation for destructive actions;
define rollback or compensation;
distinguish partial success;
never silently broaden scope after failure.
A tool that creates a draft can often be idempotent. A tool that sends an email cannot simply repeat.
Step 8: Choose a transport
Stdio
Use stdio when:
the host launches the server locally;
data and tools belong to the local machine;
a network endpoint is unnecessary;
the user controls installation.
Operational requirements:
protocol messages only on standard output;
logs on standard error;
controlled environment variables;
restricted working directory;
clean process shutdown;
no unnecessary network access.
Streamable HTTP
Use Streamable HTTP when:
many clients share a hosted server;
the capability belongs to a cloud service;
centralized authentication and updates matter;
team or tenant scope must be enforced remotely.
Operational requirements:
HTTPS;
correct MCP endpoint behavior;
authentication;
audience validation;
session management;
tenant isolation;
rate limits;
request-size limits;
origin and host protections;
scalable state design;
health and readiness checks.
Do not expose a development server to the public internet.
Step 9: Add authentication correctly
For protected remote servers, use the MCP authorization specification and established OAuth libraries.
The MCP server acts as a protected resource server. It validates access tokens issued for itself.
Check:
signature;
issuer;
audience;
expiration;
scopes;
resource binding;
revocation;
tenant context.
If the server calls another API, obtain a separate token for that API. Do not pass the MCP client token through to the downstream service.
For local servers, authentication may be replaced by process and OS trust boundaries, but local file, process, and network permissions still require control.
Step 10: Implement resources carefully
For each resource:
choose a stable URI scheme;
define MIME type;
enforce authorization on every read;
return bounded content;
paginate lists;
support templates only where necessary;
avoid leaking existence across tenants;
define update notifications accurately;
treat resource metadata as potentially model-visible.
A resource list that reveals confidential file names is already a data leak even if content reads fail.
Step 11: Implement prompts transparently
Prompts should:
have clear names and arguments;
describe what workflow they support;
avoid embedding secrets;
avoid pretending to be higher-authority host policy;
produce understandable messages;
handle missing arguments;
remain versioned and testable.
A server-provided prompt is input from the server. Hosts should not automatically trust prompts from unknown servers.
Step 12: Handle errors without leaking data
Separate:
protocol error;
validation error;
authentication failure;
authorization failure;
not found;
conflict;
rate limit;
downstream failure;
timeout;
internal error.
Return enough information for the client to recover, but not stack traces, SQL, credentials, internal paths, or cross-tenant identifiers.
For sensitive resources, consider returning the same public behavior for “not found” and “not authorized” to avoid existence disclosure.
Step 13: Add timeouts, cancellation, and progress
Long-running tools should not block indefinitely.
Implement:
connection timeout;
downstream timeout;
overall tool deadline;
cancellation propagation;
bounded retries;
progress notifications where useful;
cleanup on disconnect;
durable job state when work outlives the request.
Never retry non-idempotent writes automatically without a safe strategy.
Step 14: Log safely
For each consequential call, record:
timestamp;
actor;
client;
server version;
tool;
tenant and target;
normalized arguments or safe summary;
approval;
result;
latency;
correlation ID.
Do not record:
access tokens;
passwords;
secrets;
full sensitive documents;
unnecessary prompts;
personal data without purpose.
For stdio, logs belong on standard error so they do not corrupt the JSON-RPC channel.
Step 15: Test with MCP Inspector
MCP Inspector can exercise:
connection and initialization;
capability negotiation;
resources and templates;
prompts and arguments;
tools and schemas;
tool results;
notifications;
logs.
Development loop:
launch the server with Inspector;
verify identity and capabilities;
list tools, resources, and prompts;
test normal inputs;
test invalid and missing inputs;
test unauthorized resources;
test concurrency;
observe notifications;
restart and reconnect;
repeat after every schema change.
Inspector proves protocol behavior, not full security. Add automated tests and end-to-end authorization tests.
Step 16: Build an automated test suite
Contract tests
initialization;
declared capabilities;
tool schemas;
output schemas;
resource URIs;
prompt arguments;
errors.
Authorization tests
wrong user;
wrong tenant;
wrong workspace;
expired token;
wrong audience;
missing scope;
revoked connection;
deleted membership.
Mutation tests
duplicate request;
stale revision;
partial downstream failure;
transaction rollback;
cancellation;
timeout;
retry.
Adversarial tests
prompt injection in content;
malicious resource metadata;
oversized input;
path traversal;
SSRF;
command injection;
SQL injection;
malicious redirect;
data exfiltration;
tool-description manipulation.
Compatibility tests
Test the clients you claim to support using pinned versions.
Step 17: Package and version the server
For local distribution:
pin dependencies;
use a reproducible build;
publish under a verified package identity;
document required environment variables;
avoid install scripts unless necessary;
generate checksums or signatures where possible;
maintain a security policy;
provide uninstall and credential-cleanup instructions.
For hosted servers:
use immutable artifacts;
scan dependencies and images;
run as non-root;
restrict egress;
separate environments;
manage secrets externally;
use infrastructure policy;
roll out gradually;
maintain rollback.
A client should be able to identify what version it is connected to.
Step 18: Deploy with observability
Monitor:
connection failures;
initialization failures;
tool error rate;
latency;
downstream errors;
authorization denials;
rate-limit events;
unusual tool combinations;
data volume;
cost;
active sessions;
version adoption;
revocation events.
Alert on behavior that indicates compromised credentials, cross-tenant attempts, automated abuse, or a breaking server update.
Step 19: Document the user contract
Your README or integration guide should state:
server purpose;
current capabilities;
permissions required;
data accessed;
external systems called;
local files or processes used;
network destinations;
state-changing tools;
confirmation behavior;
credential storage;
log and retention policy;
supported clients;
version compatibility;
revocation;
security reporting.
Do not advertise “read only” if any tool can mutate data.
Step 20: Run a production-readiness review
!An operations instrument showing health, latency, errors, and deployment checkpoints
Capability
One coherent server purpose.
Minimal tools, resources, and prompts.
Clear schemas and errors.
Stable names and version.
Security
Threat model complete.
Least privilege enforced.
Tenant scope derived from identity.
No token passthrough.
Secrets isolated.
Prompt-injection path tested.
Destructive tools confirmed or removed.
Revocation tested.
Reliability
Timeouts and cancellation.
Safe retry behavior.
Idempotent mutations.
Transaction or compensation strategy.
Health checks.
Rollback plan.
Operations
Structured logs.
Audit trail.
Metrics and alerts.
Versioned release.
Security contact.
Incident runbook.
Compatibility
Inspector passes.
Supported clients tested.
Protocol and SDK versions documented.
Upgrade path defined.
A practical first server
Build a read-first workspace research server:
Tools:
search_workspace;
read_document;
create_research_draft;
append_evidence_card.
Resources:
workspace schema;
editorial policy;
approved source list.
Prompt:
create a source-grounded research brief.
Guardrails:
one workspace;
per-user identity;
no deletion;
no public sharing;
no member management;
draft folder only;
audit every write;
revoke connection centrally.
This server is useful enough to prove the full path without granting broad agency.
Frequently asked questions
Which language should I use?
Use the language and official SDK your team can operate securely. TypeScript and Python both have official SDKs. Pin a tested stable release and check current migration guidance.
Should I build tools or resources first?
Start with resources and read-only tools that prove the data boundary. Add narrow write tools after authorization, audit, and approval behavior work.
Do I need OAuth for a local stdio server?
Not always. A locally launched server often relies on OS and process boundaries. You still need to secure files, environment variables, subprocesses, and network access.
How do I test an MCP server?
Use MCP Inspector for interactive protocol testing, then add automated contract, authorization, mutation, adversarial, and client-compatibility tests.
Should my tool return text or JSON?
Prefer structured output for machine reuse and validation, with human-readable content where compatibility or display benefits. Define an output schema when the SDK and protocol version support it.
Can I expose my existing API as MCP?
Yes, but do not mechanically map every endpoint. Design a smaller model-facing capability surface with explicit schemas, scopes, safe outputs, and user-centered workflows.
How long does it take to build an MCP server?
A toy server may take minutes. A production server also needs authorization, tenant isolation, safe mutations, tests, observability, deployment, documentation, and incident response. Scope determines the work.
MCP Server Architecture for protocol structure.
MCP Security Checklist for Teams for controls.
Local vs Remote MCP Servers for transport decisions.
