Skip to content

Commit b69096a

Browse files
authored
Merge branch 'master' into todo-tool
2 parents 94adb9b + 10cf09e commit b69096a

3 files changed

Lines changed: 52 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
## Unreleased
44

5-
- Allow manually starting MCP servers even when configured with `disabled: true` (still not auto-started).
65
- Add Task tool #246
76

7+
## 0.109.6
8+
9+
- Allow manually starting MCP servers even when configured with `disabled: true` (still not auto-started).
10+
- Fix MCP OAuth discovery for servers that don't support HEAD requests (e.g. Glean) by falling back to POST, and fix two-step metadata discovery (PRM → Authorization Server Metadata per RFC 8414).
11+
812
## 0.109.5
913

1014
- Fix clear messages to reset usage tokens as well.

resources/ECA_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.109.5
1+
0.109.6

src/eca/oauth.clj

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,56 @@
110110
:error-message error}))
111111
(response/content-type "text/html"))))))
112112

113+
(defn ^:private probe-auth
114+
"Probe the MCP server for a 401 auth challenge.
115+
Tries HEAD first; falls back to POST if HEAD returns non-401
116+
(some servers like Glean don't support HEAD)."
117+
[^String url]
118+
(let [head-response (http/head url {:timeout 10000
119+
:throw-exceptions? false})]
120+
(if (= 401 (:status head-response))
121+
head-response
122+
(let [post-response (http/post url {:timeout 10000
123+
:throw-exceptions? false
124+
:headers {"Content-Type" "application/json"
125+
"Accept" "application/json, text/event-stream"}
126+
:body "{}"})]
127+
(when (= 401 (:status post-response))
128+
post-response)))))
129+
113130
(defn oauth-info [^String url]
114131
(let [base-url (url->base-url url)
115-
{:keys [status headers]} (http/head
116-
url
117-
{:timeout 10000
118-
:throw-exceptions? false})]
119-
(when (= 401 status)
132+
auth-response (probe-auth url)]
133+
(when-let [headers (:headers auth-response)]
120134
(let [callback-port (get-free-port)
121135
redirect-uri (format "http://localhost:%s/auth/callback" callback-port)
122136
www-authenticate (some-> (get headers "www-authenticate") parse-www-authenticate)
123-
auth-resource-meta (:body (http/get
124-
(or (:resource_metadata www-authenticate)
125-
(str base-url "/.well-known/oauth-authorization-server"))
126-
{:timeout 10000
127-
:throw-exceptions? false
128-
:as :json})
129-
:body)
130-
auth-server (first (:authorization_servers auth-resource-meta))
131-
base-auth-endpoint (or (:authorization_endpoint auth-resource-meta)
137+
;; Step 1: Fetch resource metadata (PRM) or auth server metadata directly
138+
first-meta (some-> (http/get
139+
(or (:resource_metadata www-authenticate)
140+
(str base-url "/.well-known/oauth-authorization-server"))
141+
{:timeout 10000
142+
:throw-exceptions? false
143+
:as :json})
144+
:body)
145+
auth-server (first (:authorization_servers first-meta))
146+
;; Step 2: If PRM returned authorization_servers but no token_endpoint,
147+
;; fetch the actual Authorization Server Metadata (RFC 8414)
148+
auth-server-meta (when (and auth-server (not (:token_endpoint first-meta)))
149+
(let [uri (java.net.URI. ^String auth-server)
150+
well-known-url (str (.getScheme uri) "://" (.getAuthority uri)
151+
"/.well-known/oauth-authorization-server"
152+
(.getPath uri))]
153+
(some-> (http/get well-known-url
154+
{:timeout 10000
155+
:throw-exceptions? false
156+
:as :json})
157+
:body)))
158+
;; Merge: auth server metadata takes precedence, PRM as fallback
159+
meta (merge first-meta auth-server-meta)
160+
base-auth-endpoint (or (:authorization_endpoint meta)
132161
(str auth-server "/authorize"))
133-
new-client-id (when-let [reg-endpoint (:registration_endpoint auth-resource-meta)]
162+
new-client-id (when-let [reg-endpoint (:registration_endpoint meta)]
134163
(let [res
135164
(http/post
136165
reg-endpoint
@@ -153,11 +182,11 @@
153182
:client_id client-id
154183
:code_challenge_method "S256"
155184
:code_challenge challenge
156-
:scopes (:scopes_supported auth-resource-meta)
185+
:scopes (:scopes_supported meta)
157186
:state verifier
158187
:redirect_uri redirect-uri})]
159188
{:callback-port callback-port
160-
:token-endpoint (or (:token_endpoint auth-resource-meta)
189+
:token-endpoint (or (:token_endpoint meta)
161190
(str auth-server "/access_token"))
162191
:verifier verifier
163192
:client-id client-id

0 commit comments

Comments
 (0)