Skip to content

About authentication

Two things about signing in surprise people: the config you commit holds no secret, and there is no login command. Both follow from the same design.

Your MCP config names a URL and a transport type. There is no token in it and nothing to fill in, because your client obtains its own credential at runtime: on the first request it sends without one, the server tells it where to sign in, your client registers itself, opens a browser, and stores the token it gets back on your machine.

Nothing in that exchange can be written down in advance, so nothing about it can leak from your repository. Check the file in.

If your client says it connected but every tool fails

Section titled “If your client says it connected but every tool fails”

That symptom has one common cause: the client is not doing the discovery step. The server answers an unauthenticated request with a 401 and a header naming where to authenticate, and a spec-compliant client treats that as the signal to start the sign-in flow. A client that ignores it believes it has connected to a server that needs no credentials, reports success, and then fails at the first real call.

If you see that, check your client supports OAuth 2.1 with Dynamic Client Registration.

A person signs in through the browser. Their credential proves who they are.

A machine — CI, an unattended agent — presents a b8e_ API key as its Bearer token instead. That credential is tied to one organization, so it proves both who is calling and where they are allowed to write.

A machine credential reaches exactly one tool: create_ticket. Every other tool on this server, read or write, requires a signed-in person and answers a machine caller with Not authenticated. So a CI job can file findings, and nothing else.

This is the one place the difference shows up in practice. Filing a finding with create_ticket needs to know which organization it lands in:

  • Signed in as a person — you must pass organization_id, because you may belong to several and your credential does not say which one you mean. Get it from list_organizations, not get_viewer: get_viewer returns a single organization, and it is simply the oldest one, which is a coin flip rather than a choice.
  • Machine credential — you can leave it out; the credential already names the organization. If you pass one anyway it is checked against the credential, and a mismatch is refused rather than redirected, so you find out instead of writing somewhere you did not intend.

To connect a client, see Connect an MCP client.