Ship It

Deploying Your MCP Server to Production

⏱ 17 min

Everything so far has run against a local WordPress environment, likely wp-env, with loose defaults that are fine for development and genuinely unsafe in production. This final lesson is a deliberate checklist of what actually changes when this same server starts serving real traffic on a real domain, not new features, just the operational discipline the earlier lessons assumed you’d add later.

What you'll learn in this lesson
Why HTTPS is not optional for HTTP transport in production
What travels over that connection, and why plaintext HTTP exposes it.
What changes between wp-env and a real host
User accounts, error visibility, and process management for STDIO.
Application Password hygiene
Rotation, scoping, and revocation as an ongoing practice, not a one-time setup step.
A pre-launch checklist
Concrete things to verify before pointing a real AI client at a live server.
Prerequisites

A working MCP server from the previous lessons, tested locally over both HTTP and STDIO. Access to your production hosting environment and its HTTPS/TLS configuration.

Step 1: HTTPS is not optional

Every request to your HTTP transport endpoint carries an Application Password in an Authorization header (Basic auth), plus, once a session starts, an Mcp-Session-Id value that’s effectively a bearer credential for that session. Over plain HTTP, both are readable to anything sitting on the network path between client and server. Locally, against wp-env on localhost, that risk is close to theoretical. In production, it is not. Every production MCP server endpoint needs to be served over HTTPS, full stop, the same standard you’d hold any other authenticated REST API to.

Before going live over HTTP transport
A valid TLS certificate on the domain serving the endpoint
Not self-signed, not expired.
HTTP to HTTPS redirects enforced at the server or load balancer level
So a client can't accidentally fall back to plaintext.
No Application Password ever logged, printed, or committed anywhere
Treat it exactly like a database password.

Step 2: What actually changes between wp-env and production

Local wp-env versus production, what’s different
AspectLocal (wp-env)Production
TransportBoth HTTP and STDIO, freely, since you control the machineHTTP is the realistic option for remote clients; STDIO via WP-CLI generally only makes sense if the AI client runs on the same server or over SSH, since it needs direct shell and file-system access to the install
—user for STDIO / transport permission callback for HTTPOften admin, for convenienceA dedicated, least-privileged account created specifically for MCP access, not a shared admin login
Error visibilityWP_DEBUG commonly on, errors visible directlyWP_DEBUG off publicly; rely on the adapter’s error handler logging (ErrorLogMcpErrorHandler by default) to your server’s error log instead
Application PasswordsCreated once, rarely rotatedScoped to a specific purpose, rotated on a schedule, and revoked immediately if a client integration is retired

Step 3: Application Password hygiene as an ongoing practice

Application Passwords don’t expire on their own, they’re valid until explicitly revoked. Treat the one your MCP client uses as a credential with a lifecycle, not a one-time setup step:

Application Password hygiene
1
Generate a dedicated password per integration
One MCP client, one Application Password, never shared across multiple tools or environments.
2
Use a dedicated WordPress user, not your own account
So revoking access for one integration doesn't require touching a real person's login, and audit logs clearly show which actions came from the MCP integration.
3
Rotate on a schedule you'd actually keep
Quarterly is a reasonable default for anything production-facing; more often for anything higher-risk.
4
Revoke immediately when a client or use case is retired
An unused Application Password is a live credential doing nothing but sitting as risk.

Revoking one is done the same way it was created, from the user’s profile screen in wp-admin (Users → Profile → Application Passwords), or programmatically if you’re managing this at scale across many sites.

Step 4: A concrete pre-launch checklist

Before pointing a real AI client at a production MCP server
HTTPS enforced on the endpoint, with a valid certificate
No plaintext fallback path exists.
A dedicated, least-privileged user for the integration, with its own Application Password
Not a shared admin account.
Every exposed ability tested against a low-privileged caller, not just an administrator
Confirmed in Lesson 4 and Lesson 10, re-verify against the production ability list specifically.
meta.mcp.public reviewed ability-by-ability, not left on by default
Anything you wouldn't want an external client to even discover should be false.
Annotations (readonly/destructive/idempotent) accurate on every public ability
So client UIs treat destructive actions with appropriate caution.
Error logging confirmed reachable
You know where ErrorLogMcpErrorHandler (or your own handler) writes to on this specific host, and someone actually looks at it.

Test it

Run the same session-based curl sequence from Lesson 6, against the production domain, and confirm it fails correctly over plain HTTP if you try it (most hosts will simply refuse the connection or redirect):

# File: terminal, sanity check against production
curl -s -D - -X POST \
  "https://yoursite.com/wp-json/my-plugin/content-mcp" \
  --user "mcp-integration-user:xxxx xxxx xxxx xxxx xxxx xxxx" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"prod-check","version":"1.0.0"}}}'

Confirm the response includes an Mcp-Session-Id header and a successful initialize result, using the dedicated integration user’s Application Password, not your own.

Reusing your personal admin account's Application Password in production

The single most common shortcut that turns into a real incident: generating an Application Password on a personal administrator account to get a production integration working quickly, then never circling back to create a dedicated, least-privileged account for it. If that Application Password ever leaks, whoever has it has your full administrator capabilities, not just whatever the MCP server’s abilities were meant to expose. The dedicated-user step in this lesson’s checklist is not optional polish, it’s the difference between a leaked credential being a contained problem and a full site compromise.

Recap

Deploying an MCP server to production is mostly discipline you’ve already learned how to apply, HTTPS enforced without exception, a dedicated least-privileged user instead of a shared admin account, Application Passwords treated as credentials with a rotation and revocation lifecycle, and every ability’s visibility and annotations reviewed rather than left at whatever was convenient locally. You now have a complete, working, secured MCP server built entirely on the official Abilities API and MCP Adapter. From here, Connect AI Assistants & Agents to WordPress covers pointing real clients, Claude Desktop, Cursor, and ChatGPT, at a server like this one and turning it into a working conversational assistant.

Resources & further reading

← Testing and Debugging Your MCP Server Finish ✓