• Documentation
  • Pricing
© 2026 Serverless, Inc. All rights reserved.

Framework

  • Overview
  • Documentation
  • Plugins360
  • Pricing

Learn

  • Blog
  • GuidesUpdated
  • Examples240
  • Courses

Resources

  • Support
  • Security
  • Trust Center
  • Status

Community

  • Slack
  • GitHub47k
  • Forum
  • Meetups

Company

  • About
  • Careers
  • Contact
  • Partners

Legal

  • Terms of Service
  • Privacy Policy
  • Trademark
  • DMCA

Deploy MCP Servers on AWS Lambda with Serverless Framework v4

Serverless TeamServerless Team
August 11, 2026
Deploy MCP Servers on AWS Lambda with Serverless Framework v4

Serverless Framework v4.41.0 can now deploy an MCP server to AWS Lambda with four lines of YAML.

mcp:
  servers:
    crm:
      server: index.ts

That gets you a real HTTPS endpoint, response streaming, and AWS Lambda's pay-per-request pricing. Here is why this just became possible, and how it works.

  • Serverless MCP Guide
  • Serverless MCP Examples
  • Serverless MCP Agent Skill

MCP went stateless

On July 28, the Model Context Protocol shipped a revision that removed the two things tying MCP servers to always-on compute: the initialization handshake and protocol-level sessions (SEP-2575 and SEP-2567). Every request now carries its own protocol version, client identity, and capabilities, so any request can land on any instance. No shared storage. No sticky routing. MCP is now plain request/response over ordinary HTTP.

Remote MCP servers are already everywhere, but they have all run on always-on servers, because a session ID pinned each client to whichever replica answered its handshake. That constraint is gone.


Lambda is the natural home

MCP traffic is bursty. An agent calls a tool, then nothing happens for a while. A container billed by the hour is the wrong shape for that. Lambda bills per invocation and costs nothing when idle.

Just as important, your tools are the code that touches your actual systems, and they belong next to them: same AWS account, same VPC, same IAM roles, same DynamoDB tables and secrets you already use. A tool that queries your orders table is an IAM policy, not an outbound integration through someone else's platform.


Your code stays plain

The file you point at is an ordinary MCP server written with the official MCP TypeScript SDK. No Lambda concepts, no Framework APIs, no handler signature to learn.

The Framework owns everything around it:

  • An HTTPS endpoint on the same REST API as your http functions
  • Response streaming wired end to end, with timeouts kept in sync
  • Packaging that bundles your code with a prebuilt entry bridging Lambda's streaming runtime to the SDK
  • When you opt into auth: your access control wired to the MCP route while the OAuth discovery route stays open - the part that is easy to get wrong by hand, because a client has to read that document before it has anything to present

The whole protocol works

Tools, resources including URI templates, and prompts all work, for every client. Responses stream, so a long tool sends progress notifications as it goes instead of going quiet and timing out.

The hardest part works too, the one where the server asks the client for something mid-call: elicitation, which sounds impossible without a session.

A tool stops halfway and asks the user a question - which of these three orders did you mean? The answer arrives in a new HTTP request that can land on any instance, and nothing on the server remembers the first call. So the server seals everything it needs to resume into a blob, hands it over with the question, and the client returns it alongside the answer. Whichever instance picks up the retry unseals it and continues where the first one stopped. The state travels through the client instead of living on the server.

That blob passes through the caller's hands, so it has to be signed - otherwise a client could edit its own resume state and the server would trust it. state: true provisions that signing key in your stack: a single generated secret, no database, because the state itself never lands on the server. One line, and your tools can hold a conversation.


The whole configuration surface

server - Points at your MCP server code. That one path is all it takes to get a deployed server on an HTTPS endpoint. The only required key.

authorizer - Locks the server down before it runs. Access control enforced by API Gateway in front of the function, in the same shapes an http event accepts: point it at a Cognito user pool and API Gateway validates tokens itself - no code to write, and a rejected request never invokes anything you pay for. Or bring your own Lambda authorizer for any identity provider, or require IAM-signed callers.

oauthDiscovery - Tells clients where to log in. One line - your issuer URL - becomes the discovery document MCP clients read to find your authorization server (RFC 9728), served as a static API Gateway response with no Lambda behind it.

The two are independent, and any combination is coherent: an authorizer with no discovery for machine-to-machine callers that already hold tokens, discovery with no authorizer when your module verifies tokens itself, both for interactive clients, neither for a public server. And when you want the specification's own semantics - scope-aware 403s, the caller's verified identity inside your tools - the MCP SDK's requireBearerAuth runs inside your module, and the layers compose.

state - Lets tools pause and ask the user for input. Provisions the signing key that makes resume state tamper-proof, created and destroyed with your stack. Or point it at your own SSM or Secrets Manager ARN.

timeout - Sets how long a tool may run, up to 15 minutes. One value moves the function timeout and the streaming integration timeout together, so a long tool cannot be cut off by a limit you forgot about.

memorySize - Gives heavy tools more room, up to 10 GB. CPU scales with it.

environment - Connects the server to the resources it uses. Ordinary environment variables, CloudFormation intrinsics included, so !Ref OrdersTable hands your tools a real table name.

Here is the Cognito shape end to end - the user pool lives in the same service's resources, and one deploy wires all of it, discovery document included:

mcp:
  servers:
    crm:
      server: index.ts
      authorizer:
        name: crmPool
        type: COGNITO_USER_POOLS
        arn: !GetAtt UserPool.Arn
        scopes:
          - mcp/invoke
      oauthDiscovery:
        issuer: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}
      state: true

Everything else comes from settings you already use. provider.vpc, provider.architecture, provider.layers, and provider.iam apply to an MCP server exactly as they do to your functions, down to perFunction roles. provider.domain puts a custom domain in front. There is no parallel configuration language to learn, because there is no parallel resource.


It's just a function

Day to day, an MCP server is a function in your service. serverless logs -f crm and serverless invoke -f crm work by bare server name. Metrics, versions, and rollback work. Two servers in one service share one API, one stage, and one custom domain, and serverless deploy prints each endpoint. Nothing is a special case.

We verified this against real clients, not a demo harness: 664 unit tests, a live integration suite that deploys one server per way of enforcing access - including proof from CloudWatch that a rejected request never invokes the function, and a full Cognito auth chain with real access tokens - and end-to-end runs with the official MCP client, MCP Inspector, and Claude Code through a real OAuth browser login and an elicitation round trip.


Two things before you ship

Interactive login needs a custom domain mapped at the root. On the default execute-api URL, the discovery document sits under the stage prefix, where clients never look - so browser login cannot start there. Machine-to-machine callers that already hold tokens are unaffected, and the Framework warns you at packaging time whenever the document would land on the stage URL. One more provider note: Cognito has no dynamic client registration, so it fits pre-registered clients and machine-to-machine callers; the full discover-then-register flow needs a provider that supports registration, such as Auth0, Okta, or Entra ID.

Elicitation needs a client on the 2026-07-28 revision. Official-SDK clients reach it by opting in.

Docs: serverless.com/framework/docs/providers/aws/guide/mcp


Where to go next

  • The MCP guide - the full reference: every configuration key, the three ways to enforce access and how they compose, elicitation state, permissions, and troubleshooting.
  • The examples hub - deployable examples for each tier, from a public server to the full OAuth walkthrough with a real client, plus a custom/ family for when you want to own the hosting glue yourself.
  • The serverless-mcp Agent Skill - if you build with an AI coding agent, this skill teaches it the whole surface: configuration shapes, server code patterns, testing, and troubleshooting. It ships inside the CLI - run serverless agent skills install in your service directory and the Framework installs it for your agent and keeps it current from then on.

The Serverless Framework is free for individuals and organizations under $2M in annual revenue. For larger teams, learn about our Subscription plans or schedule a meeting with us.

Contents

  • MCP went stateless
  • Lambda is the natural home
  • Your code stays plain
  • The whole protocol works
  • The whole configuration surface
  • It's just a function
  • Two things before you ship
  • Where to go next

Related

GuidesPluginsExamplesSlack CommunitySupport