@@ -3,9 +3,14 @@ import { Readable } from 'node:stream';
33import { URL } from 'node:url' ;
44
55import type { AuthMetadataOptions , AuthRouterOptions , WebHandlerContext } from '@modelcontextprotocol/server' ;
6- import { mcpAuthMetadataRouter as createWebAuthMetadataRouter , mcpAuthRouter as createWebAuthRouter } from '@modelcontextprotocol/server' ;
6+ import {
7+ mcpAuthMetadataRouter as createWebAuthMetadataRouter ,
8+ mcpAuthRouter as createWebAuthRouter ,
9+ TooManyRequestsError
10+ } from '@modelcontextprotocol/server' ;
711import type { RequestHandler , Response as ExpressResponse } from 'express' ;
812import express from 'express' ;
13+ import { rateLimit } from 'express-rate-limit' ;
914
1015type ExpressRequestLike = IncomingMessage & {
1116 method : string ;
@@ -112,11 +117,23 @@ async function writeWebResponse(res: ExpressResponse, webResponse: Response): Pr
112117
113118function toHandlerContext ( req : ExpressRequestLike ) : WebHandlerContext {
114119 return {
115- parsedBody : req . body ,
116- clientAddress : req . ip
120+ parsedBody : req . body
117121 } ;
118122}
119123
124+ export type ExpressAuthRateLimitOptions =
125+ | false
126+ | {
127+ /**
128+ * Window size in ms (default: 60s)
129+ */
130+ windowMs ?: number ;
131+ /**
132+ * Max requests per window per client (default: 60)
133+ */
134+ max ?: number ;
135+ } ;
136+
120137/**
121138 * Express router adapter for the Web-standard `mcpAuthRouter` from `@modelcontextprotocol/server`.
122139 *
@@ -126,12 +143,34 @@ function toHandlerContext(req: ExpressRequestLike): WebHandlerContext {
126143 * app.use(mcpAuthRouter(...))
127144 * ```
128145 */
129- export function mcpAuthRouter ( options : AuthRouterOptions ) : RequestHandler {
146+ export function mcpAuthRouter ( options : AuthRouterOptions & { rateLimit ?: ExpressAuthRateLimitOptions } ) : RequestHandler {
130147 const web = createWebAuthRouter ( options ) ;
131148 const router = express . Router ( ) ;
132149
150+ const rateLimitOptions = options . rateLimit ;
151+ const limiter =
152+ rateLimitOptions === false
153+ ? undefined
154+ : rateLimit ( {
155+ windowMs : rateLimitOptions ?. windowMs ?? 60_000 ,
156+ max : rateLimitOptions ?. max ?? 60 ,
157+ standardHeaders : true ,
158+ legacyHeaders : false ,
159+ handler : ( _req , res ) => {
160+ const err = new TooManyRequestsError ( 'Too many requests' ) ;
161+ res . status ( 429 ) . json ( err . toResponseObject ( ) ) ;
162+ }
163+ } ) ;
164+
165+ const isRateLimitedPath = ( path : string ) : boolean =>
166+ path === '/authorize' || path === '/token' || path === '/register' || path === '/revoke' ;
167+
133168 for ( const route of web . routes ) {
134- router . all ( route . path , async ( req , res , next ) => {
169+ const handlers : RequestHandler [ ] = [ ] ;
170+ if ( limiter && isRateLimitedPath ( route . path ) ) {
171+ handlers . push ( limiter ) ;
172+ }
173+ handlers . push ( async ( req , res , next ) => {
135174 try {
136175 const parsedBodyProvided = ( req as ExpressRequestLike ) . body !== undefined ;
137176 const webReq = await expressToWebRequest ( req as ExpressRequestLike , parsedBodyProvided ) ;
@@ -141,6 +180,7 @@ export function mcpAuthRouter(options: AuthRouterOptions): RequestHandler {
141180 next ( err ) ;
142181 }
143182 } ) ;
183+ router . all ( route . path , ...handlers ) ;
144184 }
145185
146186 return router ;
0 commit comments