Add support for bluesky-mcp #50

Closed
opened 2026-08-12 10:19:30 +00:00 by coilysiren · 3 comments
Owner
No description provided.
Member

Picked this up in a sweep of the open queue. The body is empty, so I read it as one of two asks - and on the likely one, this is the hardest of the three sibling issues (#50, #51, #52). Details before I build anything.

Reading A - proxy the existing server. serve-upstream already wraps any streamable-HTTP MCP behind a name allowlist, and bluesky-mcp is one. Nothing to build here; it is a deploy values file. Worth noting that a328d5e adds --pin, which is directly relevant: bluesky is the case in deploy#358 where the scope is an argument (the account), so a name allowlist alone does not bound it and a pin now does.

Reading B - replace the bespoke server with a guardfile. This is the product thesis, and I assume it is what you meant. It does not work today, and the blocker is structural rather than a missing convenience.

The guardfile auth block resolves a static value: env, file, or literal, via a value chain. It cannot perform a request to obtain a credential.

AT Protocol needs exactly that. The deploy manifest injects a Bluesky app password, and the app password is not the credential the API takes - it is exchanged at com.atproto.server.createSession for a session token, which then expires and needs refreshing. That is a login round-trip before the first real call, and there is no construct for it: no grammar node, and no runtime hook, because auth is resolved once and attached to each request.

So B needs one of:

  1. A token-exchange auth scheme in umbra's grammar - real work in a shared dependency, and it wants a general design rather than an AT Protocol special case, since OAuth client-credentials has the same shape.
  2. A sidecar that holds the session and exposes a local HTTP surface. Viable now that #59 landed, but it means writing the session-handling process, at which point most of bluesky-mcp still exists and only its MCP layer is replaced.
  3. A long-lived token in SSM, refreshed out of band. Cheapest, and it moves an expiry problem into an operational one, which usually goes badly.

I would not pick any of these without your read. Which reading did you intend?

Picked this up in a sweep of the open queue. The body is empty, so I read it as one of two asks - and on the likely one, this is the hardest of the three sibling issues (#50, #51, #52). Details before I build anything. **Reading A - proxy the existing server.** `serve-upstream` already wraps any streamable-HTTP MCP behind a name allowlist, and bluesky-mcp is one. Nothing to build here; it is a deploy values file. Worth noting that a328d5e adds `--pin`, which is directly relevant: bluesky is the case in deploy#358 where the scope is an *argument* (the account), so a name allowlist alone does not bound it and a pin now does. **Reading B - replace the bespoke server with a guardfile.** This is the product thesis, and I assume it is what you meant. **It does not work today, and the blocker is structural rather than a missing convenience.** The guardfile `auth` block resolves a **static** value: `env`, `file`, or `literal`, via a value chain. It cannot perform a request to obtain a credential. AT Protocol needs exactly that. The deploy manifest injects a Bluesky **app password**, and the app password is not the credential the API takes - it is exchanged at `com.atproto.server.createSession` for a session token, which then expires and needs refreshing. That is a login round-trip before the first real call, and there is no construct for it: no grammar node, and no runtime hook, because `auth` is resolved once and attached to each request. So B needs one of: 1. **A token-exchange auth scheme in umbra's grammar** - real work in a shared dependency, and it wants a general design rather than an AT Protocol special case, since OAuth client-credentials has the same shape. 2. **A sidecar that holds the session** and exposes a local HTTP surface. Viable now that #59 landed, but it means writing the session-handling process, at which point most of bluesky-mcp still exists and only its MCP layer is replaced. 3. **A long-lived token in SSM, refreshed out of band.** Cheapest, and it moves an expiry problem into an operational one, which usually goes badly. I would not pick any of these without your read. Which reading did you intend?
Member

Reading B confirmed by Kai. Blocked, and re-verified rather than assumed.

guardfile.parseAuth supports exactly three schemes — header-token, bearer, query-param — and every one resolves a static value chain through env, file, or literal. There is no request-to-obtain-a-credential anywhere in the grammar, and no runtime hook for one: auth resolves and attaches to each request, it never makes one of its own.

AT Protocol needs a login round-trip. The deploy manifest injects a Bluesky app password, which is not the credential the API accepts — it is exchanged at com.atproto.server.createSession for a session token that then expires. Nothing in this runtime can perform that exchange or refresh.

For contrast, this is exactly why the sibling issues split the way they did:

  • #52 Steam — done. Static API key in a query parameter, which auth query-param already covers.
  • #51 reddit — blocked on a non-JSON response body (umbra#289), not on auth. Its credential is a tokenized URL, which is static and fine.
  • #50 bluesky — blocked on auth itself, which is the deepest of the three.

What I would need from you before building anything

The three options from my earlier comment stand, and my read has firmed up:

  1. A token-exchange auth scheme in umbra. The right general fix, and genuinely general — OAuth client-credentials has the same shape, so this is not an AT Protocol special case. It is also the largest, and it puts credential-acquisition logic into a shared engine that currently has none. That is a real design decision, not a patch.
  2. A session-holding sidecar. Viable now that #59 landed. But it means writing the process that holds and refreshes the session — at which point most of bluesky-mcp still exists and only its MCP layer has been replaced. Little is saved.
  3. A long-lived token in SSM, refreshed out of band. Cheapest, and it converts an expiry problem into a silent-breakage-at-3am problem. I would not pick it.

My recommendation: keep bluesky-mcp bespoke unless you want option 1 for its own sake. It is the one of the three where the bespoke server is carrying real weight that the guardfile model does not currently have a place for.

Worth noting separately: a328d5e's --pin does bound this server's scope if you proxy it rather than replace it, which is the deploy#358 path and is unaffected by any of the above.

Leaving open, blocked on that call.

Reading B confirmed by Kai. **Blocked**, and re-verified rather than assumed. `guardfile.parseAuth` supports exactly three schemes — `header-token`, `bearer`, `query-param` — and every one resolves a **static** value chain through `env`, `file`, or `literal`. There is no request-to-obtain-a-credential anywhere in the grammar, and no runtime hook for one: `auth` resolves and attaches to each request, it never makes one of its own. AT Protocol needs a login round-trip. The deploy manifest injects a Bluesky app password, which is not the credential the API accepts — it is exchanged at `com.atproto.server.createSession` for a session token that then expires. Nothing in this runtime can perform that exchange or refresh. For contrast, this is exactly why the sibling issues split the way they did: * **#52 Steam — done.** Static API key in a query parameter, which `auth query-param` already covers. * **#51 reddit — blocked** on a non-JSON response body (umbra#289), not on auth. Its credential is a tokenized URL, which is static and fine. * **#50 bluesky — blocked** on auth itself, which is the deepest of the three. ## What I would need from you before building anything The three options from my earlier comment stand, and my read has firmed up: 1. **A token-exchange auth scheme in umbra.** The right general fix, and genuinely general — OAuth client-credentials has the same shape, so this is not an AT Protocol special case. It is also the largest, and it puts credential-acquisition logic into a shared engine that currently has none. That is a real design decision, not a patch. 2. **A session-holding sidecar.** Viable now that #59 landed. But it means writing the process that holds and refreshes the session — at which point most of bluesky-mcp still exists and only its MCP layer has been replaced. Little is saved. 3. **A long-lived token in SSM, refreshed out of band.** Cheapest, and it converts an expiry problem into a silent-breakage-at-3am problem. I would not pick it. My recommendation: **keep bluesky-mcp bespoke** unless you want option 1 for its own sake. It is the one of the three where the bespoke server is carrying real weight that the guardfile model does not currently have a place for. Worth noting separately: a328d5e's `--pin` does bound this server's *scope* if you proxy it rather than replace it, which is the deploy#358 path and is unaffected by any of the above. Leaving open, blocked on that call.
Member

Decision recorded by Darren (director seat), 2026-08-16, from a full triage pass of this repo. Kai's call in a consult round. Closing.

The call

bluesky-mcp stays bespoke. No migration to a guardfile.

Olaf's read is accepted, and it was re-verified rather than assumed. guardfile.parseAuth supports exactly header-token, bearer, and query-param, and every one resolves a static value chain through env, file, or literal. AT Protocol needs a login round trip, exchanging an app password at com.atproto.server.createSession for a session token that then expires. Nothing in this runtime can perform that exchange or refresh it, and there is no runtime hook for one, because auth resolves and attaches to each request and never makes one of its own.

This is the one of the three sibling issues where the bespoke server carries real weight that the guardfile model has no place for.

What was rejected, and why

  • A token-exchange auth scheme in umbra. Genuinely general rather than an AT Protocol special case, since OAuth client-credentials has the same shape. Declined for now - it puts credential-acquisition logic into a shared engine that has none, for no consumer that needs it once bluesky stays bespoke. Worth its own umbra issue if a second consumer appears.
  • A session-holding sidecar. Viable since #59 landed, and it means writing the process that holds and refreshes the session, at which point most of bluesky-mcp still exists and only its MCP layer has been replaced. Little is saved.
  • A long-lived token in SSM refreshed out of band. Cheapest, and it converts an expiry problem into a silent-breakage problem.

What is unaffected

Proxying rather than replacing. serve-upstream wraps bluesky-mcp behind a name allowlist, and --pin from a328d5e bounds the account argument so it leaves the tool schema entirely. That is the deploy#358 path, it shipped, and this closure does not touch it.

Closing as decided, not as dead. Reopen if umbra grows token-exchange auth for another reason.

**Decision recorded by Darren (director seat), 2026-08-16, from a full triage pass of this repo. Kai's call in a consult round. Closing.** ## The call **bluesky-mcp stays bespoke.** No migration to a guardfile. Olaf's read is accepted, and it was re-verified rather than assumed. `guardfile.parseAuth` supports exactly `header-token`, `bearer`, and `query-param`, and every one resolves a **static** value chain through `env`, `file`, or `literal`. AT Protocol needs a login round trip, exchanging an app password at `com.atproto.server.createSession` for a session token that then expires. Nothing in this runtime can perform that exchange or refresh it, and there is no runtime hook for one, because `auth` resolves and attaches to each request and never makes one of its own. This is the one of the three sibling issues where the bespoke server carries real weight that the guardfile model has no place for. ## What was rejected, and why * **A token-exchange auth scheme in umbra.** Genuinely general rather than an AT Protocol special case, since OAuth client-credentials has the same shape. Declined for now - it puts credential-acquisition logic into a shared engine that has none, for no consumer that needs it once bluesky stays bespoke. Worth its own umbra issue if a second consumer appears. * **A session-holding sidecar.** Viable since #59 landed, and it means writing the process that holds and refreshes the session, at which point most of bluesky-mcp still exists and only its MCP layer has been replaced. Little is saved. * **A long-lived token in SSM refreshed out of band.** Cheapest, and it converts an expiry problem into a silent-breakage problem. ## What is unaffected Proxying rather than replacing. `serve-upstream` wraps bluesky-mcp behind a name allowlist, and `--pin` from a328d5e bounds the account argument so it leaves the tool schema entirely. That is the `deploy#358` path, it shipped, and this closure does not touch it. Closing as **decided**, not as dead. Reopen if umbra grows token-exchange auth for another reason.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/mcp-beaver#50
No description provided.