Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ jobs:
echo "::notice::Skipping authenticated preview smoke because MCP_EVAL_TOKEN is unavailable"
fi
- uses: actions/checkout@v4
if: steps.credential.outputs.available == 'true'
- uses: actions/setup-node@v6
if: steps.credential.outputs.available == 'true'
with:
Expand All @@ -193,7 +192,6 @@ jobs:
if: steps.credential.outputs.available == 'true'
run: npm ci
- name: Wait for this commit's Vercel preview

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Restore the credential guard around preview-only steps. The metadata assertion requests the fixed production URL in AUTHORIZATION_SERVER_METADATA_URL and does not consume either the Vercel deployment or steps.preview.outputs.endpoint. However, this change also removes if: steps.credential.outputs.available == 'true' from Wait for this commit's Vercel preview, Resolve Vercel preview endpoint, and Verify preview still belongs to this commit. When MCP_EVAL_TOKEN is unavailable, both matrix jobs now wait for and validate a Vercel preview even though the authenticated preview smoke is skipped; only the 2026-07-28 job then performs the independent production metadata check. A delayed deployment, missing Vercel status, or missing/churned bot comment can therefore fail CI for reasons unrelated to this assertion. Please restore the credential guard on those three preview-only steps so the production metadata assertion can run independently. Alternatively, if the preview is intended to be tested, use the resolved preview origin in the assertion.

