Dokki Blog logo

How to Build an MCP Server: Production Checklist

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:

  1. load authenticated identity;

  2. resolve tenant and resource scope;

  3. authorize the operation;

  4. validate input;

  5. enforce business invariants;

  6. call the downstream system with least privilege;

  7. minimize output;

  8. write audit events;

  9. 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:

  1. launch the server with Inspector;

  2. verify identity and capabilities;

  3. list tools, resources, and prompts;

  4. test normal inputs;

  5. test invalid and missing inputs;

  6. test unauthorized resources;

  7. test concurrency;

  8. observe notifications;

  9. restart and reconnect;

  10. 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.

Sources