Skip to main content
Glama
hteek

Serverless MCP

by hteek
lambdaFunctionURL.ts5.55 kB
import middy, { MiddyfiedHandler } from '@middy/core'; import httpErrorHandler from '@middy/http-error-handler'; import { getInternal } from '@middy/util'; import { LambdaFunctionURLEvent, LambdaFunctionURLHandler, LambdaFunctionURLResult, } from 'aws-lambda'; import createError from 'http-errors'; import UrlPattern from 'url-pattern'; import { createMiddlewareHandler, createTenantMiddleware, lambdaHandler, MiddlewareContext, } from './handler.js'; export type LambdaFunctionURLOpts = { methods?: ( | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' )[]; urlPattern: string; }; /** * Validates HTTP method and path of a Lambda Function URL Event. * Checks if the method is allowed and the path matches a defined pattern. * * @template T - The type of parameters extracted from the path, defaults to an object with an 'id' property * @param {LambdaFunctionURLEvent} event - The event containing HTTP method and path * @param {string} allowedPath - The allowed path pattern * @param {string[]} [allowedMethods] - Optional array of allowed HTTP methods * @returns {{ method: string; params: T; path: string }} Object containing the method, extracted parameters, and path * @throws {Error} 405 error if method is not allowed, 404 error if path is not found */ export const checkHttpMethodAndPath = <TParams = { id: string }>( event: LambdaFunctionURLEvent, allowedPath: string, allowedMethods?: string[] ): { method: string; params: TParams; path: string } => { const { http: { method, path }, } = event.requestContext; if (allowedMethods && !allowedMethods.some((value) => value === method)) { throw createError(405, `Method ${method} not allowed for ${path}`); } const pattern = new UrlPattern(allowedPath); if (pattern && pattern.match(path) === null) { throw createError(404, `Path ${path} not found`); } const params = pattern.match(path) as TParams; return { method, params, path, }; }; export const getBodyFromEvent = <T = unknown>(event: LambdaFunctionURLEvent) => JSON.parse(event.body ? event.body : '{}') as T; const getTenantIdFromEvent = (event: LambdaFunctionURLEvent) => { const { headers } = event; const tenantId = headers['api-key'] || headers['authorization'] || headers['bearer'] || 'public'; if (!tenantId) { throw createError(401, 'no tenant id given'); } return tenantId; }; /** * Middleware for extracting the tenant ID from Lambda Function URL event. * Adds the tenant ID to the internal state and to the request context. * * @template TResult - The return type of the Lambda handler, defaults to void * @returns {middy.MiddlewareObj} A Middy middleware object with a 'before' hook * @throws {Error} Throws a 401 error if no tenant ID is found in the event */ const tenant = <TResult = void>(): middy.MiddlewareObj< LambdaFunctionURLEvent, TResult > => createTenantMiddleware(getTenantIdFromEvent); /** * Middleware for Lambda Function URL. * This middleware checks the HTTP method and path of the event against allowed methods and a URL pattern. * It also extracts the method and parameters from the event and adds them to the request context. * @param opts - Options for the middleware, including allowed methods and URL pattern */ const lambdaFunctionURL = <TParams = { id: string }, TResult = void>( opts: LambdaFunctionURLOpts ): middy.MiddlewareObj<LambdaFunctionURLEvent, TResult> => ({ before: async (request) => { const { context, internal, event } = request; const { methods, urlPattern } = opts; const { method, params } = checkHttpMethodAndPath<TParams>( event, urlPattern, methods ); Object.assign(internal, { getBodyFromEvent, method, params, }); Object.assign(context, { lambdaFunctionURL: { ...(await getInternal(['getBodyFromEvent'], request)), ...(await getInternal(['method'], request)), ...(await getInternal(['params'], request)), }, }); }, }); /** * Creates a Lambda Function URL middleware handler. * This middleware handler includes logging, metrics, tracing, tenant extraction, STS role assumption, and the Lambda Function URL middleware. * @param opts - Options for the Lambda Function URL middleware, including allowed methods and URL pattern * @returns {Array} An array of middleware functions */ export const lambdaFunctionURLHandler = < TParams = { id: string }, TResult = void, >( opts: LambdaFunctionURLOpts ): Array<middy.MiddlewareObj<any>> => [ ...lambdaHandler(tenant), lambdaFunctionURL<TParams, TResult>(opts), httpErrorHandler(), ]; export type MiddlewareLambdaFunctionURLHandler<T = never> = LambdaFunctionURLHandler<T> extends ( event: infer E, context: infer _, callback: infer C ) => infer R ? (event: E, context: MiddlewareContext, callback: C) => R : never; export type MiddyfiedMiddlewareLambdaFunctionURLHandler<T = never> = MiddyfiedHandler< LambdaFunctionURLEvent, LambdaFunctionURLResult<T>, Error, MiddlewareContext >; /** * Creates a Middyfied Lambda Function URL handler. * @param handler The Lambda Function URL handler to be wrapped. */ export const createMiddlewareLambdaFunctionURLHandler: <T = never>( handler: MiddlewareLambdaFunctionURLHandler<T> ) => MiddyfiedMiddlewareLambdaFunctionURLHandler<T> = <T = never>( handler: MiddlewareLambdaFunctionURLHandler<T> ) => createMiddlewareHandler(handler);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hteek/serverless-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server