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.
Why the config is safe to commit
Section titled “Why the config is safe to commit”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.
People and machines sign in differently
Section titled “People and machines sign in differently”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.
Naming an organization when you file
Section titled “Naming an organization when you file”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 fromlist_organizations, notget_viewer:get_viewerreturns 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.