Almost every argument about this is really an argument about who holds the credential.
A Model Context Protocol server can run as a local process the client launches over stdio, or as an HTTP endpoint someone else hosts. People debate this as though it were a performance question. It usually is not.
| Question | Local (stdio) | Remote (HTTP) |
|---|---|---|
| Who holds the secret | The user's machine | The host, via OAuth |
| Touches local files | Yes, naturally | No |
| Install cost per user | Real — runtime, version drift | A URL |
| You can ship a fix | When users update | Immediately |
| Debuggable by the user | Yes — it is a process | Only what you log |
If the server needs the user's filesystem or a secret you must never see, it goes local. If it is a wrapper over a service you already operate, it goes remote, because the install cost of local servers is the thing that silently kills adoption.
The mistake I see most often is shipping a local server whose only job is to call a hosted API. Now every user is running a version of your client code, and you cannot fix a bug without a release. That is all downside.
A remote MCP endpoint should advertise its protected-resource metadata so a client can discover how to authenticate, rather than expecting a hand-pasted token. A 401 that names a
.well-known/oauth-protected-resource document, which then actually serves JSON, is the difference between “works in any client” and “works in the one I tested”. If you want a worked example of a hosted endpoint set up this way, Wagglet's MCP docs lay the flow out end to end.
Test it with a bare curl before you believe it. Plenty of endpoints return the right header and then redirect the metadata URL to a marketing page, which no client can use.