Skip to main content
Glama
ga815647

Fellow Aiden brew.link MCP server

by ga815647

Fellow Aiden brew.link MCP server

A remote MCP server on Cloudflare Workers that turns a Fellow Aiden brew profile into a shareable brew.link URL. It exposes two tools: a local-only profile validator and an explicitly mutating tool that creates a profile through Fellow's private API and returns its share link. It works with native Streamable HTTP clients such as Codex and Claude Code, while retaining the existing Claude.ai browser-connector path.

Built with Cloudflare's agents SDK (McpAgent, a Durable Object per session) over the Streamable HTTP transport at /mcp, plus @modelcontextprotocol/sdk and zod.

Tools

Tool

What it does

create_aiden_brew_link

External write; require approval. Validates the profile, then login → get device → create profile → share against Fellow's API. Returns the brew.link URL (plain text), or a clear error.

validate_aiden_profile

Read-only. Validates the profile against every Aiden constraint without calling Fellow's API. Returns { "valid": true, "profile": {…} } or { "valid": false, "errors": [...] }.

Profile input schema

All fields are required (except profileType, which defaults to 0). Validated before any API call:

Field

Type

Constraint

profileType

integer

use 0

title

string

1–50 chars, charset A-Z a-z 0-9 space ! @ # $ % & * - + ? / . , : ) (

ratio

number

one of 14, 14.5, 15, … 20 (0.5 steps)

bloomEnabled

boolean

bloomRatio

number

one of 1, 1.5, 2, 2.5, 3

bloomDuration

integer

1–120 (seconds)

bloomTemperature

number

one of 50, 50.5, … 99 (0.5 steps, °C)

ssPulsesEnabled

boolean

ssPulsesNumber

integer

1–10

ssPulsesInterval

integer

5–60 (seconds)

ssPulseTemperatures

number[]

each one of 50…99 by 0.5; length must equal ssPulsesNumber

batchPulsesEnabled

boolean

batchPulsesNumber

integer

1–10

batchPulsesInterval

integer

5–60 (seconds)

batchPulseTemperatures

number[]

each one of 50…99 by 0.5; length must equal batchPulsesNumber

Project layout

src/
  index.ts     MCP server (McpAgent), the two tools, CORS + auth gate + routing
  fellow.ts    Fellow API client: login → devices → create profile → share (401 re-login retry)
  profile.ts   zod schema + cross-field length validation
  env.d.ts     secret typings merged into the generated Env
wrangler.jsonc  Worker config (Durable Object binding MCP_OBJECT + migration)
test-tool.ps1   end-to-end handshake test (calls a tool and prints the result)

1. Install

npm install

2. Set the three secrets

The Worker reads three secrets from env — never hardcode them. Set each in your deployed Worker:

npx wrangler secret put FELLOW_EMAIL
npx wrangler secret put FELLOW_PASSWORD
npx wrangler secret put MCP_AUTH_TOKEN
  • FELLOW_EMAIL / FELLOW_PASSWORD — your Fellow account login (the same one the Fellow app uses).

  • MCP_AUTH_TOKEN — a long random string you choose. It gates the /mcp endpoint so only your connector can call it. Generate one with:

    [Convert]::ToBase64String((1..32 | ForEach-Object { Get-Random -Max 256 }))

Windows / PowerShell gotcha: do not pipe a value into wrangler secret put (e.g. echo $x | wrangler secret put …) — PowerShell prepends a UTF‑8 BOM and the stored secret gets a hidden leading character, so logins/auth then fail mysteriously. Instead run the command with no pipe and paste the value at the interactive prompt, or pipe through cmd: cmd /c "type secret.txt" | npx wrangler secret put FELLOW_PASSWORD.

Local development

For npm run dev, copy .dev.vars.example to the gitignored .dev.vars. Use a distinct, non-production local token. Validation-only tests do not call Fellow, so their Fellow credentials should also remain isolated placeholders:

FELLOW_EMAIL=local-validation@example.invalid
FELLOW_PASSWORD=local-validation-placeholder
MCP_AUTH_TOKEN=local-validation-token-not-a-secret
npm run dev   # http://127.0.0.1:8787  (MCP at /mcp, health at /health)

Wrangler reads these values from .dev.vars; do not supply a real token with wrangler --var or expand one into the command line.

3. Deploy

npm run deploy

Wrangler prints the public URL. Your MCP endpoint is that URL + /mcp:

https://fellow-aiden-mcp.<your-workers-subdomain>.workers.dev/mcp

(If this is your first Worker, Cloudflare will prompt you to register a free *.workers.dev subdomain.)

4. Authentication model

The /mcp endpoint requires the MCP_AUTH_TOKEN shared secret, accepted two ways so every client works:

  • Authorization: Bearer <MCP_AUTH_TOKEN> header — used by curl, Codex, Claude Code, and API MCP clients.

  • ?token=<MCP_AUTH_TOKEN> query param on the URL — used by Claude.ai web, whose "Add custom connector" UI currently has no field for a bearer token or custom header (only OAuth client ID/secret). Putting the secret in the URL is the practical way to authenticate the web connector.

