Your Remote MCP Server Will Fail Every Directory Check. Here Is Why

MCP directories verify a server by starting it in a container with no browser. A remote server that authorizes at startup times out and gets no score. The fix, with the logs from five failed build tests and the change that fixed them.

August 19, 2026
6 min read
Share:
Your Remote MCP Server Will Fail Every Directory Check. Here Is Why

If you have shipped a remote MCP server behind OAuth and submitted it to a directory, you may have watched its build checks fail without a useful explanation. Ours failed five times between July and August 2026. The listing sat at a 17% quality score, and the pull request adding it to awesome-mcp-servers was blocked on exactly that score.

The cause turned out to be a design decision that looks correct until you meet the first automated client.

Disclosure: we build DMARC Examiner, which ships an MCP server. The logs and numbers below are ours.

How directories verify a server

A directory cannot take your word for what your server does. Glama, for instance, builds a container from your repository, starts the server inside it, and asks it over stdio what tools it offers. That is a reasonable check: it proves the thing runs and that the tool list is real.

It also has a consequence that is easy to miss. Their documentation states it plainly:

If the server cannot be started without valid credentials, then we currently do not have a way to run the checks.

There is no browser in that container. There is no human to click Approve. If your server needs either one before it will answer, the check cannot pass.

What failure looks like

Our npm bridge authorized before proxying anything. In a container, that produced this:

Authorize DMARC Examiner in your browser:
  https://mcp.dmarc-examiner.com/oauth/authorize?response_type=code&client_id=...

transport event { type: 'close' }
could not start the proxy McpError: MCP error -32001: Request timed out
  { timeout: 60000 }

The build succeeded. The container started. Then the server printed a URL nobody would ever open and waited until the harness gave up after 60 seconds.

Five build tests, the same shape every time. No score, no listing.

Why authorizing at startup seemed right

This is worth defending before dismantling, because the reasoning was sound when it was written. The comment in our own source said:

Authorizing up front rather than reacting to a 401 mid-session matters: the MCP client on the other end of stdio is blocked on its initialize, and a browser round trip in the middle of that exchange looks like a hang.

That is true. If the server rejects initialize and the bridge responds by opening a browser, the client sits there with no output and no explanation. Authorizing first turns a confusing hang into an explicit prompt.

The flaw is not the reasoning. It is the premise: that the server rejects everything.

The fix has two halves

Serve discovery without a token

initialize, tools/list and ping expose no customer data. Tool names, descriptions and JSON schemas describe what your product can do — the same thing your documentation says in public. Requiring a token for them means a client cannot show a user what the server offers until they have authorized, which is backwards: people authorize because they saw something worth using.

Keep the gate on tools/call, where an actual action happens against actual data.

Doing this safely takes more than deleting a middleware. What we ended up with:

  • Anonymous requests never create a session. They get a stateless transport that closes with the response, so an unauthenticated caller cannot grow a session map.
  • The session header is ignored on the anonymous path, so nobody can attach to someone else's session.
  • A batch is admitted only if every method in it is public. Otherwise [tools/list, tools/call] walks straight through the gate.
  • A present Authorization header is always validated. An invalid token is rejected, never quietly downgraded to anonymous.
  • Anonymous traffic is rate limited and fails closed if the limiter is unavailable.

Under all that, the per-tool authorization checks stay exactly as they were. Nothing that touches data got easier to reach.

Authorize when the server says no

The second half is the bridge. Instead of authorizing on startup, it now:

  1. Reuses and refreshes stored tokens, opening nothing.
  2. Forwards messages as usual.
  3. When the server rejects one — the transport surfaces a 401 as UnauthorizedError — runs the browser flow then, and replays the rejected message so the client's call completes instead of failing.

Concurrent rejections share a single authorization rather than opening a browser tab each.

The user experience improves as a side effect. Before, you added the server and were sent to a browser before you could see a single tool. Now you add it, see all the tools, and authorize the first time you ask it to do something — at the moment where a pause makes sense, because you just asked for it.

The result

Same repository, same Dockerfile, one behavioural change:

before   5 build tests, each timing out after 60s
after    build test success in 15.1s
         the directory read all 21 tools from the container
         release published, quality score 17% → 58%

What to check in your own server

If your remote MCP server is failing directory checks, work through these in order:

  1. Start it in a container with no credentials and no browser. An empty HOME is enough to simulate it. Send initialize, then tools/list. If either hangs or prints an authorization URL, that is your failure.
  2. Check what your discovery methods return without a token. If initialize returns 401, no automated client can even begin.
  3. Confirm the gate is per request, not per session. Serving discovery anonymously is only safe if an anonymous caller cannot then reach anything else.
  4. Read what you expose in tools/list. Descriptions and schemas become public. Ours mention plan tiers and HTTP status codes, which is fine; internal hostnames or identifiers would not be.

The general lesson is smaller than it looks: an MCP server has two audiences, and only one of them is a person. The other is a client — a directory, a scanner, an agent — deciding whether your server is worth listing at all. It will never open a browser for you.

Tags:mcpmodel context protocoloauthmcp serverremote mcpglamadeveloper tools

Ready to improve your email deliverability?

Start monitoring your DMARC reports and get insights into your email authentication setup.

Start Free Trial