Skip to content

chore: merge upstream chrisdoc/hevy-mcp v1.23.8 - #317

Closed
aaearon wants to merge 4 commits into
chrisdoc:mainfrom
aaearon:chore/merge-upstream-1.23.8
Closed

chore: merge upstream chrisdoc/hevy-mcp v1.23.8#317
aaearon wants to merge 4 commits into
chrisdoc:mainfrom
aaearon:chore/merge-upstream-1.23.8

Conversation

@aaearon

@aaearon aaearon commented May 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Merge upstream chrisdoc/hevy-mcp v1.23.8 into fork main.
  • Picks up body-measurements MCP tools (upstream PR feat: add body measurements MCP tools #306): get-body-measurements, get-body-measurement, create-body-measurement, update-body-measurement.
  • Picks up regenerated Kubb client (upstream PR build: update openapi spec and regenerate api client #305): new body-measurements + user-info API endpoints, Kubb param-order fix (data before headers), rep_range nullundefined.
  • Preserves local Streamable HTTP and OAuth 2.1 transports unchanged.
  • Adopts upstream package.json version 1.23.8.

Test plan

  • npm run check:types — clean
  • npm run check — 0 warnings, 0 errors
  • npx vitest run --exclude tests/integration/** — 21 test files, 196 tests passed
  • npm run build — clean
  • OAuth transport boots; /.well-known/oauth-authorization-server responds; unauthenticated /mcp returns 401

✨ PR Description

Purpose: Merge upstream MCP SDK changes from chrisdoc/hevy-mcp v1.23.8, adding HTTP and OAuth transport modes alongside existing stdio transport for flexible deployment options.

Main changes:

  • Added HTTP and HTTP+OAuth transport modes with StreamableHTTPServerTransport, OAuth 2.1 provider using SQLite, and password-gated consent flow
  • Implemented new CLI flags (--transport, --port, --issuer-url) with environment variable fallbacks for MCP_ISSUER_URL and MCP_AUTH_PASSWORD
  • Added Docker Compose deployment configuration with Dockerfile.oauth, Traefik routing, and comprehensive test coverage for OAuth flows

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Description using Guidelines Learn how

Summary by CodeRabbit

  • New Features

    • Added HTTP transport mode with configurable port via --transport=http and --port flags
    • Added HTTP+OAuth transport mode with password-gated OAuth 2.1 authorization server
    • Support for environment variable configuration: MCP_ISSUER_URL, MCP_AUTH_PASSWORD, OAUTH_DB_PATH
  • Documentation

    • Updated README with transport modes overview and HTTP mode quick start guide
    • Added HTTP+OAuth deployment documentation with Docker Compose workflow
  • Tests

    • Added comprehensive test coverage for HTTP servers, OAuth flows, and consent handling

aaearon and others added 4 commits April 7, 2026 19:52
* feat: implement Streamable HTTP transport

Replace the HTTP/SSE stub with a real StreamableHTTPServerTransport
server. Sessions are tracked per-connection; unknown session IDs return
404 to match SDK semantics.

- Add `transport` and `port` to HevyConfig; parse --transport and --port
- Replace createHttpServer stub with startHttpServer() returning http.Server
- Branch runServer() on --transport=http vs stdio (default)
- Update unit and integration tests; add config parsing tests
- Document HTTP mode in README, remove "stdio only" deprecation notice

* fix: address Copilot review comments on HTTP transport

- Wrap async request handler in try/catch; send 500 JSON on unhandled errors
- Fix TDZ: declare `let mcpServer` before StreamableHTTPServerTransport construction
- Normalize mcp-session-id header to handle string[] values
- Add 1 MiB body size limit; drain socket before responding 413 / 400
- Set Content-Type + Content-Length on all JSON error responses via helper
- Add HTTP behavior unit tests: non-/mcp 404, unknown session 404 JSON, oversized body 413
- Wrap client.close() in finally block in integration test
- Validate --port range (0-65535) in parseConfig; add test
* feat: implement OAuth 2.1 HTTP transport (http+oauth)

Adds password-gated OAuth 2.1 authorization server + MCP resource
server compatible with claude.ai Connectors.

- SQLiteOAuthProvider: full OAuthServerProvider implementation backed
  by better-sqlite3 (WAL mode, 4-table schema, token rotation, family
  revocation)
- consent router: timing-safe password check, HTML consent form
- config: http+oauth transport, --issuer-url / MCP_ISSUER_URL
- httpServer: startOAuthHttpServer using SDK mcpAuthRouter +
  requireBearerAuth
- index: http+oauth branch in runServer()
- Tests: 43 new tests across oauthProvider, consent, config, and
  httpServer.oauth
- Dockerfile.oauth + docker-compose.yml for local deployment

* style: fix oxfmt formatting in httpServer.ts

* fix: address PR review comments

- consent: validate session ID as 32-hex, HTML-escape all dynamic values (XSS)
- consent: exact-length check before timingSafeEqual to prevent prefix bypass
- oauthProvider: validate redirectUri before consuming auth code; wrap
  delete+insert in a single transaction to prevent race/DoS
- oauthProvider: move refresh token read+delete+insert into one transaction
  to prevent concurrent use producing multiple new tokens
- oauthProvider: add TTL (10 min) and max-size cap (1000) to pendingSessions
  to prevent memory leaks from abandoned authorization flows
- httpServer: add express.json() with 1 MiB limit before /mcp route so
  req.body is parsed in OAuth server
- httpServer.oauth.test: save/restore MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL
  in beforeAll/afterAll to prevent env leak across test files
- oauthProvider.test: actually force expiry via DB update to test expired
  auth code path
- docker-compose.yml: remove external network dependency; use default bridge
Brings in body-measurements MCP tools (chrisdoc#306) and the regenerated
Kubb client (chrisdoc#305) from upstream; preserves fork-only Streamable
HTTP and OAuth 2.1 transports.
Copilot AI review requested due to automatic review settings May 9, 2026 06:01
@aaearon

aaearon commented May 9, 2026

Copy link
Copy Markdown
Contributor Author

Opened in wrong repo — this was meant to target the fork (aaearon/hevy-mcp).

@aaearon aaearon closed this May 9, 2026
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Rate limit exceeded

@aaearon has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 57 minutes and 58 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3876640d-8a68-409c-8998-8935039e29a5

📥 Commits

Reviewing files that changed from the base of the PR and between 83884f6 and c48aff1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • CLAUDE.md
  • Dockerfile.oauth
  • README.md
  • deploy/traefik-hevy-mcp.yml
  • docker-compose.yml
  • package.json
  • src/index.ts
  • src/utils/config.test.ts
  • src/utils/config.ts
  • src/utils/consent.test.ts
  • src/utils/consent.ts
  • src/utils/httpServer.oauth.test.ts
  • src/utils/httpServer.test.ts
  • src/utils/httpServer.ts
  • src/utils/oauthProvider.test.ts
  • src/utils/oauthProvider.ts
  • tests/integration/http-transport.integration.test.ts
📝 Walkthrough

Walkthrough

This PR adds OAuth 2.1 authorization to the MCP server with a password-gated consent step and local SQLite persistence, enabling authenticated HTTP transport alongside stdio and HTTP modes. The implementation includes configuration extensions, a SQLite OAuth provider, a password-protected consent router, and dual HTTP server implementations.

Changes

OAuth 2.1 Integration

Layer / File(s) Summary
Configuration Schema
src/utils/config.ts, src/utils/config.test.ts
HevyConfig adds optional transport, port, and issuerUrl fields. parseConfig parses CLI arguments and env fallback for all three fields. New assertIssuerUrl validator.
OAuth Provider Implementation
src/utils/oauthProvider.ts, src/utils/oauthProvider.test.ts
SQLiteOAuthProvider implements OAuthServerProvider with lazy SQLite init (WAL mode), client registration, pending-session TTL eviction, authorization code exchange, refresh token rotation with family tracking, and token revocation.
Password-Gated Consent Router
src/utils/consent.ts, src/utils/consent.test.ts
createConsentRouter renders HTML form, validates session IDs (32-hex regex), compares passwords using constant-time comparison, creates authorization codes, and redirects with code+state.
HTTP Server Implementations
src/utils/httpServer.ts, src/utils/httpServer.test.ts, src/utils/httpServer.oauth.test.ts
startHttpServer serves /mcp with session-based MCP routing; startOAuthHttpServer adds Express OAuth middleware and consent routing. Both enforce 1 MiB body limits and return JSON-RPC errors.
Entry Point Orchestration
src/index.ts
runServer() dispatches to startOAuthHttpServer, startHttpServer, or StdioServerTransport based on cfg.transport.
Integration Tests
tests/integration/http-transport.integration.test.ts
Test replaces old stub with live integration: starts ephemeral HTTP server, connects StreamableHTTPClientTransport, calls listTools, verifies noop tool appears.
Deployment Configuration
Dockerfile.oauth, docker-compose.yml, deploy/traefik-hevy-mcp.yml
Dockerfile builds Node image with http+oauth transport on port 8000; docker-compose maps to port 8012 with /data volume for SQLite; Traefik routes hevymcp.ams.iosharp.com to backend.
Package Dependencies
package.json
Adds better-sqlite3, express (runtime) and @types/better-sqlite3, @types/express (dev). Updates supertest from ^7.2.0 to ^7.2.2.
Documentation
README.md, CLAUDE.md
README adds HTTP mode to Quick Start table and documents Transport Modes (stdio, HTTP). CLAUDE adds HTTP+OAuth Transport section with env vars, startup command, verification steps, and Docker Compose workflow.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

30 min review

Poem

🐰 A bunny hops through OAuth flows,
With SQLite where the token grows,
Consent forms rendered, passwords clear,
HTTP transports bring cheer!
Express and better-sqlite aligned,
Authorization deeply signed. 🔐

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gitstream-cm gitstream-cm Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ PR Review

LGTM

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how

@greptile-apps

greptile-apps Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR merges upstream chrisdoc/hevy-mcp v1.23.8 into the fork and adds a locally-developed HTTP+OAuth 2.1 transport that exposes the MCP server behind a password-gated authorization server backed by SQLite.

  • New OAuth transport (src/utils/httpServer.ts, oauthProvider.ts, consent.ts): Implements dynamic client registration, PKCE auth-code flow with atomic code consumption, refresh-token rotation with family-wide revocation, and a timing-safe consent form — all stored in a SQLite DB (better-sqlite3).
  • New deployment artifacts (Dockerfile.oauth, docker-compose.yml, deploy/traefik-hevy-mcp.yml): Containerised deployment for the OAuth mode; the Traefik config file accidentally commits a personal public hostname and private LAN IP.
  • Upstream body-measurements tools and Kubb client regeneration picked up from upstream PRs build: update openapi spec and regenerate api client #305/feat: add body measurements MCP tools #306 via the version bump to 1.23.8.

Confidence Score: 4/5

Safe to merge with one item to address before pushing to a public remote: the Traefik config file exposes personal infrastructure.

The OAuth implementation is well-structured — code consumption is atomic, refresh tokens are rotated with family-wide revocation, the consent form uses timing-safe comparison, and all user-controlled values are HTML-escaped. The one concrete issue is deploy/traefik-hevy-mcp.yml, which commits a personal public hostname and private LAN IP to the repository. The resource indicator being silently dropped during refresh token rotation is a minor gap unlikely to affect current behaviour but worth fixing for correctness.

deploy/traefik-hevy-mcp.yml should be sanitised or removed before the branch is published publicly; src/utils/oauthProvider.ts line 322 deserves a second look regarding resource propagation on refresh.

Security Review

  • Personal network topology exposeddeploy/traefik-hevy-mcp.yml commits a personal public hostname and a private LAN IP to a public repository, revealing home-network addressing.
  • Password-gated OAuth consent formconsent.ts correctly uses timingSafeEqual for password comparison and properly short-circuits on an empty MCP_AUTH_PASSWORD, preventing both timing attacks and login when no password is configured.
  • HTML injection mitigatedescapeHtml() in consent.ts escapes all user-controlled values before interpolation into the HTML form.
  • Authorization code replay preventionexchangeAuthorizationCode atomically deletes the code and mints tokens inside a single SQLite transaction, preventing double-spend.
  • Refresh token rotation — both exchangeRefreshToken and revokeToken use family-wide deletion, preventing replay of old refresh tokens.

Important Files Changed

Filename Overview
deploy/traefik-hevy-mcp.yml New Traefik routing config that hardcodes a personal public hostname and a private LAN IP, inadvertently publishing home-network topology to a public repo.
src/utils/oauthProvider.ts New SQLite-backed OAuth 2.1 provider; authorization code replay prevention is atomic, refresh token rotation is family-wide, but the resource indicator is silently dropped on refresh exchanges.
src/utils/consent.ts Password-gated consent form using timing-safe comparison, proper HTML escaping, and a consumed-once session mechanism.
src/utils/httpServer.ts Adds startOAuthHttpServer alongside the existing startHttpServer; both handle per-session MCP transport lifecycle correctly.
src/index.ts Adds http+oauth transport branch and wires startOAuthHttpServer; logic is straightforward and consistent with existing transport handling.
src/utils/config.ts Extends config parsing with --transport=http+oauth, --issuer-url, MCP_ISSUER_URL, and assertIssuerUrl; well-tested.
docker-compose.yml New Docker Compose service wiring Dockerfile.oauth with a persistent volume for the SQLite DB; correctly externalizes secrets via .env.
Dockerfile.oauth New Dockerfile for the OAuth transport; functional but includes devDependencies in the image.

Reviews (1): Last reviewed commit: "chore: merge upstream chrisdoc/hevy-mcp ..." | Re-trigger Greptile

Comment on lines +1 to +14
http:
routers:
hevy-mcp:
rule: "Host(`hevymcp.ams.iosharp.com`)"
entryPoints:
- web-secure
service: hevy-mcp
tls:
certResolver: myresolver
services:
hevy-mcp:
loadBalancer:
servers:
- url: "http://192.168.178.42:8012"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Hardcoded personal infrastructure details committed to a public repository

This file contains a personal public hostname (hevymcp.ams.iosharp.com) and a private LAN IP (192.168.178.42:8012), effectively publishing your home network topology. Anyone reading this public fork can identify a live service at that domain and infer your local network layout. Consider either removing this file, adding it to .gitignore, or replacing the values with generic placeholders (e.g. your-domain.example.com / YOUR_HOST_IP:8012) before pushing to a public repo.

Comment on lines +312 to +324
this.db
.prepare(
`INSERT INTO access_tokens (token, client_id, scopes, expires_at, resource, family_id)
VALUES (?, ?, ?, ?, ?, ?)`,
)
.run(
accessTok,
client.client_id,
JSON.stringify(effectiveScopes),
now + ACCESS_TOKEN_TTL,
null,
familyId,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Pass the _resource parameter through to the new access token instead of always inserting null, so resource-bound tokens remain bound after rotation.

Suggested change
this.db
.prepare(
`INSERT INTO access_tokens (token, client_id, scopes, expires_at, resource, family_id)
VALUES (?, ?, ?, ?, ?, ?)`,
)
.run(
accessTok,
client.client_id,
JSON.stringify(effectiveScopes),
now + ACCESS_TOKEN_TTL,
null,
familyId,
);
this.db
.prepare(
`INSERT INTO access_tokens (token, client_id, scopes, expires_at, resource, family_id)
VALUES (?, ?, ?, ?, ?, ?)`,
)
.run(
accessTok,
client.client_id,
JSON.stringify(effectiveScopes),
now + ACCESS_TOKEN_TTL,
_resource?.toString() ?? null,
familyId,
);

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Merges upstream chrisdoc/hevy-mcp v1.23.8 into the fork while keeping/advancing the fork’s HTTP transports by adding Streamable HTTP and an OAuth 2.1–backed HTTP mode (with SQLite persistence + password-gated consent UI), plus corresponding config parsing, docs, and tests.

Changes:

  • Added Streamable HTTP server support (--transport=http) and HTTP+OAuth mode (--transport=http+oauth) with consent flow and SQLite-backed token storage.
  • Extended CLI configuration parsing to support --transport, --port, and --issuer-url (plus MCP_ISSUER_URL).
  • Added/updated unit + integration tests and refreshed docs/deployment artifacts for the new transport options.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/integration/http-transport.integration.test.ts Integration test that connects via Streamable HTTP and lists tools.
src/utils/oauthProvider.ts SQLite-backed OAuth provider implementation (auth codes, access/refresh tokens, revocation).
src/utils/oauthProvider.test.ts Unit tests for OAuth provider flows (clients, codes, refresh rotation, revocation).
src/utils/httpServer.ts Streamable HTTP server and Express-based OAuth HTTP server entrypoints.
src/utils/httpServer.test.ts Unit tests for Streamable HTTP server (404s, oversized body handling).
src/utils/httpServer.oauth.test.ts Unit tests for OAuth router + bearer auth middleware behavior.
src/utils/consent.ts Password-gated consent UI + redirect-to-client with auth code.
src/utils/consent.test.ts Unit tests for consent UI/session consumption and password handling.
src/utils/config.ts CLI/env config parsing and assertions for API key + issuer URL.
src/utils/config.test.ts Unit tests for new config flags and issuer URL assertion behavior.
src/index.ts Wires new transport modes into runtime entrypoint.
README.md Adds Streamable HTTP docs (but currently doesn’t cover http+oauth).
package.json Adds dependencies for OAuth HTTP mode (express, better-sqlite3, supertest types).
package-lock.json Lockfile updates for added dependencies.
Dockerfile.oauth Container build/run for --transport=http+oauth.
docker-compose.yml Compose setup with persisted OAuth DB volume.
deploy/traefik-hevy-mcp.yml Traefik router/service config for an HTTP deployment.
CLAUDE.md Documents http+oauth transport usage, env vars, and Docker compose flow.

Comment on lines +319 to +323
client.client_id,
JSON.stringify(effectiveScopes),
now + ACCESS_TOKEN_TTL,
null,
familyId,
Comment thread src/utils/httpServer.ts

type Session = {
transport: StreamableHTTPServerTransport;
server: McpServer;
Comment thread src/utils/config.ts
"Issuer URL is required for http+oauth transport. Provide it via the MCP_ISSUER_URL environment variable or the --issuer-url=URL command argument.",
);
process.exit(1);
}
Comment thread README.md
Comment on lines +154 to +168
## Transport Modes

`hevy-mcp` supports two transport modes:

### stdio (default)

The default mode. Used by all MCP clients that launch the server as a subprocess:

```bash
HEVY_API_KEY=your_key npx -y hevy-mcp
```

### Streamable HTTP

### Stdio Only
Use `--transport=http` to start a Streamable HTTP server. The server listens on `/mcp` and manages one MCP session per client connection.
Comment on lines +3 to +14
hevy-mcp:
rule: "Host(`hevymcp.ams.iosharp.com`)"
entryPoints:
- web-secure
service: hevy-mcp
tls:
certResolver: myresolver
services:
hevy-mcp:
loadBalancer:
servers:
- url: "http://192.168.178.42:8012"

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request re-introduces HTTP transport support and implements a new http+oauth transport mode, featuring a password-gated OAuth 2.1 authorization server and a SQLite-backed provider. The update includes new Docker and Traefik configurations, expanded documentation, and a suite of integration tests for the authentication flows. Feedback identifies a potential issue in the HTTP server where a strict URL check might fail if query parameters or trailing slashes are present, suggesting a more robust pathname-based validation.

Comment thread src/utils/httpServer.ts
Comment on lines +82 to +85
if (req.url !== "/mcp") {
res.writeHead(404).end();
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current check req.url !== "/mcp" is too strict and will fail for valid requests that include query parameters (e.g., /mcp?foo=bar) or trailing slashes (e.g., /mcp/). It is recommended to parse the URL to check the pathname instead.

Suggested change
if (req.url !== "/mcp") {
res.writeHead(404).end();
return;
}
const url = new URL(req.url ?? "/", "http://localhost");
if (url.pathname !== "/mcp") {

@aaearon
aaearon deleted the chore/merge-upstream-1.23.8 branch August 12, 2026 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants