11import { MCP } from "@/mcp"
22import { ConfigMCP } from "@/config/mcp"
33import { Effect , Layer , Schema } from "effect"
4- import { HttpApi , HttpApiBuilder , HttpApiEndpoint , HttpApiGroup , OpenApi } from "effect/unstable/httpapi"
4+ import { HttpApi , HttpApiBuilder , HttpApiEndpoint , HttpApiError , HttpApiGroup , OpenApi } from "effect/unstable/httpapi"
55import { Authorization } from "./auth"
66
77const AddPayload = Schema . Struct ( {
@@ -10,9 +10,22 @@ const AddPayload = Schema.Struct({
1010} ) . annotate ( { identifier : "McpAddInput" } )
1111
1212const StatusMap = Schema . Record ( Schema . String , MCP . Status )
13+ const AuthStartResponse = Schema . Struct ( {
14+ authorizationUrl : Schema . String ,
15+ oauthState : Schema . String ,
16+ } ) . annotate ( { identifier : "McpAuthStartResponse" } )
17+ const AuthCallbackPayload = Schema . Struct ( {
18+ code : Schema . String ,
19+ } ) . annotate ( { identifier : "McpAuthCallbackInput" } )
20+ const AuthRemoveResponse = Schema . Struct ( {
21+ success : Schema . Literal ( true ) ,
22+ } ) . annotate ( { identifier : "McpAuthRemoveResponse" } )
1323
1424export const McpPaths = {
1525 status : "/mcp" ,
26+ auth : "/mcp/:name/auth" ,
27+ authCallback : "/mcp/:name/auth/callback" ,
28+ authAuthenticate : "/mcp/:name/auth/authenticate" ,
1629 connect : "/mcp/:name/connect" ,
1730 disconnect : "/mcp/:name/disconnect" ,
1831} as const
@@ -40,6 +53,47 @@ export const McpApi = HttpApi.make("mcp")
4053 description : "Dynamically add a new Model Context Protocol (MCP) server to the system." ,
4154 } ) ,
4255 ) ,
56+ HttpApiEndpoint . post ( "authStart" , McpPaths . auth , {
57+ params : { name : Schema . String } ,
58+ success : AuthStartResponse ,
59+ } ) . annotateMerge (
60+ OpenApi . annotations ( {
61+ identifier : "mcp.auth.start" ,
62+ summary : "Start MCP OAuth" ,
63+ description : "Start OAuth authentication flow for a Model Context Protocol (MCP) server." ,
64+ } ) ,
65+ ) ,
66+ HttpApiEndpoint . post ( "authCallback" , McpPaths . authCallback , {
67+ params : { name : Schema . String } ,
68+ payload : AuthCallbackPayload ,
69+ success : MCP . Status ,
70+ } ) . annotateMerge (
71+ OpenApi . annotations ( {
72+ identifier : "mcp.auth.callback" ,
73+ summary : "Complete MCP OAuth" ,
74+ description : "Complete OAuth authentication for a Model Context Protocol (MCP) server using the authorization code." ,
75+ } ) ,
76+ ) ,
77+ HttpApiEndpoint . post ( "authAuthenticate" , McpPaths . authAuthenticate , {
78+ params : { name : Schema . String } ,
79+ success : MCP . Status ,
80+ } ) . annotateMerge (
81+ OpenApi . annotations ( {
82+ identifier : "mcp.auth.authenticate" ,
83+ summary : "Authenticate MCP OAuth" ,
84+ description : "Start OAuth flow and wait for callback (opens browser)." ,
85+ } ) ,
86+ ) ,
87+ HttpApiEndpoint . delete ( "authRemove" , McpPaths . auth , {
88+ params : { name : Schema . String } ,
89+ success : AuthRemoveResponse ,
90+ } ) . annotateMerge (
91+ OpenApi . annotations ( {
92+ identifier : "mcp.auth.remove" ,
93+ summary : "Remove MCP OAuth" ,
94+ description : "Remove OAuth credentials for an MCP server." ,
95+ } ) ,
96+ ) ,
4397 HttpApiEndpoint . post ( "connect" , McpPaths . connect , {
4498 params : { name : Schema . String } ,
4599 success : Schema . Boolean ,
@@ -89,6 +143,28 @@ export const mcpHandlers = Layer.unwrap(
89143 return Schema . decodeUnknownSync ( StatusMap ) ( "status" in result ? { [ payload . name ] : result } : result )
90144 } )
91145
146+ const authStart = Effect . fn ( "McpHttpApi.authStart" ) ( function * ( ctx : { params : { name : string } } ) {
147+ if ( ! ( yield * mcp . supportsOAuth ( ctx . params . name ) ) ) return yield * new HttpApiError . BadRequest ( { } )
148+ return yield * mcp . startAuth ( ctx . params . name )
149+ } )
150+
151+ const authCallback = Effect . fn ( "McpHttpApi.authCallback" ) ( function * ( ctx : {
152+ params : { name : string }
153+ payload : typeof AuthCallbackPayload . Type
154+ } ) {
155+ return yield * mcp . finishAuth ( ctx . params . name , ctx . payload . code )
156+ } )
157+
158+ const authAuthenticate = Effect . fn ( "McpHttpApi.authAuthenticate" ) ( function * ( ctx : { params : { name : string } } ) {
159+ if ( ! ( yield * mcp . supportsOAuth ( ctx . params . name ) ) ) return yield * new HttpApiError . BadRequest ( { } )
160+ return yield * mcp . authenticate ( ctx . params . name )
161+ } )
162+
163+ const authRemove = Effect . fn ( "McpHttpApi.authRemove" ) ( function * ( ctx : { params : { name : string } } ) {
164+ yield * mcp . removeAuth ( ctx . params . name )
165+ return { success : true as const }
166+ } )
167+
92168 const connect = Effect . fn ( "McpHttpApi.connect" ) ( function * ( ctx : { params : { name : string } } ) {
93169 yield * mcp . connect ( ctx . params . name )
94170 return true
@@ -100,7 +176,15 @@ export const mcpHandlers = Layer.unwrap(
100176 } )
101177
102178 return HttpApiBuilder . group ( McpApi , "mcp" , ( handlers ) =>
103- handlers . handle ( "status" , status ) . handle ( "add" , add ) . handle ( "connect" , connect ) . handle ( "disconnect" , disconnect ) ,
179+ handlers
180+ . handle ( "status" , status )
181+ . handle ( "add" , add )
182+ . handle ( "authStart" , authStart )
183+ . handle ( "authCallback" , authCallback )
184+ . handle ( "authAuthenticate" , authAuthenticate )
185+ . handle ( "authRemove" , authRemove )
186+ . handle ( "connect" , connect )
187+ . handle ( "disconnect" , disconnect ) ,
104188 )
105189 } ) ,
106190) . pipe ( Layer . provide ( MCP . defaultLayer ) )
0 commit comments