/health is open (no auth) for liveness checks. Requests with a missing/wrong token get HTTP 401.

5. Test end-to-end (before wiring a client)

A full test run uses four Streamable HTTP messages (initializenotifications/initializedtools/listtools/call) that share an Mcp-Session-Id. The included test-tool.ps1 does this for you and verifies each tool's read/write annotation. It reads MCP_AUTH_TOKEN inside the PowerShell process, first from the process environment and then from the gitignored .dev.vars; it does not accept a token argument and does not launch a child process containing the token.

# Local validation with isolated placeholder values in .dev.vars
# (no Fellow API call and no profile created):
./test-tool.ps1 -Url "http://127.0.0.1:8787/mcp" -ValidateOnly

# To validate a deployed endpoint without mutating Fellow, set MCP_AUTH_TOKEN in
# the process environment that launches PowerShell, then use -ValidateOnly:
./test-tool.ps1 -Url "https://fellow-aiden-mcp.<subdomain>.workers.dev/mcp" -ValidateOnly

Do not put real tokens in command arguments, wrangler --var, curl headers, or test URLs: process listings, shell history, and terminal transcripts may retain them. A full run without -ValidateOnly creates a real Fellow profile and must be performed only when that mutation is explicitly intended.

Raw curl

Health (no auth):

curl https://fellow-aiden-mcp.<subdomain>.workers.dev/health

Wrong/no token is rejected:

curl -i -X POST "https://fellow-aiden-mcp.<subdomain>.workers.dev/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
# -> HTTP/1.1 401 Unauthorized

For authenticated MCP requests, use test-tool.ps1 or a client that sources bearer credentials from an environment variable. The raw curl examples intentionally cover only unauthenticated health and rejection checks so a real credential is never expanded into curl's process arguments.

6. Add it to Codex

Codex supports remote Streamable HTTP servers and can source a bearer token from the environment. Keep the endpoint free of query secrets and use a dedicated local variable for the connector token:

# ~/.codex/config.toml
[mcp_servers.fellow-aiden]
url = "https://fellow-aiden-mcp.<your-workers-subdomain>.workers.dev/mcp"
bearer_token_env_var = "FELLOW_AIDEN_MCP_AUTH_TOKEN"
enabled = true
enabled_tools = ["create_aiden_brew_link", "validate_aiden_profile"]
default_tools_approval_mode = "prompt"

[mcp_servers.fellow-aiden.tools.validate_aiden_profile]
approval_mode = "approve"

[mcp_servers.fellow-aiden.tools.create_aiden_brew_link]
approval_mode = "prompt"

Set FELLOW_AIDEN_MCP_AUTH_TOKEN in the environment that launches Codex to the same value as the Worker's MCP_AUTH_TOKEN, then start a fresh Codex session. validate_aiden_profile may run automatically because it is local and read-only; create_aiden_brew_link remains approval-gated because it writes a real profile and share link.

Do not place the token in the URL or directly in config.toml. The query-token compatibility path described below exists only for the browser connector that cannot send a custom bearer header.

7. Add it to Claude.ai (web) as a custom connector

  1. Go to Settings → Connectors → Add custom connector.

  2. Remote MCP server URL: paste your endpoint with the token in the URL:

    https://fellow-aiden-mcp.<subdomain>.workers.dev/mcp?token=<MCP_AUTH_TOKEN>
  3. Leave Advanced settings (OAuth Client ID/Secret) blank — this server uses the URL token, not OAuth.

  4. Save. Claude will connect and discover create_aiden_brew_link and validate_aiden_profile. In a chat, enable the connector and ask Claude to create a brew profile; it returns a brew.link.

Why the token is in the URL: Claude.ai's web connector UI has no bearer-token/header field (only OAuth). The query-param token is the supported way to authenticate. Treat the full URL (with token) as a secret. If you'd rather not put a secret in a URL, the alternative is to implement OAuth via Cloudflare's workers-oauth-provider — a larger change.

Claude Code / API connector

Use the header form with the client's secure environment-variable or secret-store support. Do not embed the bearer value in a CLI argument, checked-in configuration, or URL.

Scripts

npm run dev         wrangler dev (local, reads .dev.vars)
npm run deploy      wrangler deploy
npm run type-check  tsc --noEmit
npm run cf-typegen  regenerate worker-configuration.d.ts after wrangler.jsonc changes

Notes & limitations

  • Single brewer assumed. The brewer id is read from GET /devices?dataType=real element [0]. Multi-brewer accounts would need a selector.

  • 401 handling. Any Fellow call returning 401 triggers one re-login + retry, per Fellow's API behavior.

  • No persistence. The server is stateless per call (the Durable Object only backs MCP session transport); nothing about your brews is stored.

  • Unofficial API. Endpoints/headers are reverse-engineered from the open-source 9b/fellow-aiden package and may change without notice.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ga815647/fellow-aiden-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server