{"version":3,"file":"index.cjs","names":["OAuthError","OAuthErrorCode","OAuthError","OAuthErrorCode"],"sources":["../src/middleware/hostHeaderValidation.ts","../src/middleware/originValidation.ts","../src/express.ts","../src/auth/bearerAuth.ts","../src/auth/metadataRouter.ts"],"sourcesContent":["import { localhostAllowedHostnames, validateHostHeader } from '@modelcontextprotocol/server';\nimport type { NextFunction, Request, RequestHandler, Response } from 'express';\n\n/**\n * Express middleware for DNS rebinding protection.\n * Validates `Host` header hostname (port-agnostic) against an allowed list.\n *\n * This is particularly important for servers without authorization or HTTPS,\n * such as localhost servers or development servers. DNS rebinding attacks can\n * bypass same-origin policy by manipulating DNS to point a domain to a\n * localhost address, allowing malicious websites to access your local server.\n *\n * @param allowedHostnames - List of allowed hostnames (without ports).\n *   For IPv6, provide the address with brackets (e.g., `[::1]`).\n * @returns Express middleware function\n *\n * @example\n * ```ts source=\"./hostHeaderValidation.examples.ts#hostHeaderValidation_basicUsage\"\n * const middleware = hostHeaderValidation(['localhost', '127.0.0.1', '[::1]']);\n * app.use(middleware);\n * ```\n */\nexport function hostHeaderValidation(allowedHostnames: string[]): RequestHandler {\n    return (req: Request, res: Response, next: NextFunction) => {\n        const result = validateHostHeader(req.headers.host, allowedHostnames);\n        if (!result.ok) {\n            res.status(403).json({\n                jsonrpc: '2.0',\n                error: {\n                    code: -32_000,\n                    message: result.message\n                },\n                id: null\n            });\n            return;\n        }\n        next();\n    };\n}\n\n/**\n * Convenience middleware for localhost DNS rebinding protection.\n * Allows only `localhost`, `127.0.0.1`, and `[::1]` (IPv6 localhost) hostnames.\n *\n * @example\n * ```ts source=\"./hostHeaderValidation.examples.ts#localhostHostValidation_basicUsage\"\n * app.use(localhostHostValidation());\n * ```\n */\nexport function localhostHostValidation(): RequestHandler {\n    return hostHeaderValidation(localhostAllowedHostnames());\n}\n","import { localhostAllowedOrigins, validateOriginHeader } from '@modelcontextprotocol/server';\nimport type { NextFunction, Request, RequestHandler, Response } from 'express';\n\n/**\n * Express middleware for Origin header validation.\n * Validates the `Origin` header hostname (port-agnostic) against an allowed list.\n *\n * Browsers attach an `Origin` header to cross-origin requests; validating it —\n * alongside Host header validation — protects localhost and development servers\n * against DNS rebinding and cross-site request forgery. Requests without an\n * `Origin` header pass (non-browser MCP clients do not send one); a present\n * value that is not allowed, or that cannot be parsed, is rejected with `403`.\n *\n * @param allowedOriginHostnames - List of allowed origin hostnames (without scheme or port).\n *   For IPv6, provide the address with brackets (e.g., `[::1]`).\n * @returns Express middleware function\n *\n * @example\n * ```ts\n * app.use(originValidation(['localhost', '127.0.0.1', '[::1]']));\n * ```\n */\nexport function originValidation(allowedOriginHostnames: string[]): RequestHandler {\n    return (req: Request, res: Response, next: NextFunction) => {\n        const result = validateOriginHeader(req.headers.origin, allowedOriginHostnames);\n        if (!result.ok) {\n            res.status(403).json({\n                jsonrpc: '2.0',\n                error: {\n                    code: -32_000,\n                    message: result.message\n                },\n                id: null\n            });\n            return;\n        }\n        next();\n    };\n}\n\n/**\n * Convenience middleware for localhost Origin validation.\n * Allows only origins whose hostname is `localhost`, `127.0.0.1`, or `[::1]` (IPv6 localhost).\n *\n * @example\n * ```ts\n * app.use(localhostOriginValidation());\n * ```\n */\nexport function localhostOriginValidation(): RequestHandler {\n    return originValidation(localhostAllowedOrigins());\n}\n","import type { Express } from 'express';\nimport express from 'express';\n\nimport { hostHeaderValidation, localhostHostValidation } from './middleware/hostHeaderValidation';\nimport { localhostOriginValidation, originValidation } from './middleware/originValidation';\n\n/**\n * Options for creating an MCP Express application.\n */\nexport interface CreateMcpExpressAppOptions {\n    /**\n     * The hostname to bind to. Defaults to `'127.0.0.1'`.\n     * When set to `'127.0.0.1'`, `'localhost'`, or `'::1'`, DNS rebinding protection is automatically enabled.\n     */\n    host?: string;\n\n    /**\n     * List of allowed hostnames for DNS rebinding protection.\n     * If provided, host header validation will be applied using this list.\n     * For IPv6, provide addresses with brackets (e.g., `'[::1]'`).\n     *\n     * This is useful when binding to `'0.0.0.0'` or `'::'` but still wanting\n     * to restrict which hostnames are allowed.\n     */\n    allowedHosts?: string[];\n\n    /**\n     * List of allowed origin hostnames for Origin header validation.\n     * If provided, Origin validation will be applied using this list (port-agnostic,\n     * hostnames only — the same convention as `allowedHosts`).\n     *\n     * When omitted, Origin validation is automatically enabled for localhost-class\n     * binds (the same condition as host validation): requests without an `Origin`\n     * header pass, while a present `Origin` whose hostname is not localhost-class\n     * is rejected with `403`.\n     */\n    allowedOrigins?: string[];\n\n    /**\n     * Controls the maximum request body size for the JSON body parser.\n     * Passed directly to Express's `express.json({ limit })` option.\n     * Defaults to Express's built-in default of `'100kb'`.\n     *\n     * @example '1mb', '500kb', '10mb'\n     */\n    jsonLimit?: string;\n}\n\n/**\n * Creates an Express application pre-configured for MCP servers.\n *\n * When the host is `'127.0.0.1'`, `'localhost'`, or `'::1'` (the default is `'127.0.0.1'`),\n * DNS rebinding protection middleware is automatically applied to protect against\n * DNS rebinding attacks on localhost servers.\n *\n * @param options - Configuration options\n * @returns A configured Express application\n *\n * @example Basic usage - defaults to 127.0.0.1 with DNS rebinding protection\n * ```ts source=\"./express.examples.ts#createMcpExpressApp_default\"\n * const app = createMcpExpressApp();\n * ```\n *\n * @example Custom host - DNS rebinding protection only applied for localhost hosts\n * ```ts source=\"./express.examples.ts#createMcpExpressApp_customHost\"\n * const appOpen = createMcpExpressApp({ host: '0.0.0.0' }); // No automatic DNS rebinding protection\n * const appLocal = createMcpExpressApp({ host: 'localhost' }); // DNS rebinding protection enabled\n * ```\n *\n * @example Custom allowed hosts for non-localhost binding\n * ```ts source=\"./express.examples.ts#createMcpExpressApp_allowedHosts\"\n * const app = createMcpExpressApp({ host: '0.0.0.0', allowedHosts: ['myapp.local', 'localhost'] });\n * ```\n */\nexport function createMcpExpressApp(options: CreateMcpExpressAppOptions = {}): Express {\n    const { host = '127.0.0.1', allowedHosts, allowedOrigins, jsonLimit } = options;\n\n    const app = express();\n    app.use(express.json(jsonLimit ? { limit: jsonLimit } : undefined));\n\n    // If allowedHosts is explicitly provided, use that for validation\n    if (allowedHosts) {\n        app.use(hostHeaderValidation(allowedHosts));\n    } else {\n        // Apply DNS rebinding protection automatically for localhost hosts\n        const localhostHosts = ['127.0.0.1', 'localhost', '::1'];\n        if (localhostHosts.includes(host)) {\n            app.use(localhostHostValidation());\n        } else if (host === '0.0.0.0' || host === '::') {\n            // Warn when binding to all interfaces without DNS rebinding protection\n            // eslint-disable-next-line no-console\n            console.warn(\n                `Warning: Server is binding to ${host} without DNS rebinding protection. ` +\n                    'Consider using the allowedHosts option to restrict allowed hosts, ' +\n                    'or use authentication to protect your server.'\n            );\n        }\n    }\n\n    // Origin validation follows the same arming ladder as host validation:\n    // an explicit allowlist wins; otherwise localhost-class binds are protected\n    // by default. Requests without an Origin header always pass.\n    if (allowedOrigins) {\n        app.use(originValidation(allowedOrigins));\n    } else if (['127.0.0.1', 'localhost', '::1'].includes(host)) {\n        app.use(localhostOriginValidation());\n    }\n\n    return app;\n}\n","import type { BearerAuthOptions } from '@modelcontextprotocol/server';\nimport { bearerAuthChallengeResponse, OAuthError, OAuthErrorCode, verifyBearerToken } from '@modelcontextprotocol/server';\nimport type { RequestHandler } from 'express';\n\n/**\n * Options for {@link requireBearerAuth}.\n */\nexport type BearerAuthMiddlewareOptions = BearerAuthOptions;\n\n/**\n * Express middleware that requires a valid Bearer token in the `Authorization`\n * header.\n *\n * The Express adapter over the runtime-neutral core in\n * `@modelcontextprotocol/server` (`verifyBearerToken` /\n * `bearerAuthChallengeResponse` — or `requireBearerAuth` from that package for\n * web-standard `fetch(request)` hosts). The token is validated via the\n * supplied `OAuthTokenVerifier` and the resulting `AuthInfo` is attached to\n * `req.auth`. The MCP Streamable HTTP transport reads `req.auth` and surfaces\n * it to handlers as `ctx.http.authInfo`.\n *\n * On failure the middleware sends a JSON OAuth error body and a\n * `WWW-Authenticate: Bearer …` challenge that includes the configured\n * `resource_metadata` URL so clients can discover the Authorization Server.\n */\nexport function requireBearerAuth(options: BearerAuthMiddlewareOptions): RequestHandler {\n    // Destructure at creation so a plain-JS caller passing undefined or\n    // malformed options crashes at startup, not on the first request.\n    const { verifier, requiredScopes = [], resourceMetadataUrl } = options;\n    const resolved = { verifier, requiredScopes, resourceMetadataUrl };\n    return async (req, res, next) => {\n        try {\n            req.auth = await verifyBearerToken(req.headers.authorization, resolved);\n            next();\n        } catch (error) {\n            // The core Response supplies status and challenge; the body is\n            // derived directly rather than parsed back out of the Response.\n            const response = bearerAuthChallengeResponse(error, resolved);\n            const challenge = response.headers.get('WWW-Authenticate');\n            if (challenge !== null) {\n                res.set('WWW-Authenticate', challenge);\n            }\n            const body = error instanceof OAuthError ? error : new OAuthError(OAuthErrorCode.ServerError, 'Internal Server Error');\n            res.status(response.status).json(body.toResponseObject());\n        }\n    };\n}\n","import type {\n    AuthMetadataOptions as NeutralAuthMetadataOptions,\n    OAuthMetadata,\n    OAuthProtectedResourceMetadata\n} from '@modelcontextprotocol/server';\nimport { buildOAuthProtectedResourceMetadata, OAuthError, OAuthErrorCode } from '@modelcontextprotocol/server';\nimport cors from 'cors';\nimport type { RequestHandler, Router } from 'express';\nimport express from 'express';\n\n// Dev-only escape hatch: allow http:// issuer URLs (e.g., for local testing).\nconst allowInsecureIssuerUrl =\n    process.env.MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL === 'true' || process.env.MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL === '1';\nif (allowInsecureIssuerUrl) {\n    // eslint-disable-next-line no-console\n    console.warn('MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL is enabled - HTTP issuer URLs are allowed. Do not use in production.');\n}\n\n/**\n * Express middleware that rejects HTTP methods not in the supplied allow-list\n * with a 405 Method Not Allowed and an OAuth-style error body. Used by\n * {@link metadataHandler} to restrict metadata endpoints to GET/OPTIONS.\n */\nexport function allowedMethods(allowed: string[]): RequestHandler {\n    return (req, res, next) => {\n        if (allowed.includes(req.method)) {\n            next();\n            return;\n        }\n        const error = new OAuthError(OAuthErrorCode.MethodNotAllowed, `The method ${req.method} is not allowed for this endpoint`);\n        res.status(405).set('Allow', allowed.join(', ')).json(error.toResponseObject());\n    };\n}\n\n/**\n * Builds a small Express router that serves the given OAuth metadata document\n * at `/` as JSON, with permissive CORS and a GET/OPTIONS method allow-list.\n *\n * Used by {@link mcpAuthMetadataRouter} for both the Authorization Server and\n * Protected Resource metadata endpoints.\n */\nexport function metadataHandler(metadata: OAuthMetadata | OAuthProtectedResourceMetadata): RequestHandler {\n    const router = express.Router();\n    // Metadata documents must be fetchable from web-based MCP clients on any origin.\n    router.use(cors());\n    router.use(allowedMethods(['GET', 'OPTIONS']));\n    router.get('/', (_req, res) => {\n        res.status(200).json(metadata);\n    });\n    return router;\n}\n\n/**\n * Options for {@link mcpAuthMetadataRouter}: the runtime-neutral\n * `AuthMetadataOptions` from `@modelcontextprotocol/server`. The\n * insecure-issuer escape hatch can also be enabled here by the\n * `MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER_URL` environment variable.\n */\nexport type { AuthMetadataOptions } from '@modelcontextprotocol/server';\n\n/**\n * Builds an Express router that serves the two OAuth discovery documents an\n * MCP server acting purely as a Resource Server needs to expose:\n *\n *  - `/.well-known/oauth-protected-resource[/<path>]` — RFC 9728 Protected\n *    Resource Metadata, derived from the supplied options.\n *  - `/.well-known/oauth-authorization-server` — RFC 8414 Authorization\n *    Server Metadata, passed through verbatim from the supplied `oauthMetadata`.\n *\n * Mount this router at the application root:\n *\n * ```ts\n * app.use(mcpAuthMetadataRouter({ oauthMetadata, resourceServerUrl }));\n * ```\n *\n * Pair with `requireBearerAuth` on your `/mcp` route and pass\n * `getOAuthProtectedResourceMetadataUrl` as its `resourceMetadataUrl`\n * so unauthenticated clients can discover the AS from the 401 challenge.\n */\nexport function mcpAuthMetadataRouter(options: NeutralAuthMetadataOptions): Router {\n    if (options.dangerouslyAllowInsecureIssuerUrl && !allowInsecureIssuerUrl) {\n        // The env-var path warns at module load; enabling via the option is\n        // equally loud so an insecure issuer can never be allowed silently.\n        // eslint-disable-next-line no-console\n        console.warn('dangerouslyAllowInsecureIssuerUrl is enabled - HTTP issuer URLs are allowed. Do not use in production.');\n    }\n    const protectedResourceMetadata = buildOAuthProtectedResourceMetadata({\n        ...options,\n        // The env var and the option are both honored; either enables it.\n        dangerouslyAllowInsecureIssuerUrl: allowInsecureIssuerUrl || options.dangerouslyAllowInsecureIssuerUrl\n    });\n\n    const router = express.Router();\n\n    // Serve PRM at the path-aware URL per RFC 9728 §3.1.\n    const rsPath = new URL(options.resourceServerUrl.href).pathname;\n    router.use(`/.well-known/oauth-protected-resource${rsPath === '/' ? '' : rsPath}`, metadataHandler(protectedResourceMetadata));\n\n    // Mirror the AS metadata at this origin for clients that look here first.\n    router.use('/.well-known/oauth-authorization-server', metadataHandler(options.oauthMetadata));\n\n    return router;\n}\n\n// Re-exported from the runtime-neutral home in @modelcontextprotocol/server.\nexport { getOAuthProtectedResourceMetadataUrl } from '@modelcontextprotocol/server';\n"],"mappings":"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"}