if: steps.credential.outputs.available == 'true'
id: vercel
env:
GH_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -224,7 +222,6 @@ jobs:
fi
echo "inspector-url=${inspector_url}" >> "$GITHUB_OUTPUT"
- name: Resolve Vercel preview endpoint
if: steps.credential.outputs.available == 'true'
id: preview
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -239,6 +236,18 @@ jobs:
exit 1
fi
echo "endpoint=${preview_url}/mcp" >> "$GITHUB_OUTPUT"
- name: Verify authorization server advertises refresh support
if: matrix.protocol-version == '2026-07-28'
env:
AUTHORIZATION_SERVER_METADATA_URL: https://mcp.terminal49.com/.well-known/oauth-authorization-server
run: |
curl --fail --silent --show-error --location --max-time 30 \
"$AUTHORIZATION_SERVER_METADATA_URL" \
| jq --exit-status '
(.grant_types_supported // [] | index("refresh_token")) != null
and
(.scopes_supported // [] | index("offline_access")) != null
' > /dev/null
- name: POST handshake and tools/list to Vercel preview
if: steps.credential.outputs.available == 'true'
env:
Expand All @@ -247,7 +256,6 @@ jobs:
MCP_PROTOCOL_VERSION: ${{ matrix.protocol-version }}
run: npm run test:http-protocol --workspace @terminal49/mcp
- name: Verify preview still belongs to this commit
if: steps.credential.outputs.available == 'true'
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
Expand Down
54 changes: 47 additions & 7 deletions packages/mcp/WORKOS_MCP_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,27 @@ In the WorkOS environment referenced by `WORKOS_AUTHORIZATION_SERVER_URL`:

## 3. Smoke tests

### Automated metadata smoke

```sh
ISSUER="<WORKOS_AUTHORIZATION_SERVER_URL>"

# AS metadata: 200 with registration_endpoint + S256
curl -s "$ISSUER/.well-known/oauth-authorization-server" \
| jq '{registration_endpoint, code_challenge_methods_supported}'

# DCR is open: expect 201 + client_id
curl -s -X POST "$ISSUER/oauth2/register" \
# AS metadata: 200 with registration_endpoint, S256, and refresh support
curl --fail --silent --show-error --location \
"$ISSUER/.well-known/oauth-authorization-server" \
| jq --exit-status '
.registration_endpoint != null
and (.code_challenge_methods_supported // [] | index("S256")) != null
and (.grant_types_supported // [] | index("refresh_token")) != null
and (.scopes_supported // [] | index("offline_access")) != null
' > /dev/null

# DCR is open: expect 201 + client_id. A refresh-capable client must register
# both authorization_code and refresh_token.
curl --fail --silent --show-error -X POST "$ISSUER/oauth2/register" \
-H 'Content-Type: application/json' \
-d '{"client_name":"smoke","redirect_uris":["https://example.com/cb"],"grant_types":["authorization_code"],"response_types":["code"]}'
-d '{"client_name":"smoke","redirect_uris":["https://example.com/cb"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}' \
| jq --exit-status '.client_id != null' > /dev/null

# PRM: resource + authorization_servers
curl -s https://mcp.terminal49.com/.well-known/oauth-protected-resource | jq
Expand All @@ -85,6 +95,36 @@ curl -si -X POST https://mcp.terminal49.com/mcp \
# aud == https://mcp.terminal49.com
```

CI follows the preview deployment's authorization-server metadata redirect and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Describe this as a production metadata check. CI no longer follows the preview deployment's metadata redirect: the workflow hard-codes https://mcp.terminal49.com/.well-known/oauth-authorization-server, so it follows the production deployment's redirect instead. Please describe this as a live/production metadata check, or change the workflow to request the resolved preview origin. As written, maintainers may incorrectly assume a PR's redirect handler and preview environment configuration are covered.

fails if `grant_types_supported` omits `refresh_token` or `scopes_supported`
Comment on lines +98 to +99

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Correct the claim that CI checks the preview redirect

The workflow does not follow the preview deployment’s metadata redirect: .github/workflows/ci.yml hard-codes https://mcp.terminal49.com/.well-known/oauth-authorization-server. Consequently, a preview with a broken redirect or incorrect WorkOS issuer can still pass while production remains healthy, so this operational guide gives maintainers false assurance about what CI validates; describe this as a production metadata check or change the workflow to query the resolved preview origin.

Useful? React with 👍 / 👎.

Comment on lines +98 to +99

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Correct the validated environment

This text says CI follows the preview deployment’s authorization-server redirect, but the workflow requests the fixed production mcp.terminal49.com URL. Maintainers can therefore mistake successful CI for evidence that preview-specific OAuth configuration advertises refresh support.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/mcp/WORKOS_MCP_SETUP.md
Line: 98-99

Comment:
**Correct the validated environment**

This text says CI follows the preview deployment’s authorization-server redirect, but the workflow requests the fixed production `mcp.terminal49.com` URL. Maintainers can therefore mistake successful CI for evidence that preview-specific OAuth configuration advertises refresh support.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

omits `offline_access`. These capabilities belong in the WorkOS authorization
server metadata, not the Terminal49 Protected Resource Metadata (PRM).

### Human refresh smoke

Refresh requires a real user authorization. Do not store a user password in CI.
Run the local [OAuth test client](./OAUTH_TEST_CLIENT.md), which requests
`openid profile email offline_access` and registers both
`authorization_code` and `refresh_token` grant types.

1. Click **Authorize** and complete the WorkOS sign-in.
2. In the redacted token output, confirm the response contains a
`refresh_token` and the access token `aud` is
`https://mcp.terminal49.com`.
3. Click **Refresh**. The client posts `grant_type=refresh_token`, the current
refresh token, and `resource=https://mcp.terminal49.com` to the discovered
token endpoint using the same registered client.
4. Confirm the refreshed response contains a new `access_token` whose `aud`
is still `https://mcp.terminal49.com`.
5. Click **Tools List** and confirm `tools/list` succeeds with the refreshed
bearer token.
6. If WorkOS returns a replacement `refresh_token`, retain it for the next
refresh. The local test client does this automatically; it keeps the
previous refresh token only when the response omits a replacement.

The test client redacts tokens and client secrets in its browser output. Do not
copy credentials into CI output, shared shell history, or issue comments.

## 4. Per-client notes

- **ChatGPT (Apps SDK)** and **Claude connectors** connect **server-side** — no
Expand Down
Loading