Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,15 @@ void rfc9728_well_known_path_must_derive_from_resource_uri() {
ProtectedResourceMetadata.wellKnownPath(
URI.create("https://api.example.com/v2/mcp")))
.isEqualTo("/.well-known/oauth-protected-resource/v2/mcp");

// Trailing slashes on the resource path are dropped — identifiers differing
// only by a trailing slash resolve to the same metadata document (RFC 9728 §3).
assertThat(
ProtectedResourceMetadata.wellKnownPath(
URI.create("https://api.example.com/mcp/")))
.isEqualTo("/.well-known/oauth-protected-resource/mcp");

assertThat(ProtectedResourceMetadata.wellKnownPath(URI.create("https://api.example.com/")))
.isEqualTo("/.well-known/oauth-protected-resource");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@ private ProtectedResourceMetadata(
* Computes the URL path at which this resource server should serve its PRM document.
*
* <p>The path is derived from the resource URI by inserting {@code
* /.well-known/oauth-protected-resource} after the authority:
* /.well-known/oauth-protected-resource} after the authority. Trailing slashes on the resource
* path are dropped, so identifiers differing only by a trailing slash resolve to the same
* metadata document (RFC 9728 §3):
*
* <pre>
* "https://api.example.com" → "/.well-known/oauth-protected-resource"
* "https://api.example.com/" → "/.well-known/oauth-protected-resource"
* "https://api.example.com/mcp" → "/.well-known/oauth-protected-resource/mcp"
* "https://api.example.com/mcp/" → "/.well-known/oauth-protected-resource/mcp"
* "https://api.example.com/v2/mcp" → "/.well-known/oauth-protected-resource/v2/mcp"
* </pre>
*
Expand All @@ -69,7 +73,15 @@ private ProtectedResourceMetadata(
*/
public static String wellKnownPath(URI resourceUri) {
String path = resourceUri.getPath();
if (path == null || path.isEmpty() || path.equals("/")) {
if (path == null || path.isEmpty()) {
return WELL_KNOWN_PREFIX;
}

// Drop trailing slashes — "/mcp/" and "/mcp" must map to the same document
while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
if (path.isEmpty()) {
return WELL_KNOWN_PREFIX;
}

Expand All @@ -81,15 +93,13 @@ public static String wellKnownPath(URI resourceUri) {
/**
* Computes the full URL of the PRM document for the given resource URI.
*
* <p>Applies the same trailing-slash normalization as {@link #wellKnownPath(URI)}.
*
* @param resourceUri the resource server URI string
* @return the full PRM document URL
*/
public static String wellKnownUrl(String resourceUri) {
String stripped =
resourceUri.endsWith("/")
? resourceUri.substring(0, resourceUri.length() - 1)
: resourceUri;
URI uri = URI.create(stripped);
URI uri = URI.create(resourceUri);
return uri.getScheme() + "://" + uri.getAuthority() + wellKnownPath(uri);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ void wellKnownPath_resourceWithPath() {
.isEqualTo("/.well-known/oauth-protected-resource/mcp");
}

@Test
void wellKnownPath_rootResourceWithTrailingSlash() {
assertThat(ProtectedResourceMetadata.wellKnownPath(URI.create("https://api.example.com/")))
.isEqualTo("/.well-known/oauth-protected-resource");
}

@Test
void wellKnownPath_resourceWithTrailingSlash_dropped() {
assertThat(
ProtectedResourceMetadata.wellKnownPath(
URI.create("https://api.example.com/mcp/")))
.isEqualTo("/.well-known/oauth-protected-resource/mcp");
}

@Test
void wellKnownPath_resourceWithDeepPath() {
assertThat(
Expand Down Expand Up @@ -117,6 +131,12 @@ void wellKnownUrl_trailingSlash_stripped() {
.isEqualTo("https://api.example.com/.well-known/oauth-protected-resource");
}

@Test
void wellKnownUrl_pathWithTrailingSlash_stripped() {
assertThat(ProtectedResourceMetadata.wellKnownUrl("https://api.example.com/mcp/"))
.isEqualTo("https://api.example.com/.well-known/oauth-protected-resource/mcp");
}

@Test
void toJson_producesValidJson() {
var prm =
Expand Down
Loading