@@ -29,7 +29,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult
2929import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
3030import io.modelcontextprotocol.kotlin.sdk.types.TextResourceContents
3131import io.modelcontextprotocol.kotlin.test.utils.actualPort
32- import kotlinx.coroutines.Dispatchers
3332import kotlinx.coroutines.runBlocking
3433import java.util.UUID
3534import kotlin.test.Test
@@ -81,90 +80,86 @@ abstract class AbstractAuthenticationTest {
8180 abstract fun createClientTransport (baseUrl : String , user : String , pass : String ): Transport
8281
8382 @Test
84- fun `mcp behind basic auth rejects unauthenticated requests with 401` () {
85- runBlocking(Dispatchers .IO ) {
86- val server = embeddedServer(ServerCIO , host = HOST , port = 0 ) {
87- configurePlugins()
88- install(Authentication ) {
89- basic(AUTH_REALM ) {
90- validate { credentials ->
91- if (credentials.name == validUser && credentials.password == validPassword) {
92- UserIdPrincipal (credentials.name)
93- } else {
94- null
95- }
83+ fun `mcp behind basic auth rejects unauthenticated requests with 401` (): Unit = runBlocking {
84+ val server = embeddedServer(ServerCIO , host = HOST , port = 0 ) {
85+ configurePlugins()
86+ install(Authentication ) {
87+ basic(AUTH_REALM ) {
88+ validate { credentials ->
89+ if (credentials.name == validUser && credentials.password == validPassword) {
90+ UserIdPrincipal (credentials.name)
91+ } else {
92+ null
9693 }
9794 }
9895 }
99- routing {
100- authenticate( AUTH_REALM ) {
101- registerMcpServer {
102- createMcpServer { principal< UserIdPrincipal >()?.name }
103- }
96+ }
97+ routing {
98+ authenticate( AUTH_REALM ) {
99+ registerMcpServer {
100+ createMcpServer { principal< UserIdPrincipal >()?.name }
104101 }
105102 }
106- }.startSuspend(wait = false )
107-
108- val httpClient = HttpClient (ClientCIO )
109- try {
110- httpClient.get(" http://$HOST :${server.actualPort()} " ).status shouldBe HttpStatusCode .Unauthorized
111- } finally {
112- httpClient.close()
113- server.stopSuspend(500 , 1000 )
114103 }
104+ }.startSuspend(wait = false )
105+
106+ val httpClient = HttpClient (ClientCIO )
107+ try {
108+ httpClient.get(" http://$HOST :${server.actualPort()} " ).status shouldBe HttpStatusCode .Unauthorized
109+ } finally {
110+ httpClient.close()
111+ server.stopSuspend(500 , 1000 )
115112 }
116113 }
117114
118115 @Test
119- fun `authenticated mcp client can read resource scoped to principal` () {
120- runBlocking(Dispatchers .IO ) {
121- val server = embeddedServer(ServerCIO , host = HOST , port = 0 ) {
122- configurePlugins()
123- install(Authentication ) {
124- basic(AUTH_REALM ) {
125- validate { credentials ->
126- if (credentials.name == validUser && credentials.password == validPassword) {
127- UserIdPrincipal (credentials.name)
128- } else {
129- null
130- }
116+ fun `authenticated mcp client can read resource scoped to principal` (): Unit = runBlocking {
117+ val server = embeddedServer(ServerCIO , host = HOST , port = 0 ) {
118+ configurePlugins()
119+ install(Authentication ) {
120+ basic(AUTH_REALM ) {
121+ validate { credentials ->
122+ if (credentials.name == validUser && credentials.password == validPassword) {
123+ UserIdPrincipal (credentials.name)
124+ } else {
125+ null
131126 }
132127 }
133128 }
134- routing {
135- authenticate( AUTH_REALM ) {
136- registerMcpServer {
137- // `this` is the ApplicationCall at connection time.
138- // The lambda passed to createMcpServer captures this call;
139- // principal<T>() is safe to call from resource handlers because
140- // the call's authentication context remains valid for the session lifetime.
141- createMcpServer { principal< UserIdPrincipal >()?.name }
142- }
129+ }
130+ routing {
131+ authenticate( AUTH_REALM ) {
132+ registerMcpServer {
133+ // `this` is the ApplicationCall at connection time.
134+ // The lambda passed to createMcpServer captures this call;
135+ // principal<T>() is safe to call from resource handlers because
136+ // the call's authentication context remains valid for the session lifetime.
137+ createMcpServer { principal< UserIdPrincipal >()?.name }
143138 }
144139 }
145- }.startSuspend(wait = false )
140+ }
141+ }.startSuspend(wait = false )
146142
147- val baseUrl = " http://$HOST :${server.actualPort()} "
148- var mcpClient: Client ? = null
149- try {
150- mcpClient = Client (Implementation (name = " test-client" , version = " 1.0.0" ))
151- mcpClient.connect(createClientTransport(baseUrl, validUser, validPassword))
143+ val baseUrl = " http://$HOST :${server.actualPort()} "
144+ var mcpClient: Client ? = null
145+ try {
146+ mcpClient = Client (Implementation (name = " test-client" , version = " 1.0.0" ))
147+ mcpClient.connect(createClientTransport(baseUrl, validUser, validPassword))
152148
153- val result = mcpClient.readResource(
154- ReadResourceRequest (ReadResourceRequestParams (uri = WHOAMI_URI )),
155- )
149+ val result = mcpClient.readResource(
150+ ReadResourceRequest (ReadResourceRequestParams (uri = WHOAMI_URI )),
151+ )
156152
157- result.contents shouldBe listOf (
158- TextResourceContents (
159- text = validUser,
160- uri = WHOAMI_URI ,
161- mimeType = " text/plain" ,
162- ),
163- )
164- } finally {
165- mcpClient?.close()
166- server.stopSuspend(500 , 1000 )
167- }
153+ result.contents shouldBe listOf (
154+ TextResourceContents (
155+ text = validUser,
156+ uri = WHOAMI_URI ,
157+ mimeType = " text/plain" ,
158+ ),
159+ )
160+ } finally {
161+ mcpClient?.close()
162+ server.stopSuspend(500 , 1000 )
168163 }
169164 }
170165
0 commit comments