TypeScript MCP Server
Generate a TypeScript Model Context Protocol (MCP) server for providing context to Large Language Models (LLMs), and optionally deploy it to Amazon Bedrock AgentCore.
What is MCP?
Section titled “What is MCP?”The Model Context Protocol (MCP) is an open standard that allows AI assistants to interact with external tools and resources. It provides a consistent way for LLMs to:
- Execute tools (functions) that perform actions or retrieve information
- Access resources that provide context or data
Generate an MCP Server
Section titled “Generate an MCP Server”You can generate a TypeScript MCP server in two ways:
Run this generator@aws/nx-plugin:ts#mcp-server
pnpm nx g @aws/nx-plugin:ts#mcp-server yarn nx g @aws/nx-plugin:ts#mcp-server npx nx g @aws/nx-plugin:ts#mcp-server bunx nx g @aws/nx-plugin:ts#mcp-server- Install the Nx Console VSCode Plugin if you haven't already
- Open the Nx Console in VSCode
- Click
Generate (UI)in the "Common Nx Commands" section - Search for
@aws/nx-plugin - ts#mcp-server - Fill in the required parameters
- Click
Generate
Build your command6
Required
infra = agentcore | agentcore-ecr
Options
Section titled “Options”projectRequiredstringThe project to add an MCP server to
authenuminfra = agentcore | agentcore-ecrDefault:iamThe method used to authenticate with your MCP server. Only applicable when infra is set (ignored when infra is none).
iamcognitoiacenumDefault:inheritThe preferred IaC provider. By default this is inherited from your initial selection.
inheritcdkterraforminfraenumDefault:agentcoreThe type of infrastructure to host your MCP server. agentcore deploys your code as a zip to an AgentCore managed runtime for the fastest build and deploy cycle. agentcore-ecr builds and hosts a container image instead, for OS-level control or an established container pipeline. Select none for no hosting.
agentcoreagentcore-ecrnonenamestringThe name of your MCP server (default: mcp-server)
preferInstallDependenciesbooleanDefault:trueWhether to prefer installing dependencies after the generator runs. Set to false to defer installing when batching multiple generators (an install still runs if needed so subsequent generators can compute the Nx project graph); install once at the end.
Generator Output
Section titled “Generator Output”The generator will add the following files to your existing TypeScript project:
Directoryyour-project/
Directorysrc/
Directorymcp-server/ (or custom name if specified)
- index.ts Exports your server
- server.ts Main server definition
- stdio.ts Entry point for STDIO transport, useful for simple local MCP servers
- http.ts Entry point for Streamable HTTP transport, useful for hosting your MCP server
Directorytools/
- divide.ts Sample tool
Directoryresources/
- sample-guidance.ts Sample resource
- Dockerfile Container image definition (only when
infraisagentcore-ecr)
- project.json Updated with MCP server serve target
Infrastructure
Section titled “Infrastructure”The infra option selects how your code is packaged and hosted on Amazon Bedrock AgentCore Runtime:
agentcore(default) uses direct code deployment: your built code is packaged as a.zip, uploaded to S3, and run on an AgentCore managed language runtime. There is no container image to build, no ECR repository to manage, and no image to push, which makes for a substantially faster build and deploy cycle.agentcore-ecrbuilds anarm64container image from a vendedDockerfileand hosts it from the sharedcore/asset-ecrregistry, alongside every other container in the workspace. Choose this when you need control over the operating system image — for example to install native system libraries — or when you have an established container pipeline. This option additionally vends a Trivy image scan target (see Image Scanning below).nonegenerates no infrastructure at all, so the project can only be run locally.
Since this generator vends infrastructure as code based on your chosen iac, it will create a project in packages/common which includes the relevant CDK constructs or Terraform modules.
The common infrastructure as code project is structured as follows:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ Constructs for infrastructure specific to a project/generator
- …
Directorycore/ Generic constructs which are reused by constructs in
app- …
- index.ts Entry point exporting constructs from
app
- project.json Project build targets and configuration
Directorypackages/common/terraform
Directorysrc
Directoryapp/ Terraform modules for infrastructure specific to a project/generator
- …
Directorycore/ Generic modules which are reused by modules in
app- …
- project.json Project build targets and configuration
For deploying your MCP Server, the following files are generated:
Directorypackages/common/constructs/src
Directoryapp
Directorymcp-servers
Directory<mcp-server-name>
- <mcp-server-name>.ts CDK construct for deploying your MCP Server
Directorypackages/common/terraform/src
Directoryapp
Directorymcp-servers
Directory<mcp-server-name>
- <mcp-server-name>.tf Module for deploying your MCP Server
Directorycore
Directoryagent-core
- runtime.tf Generic module for deploying to Bedrock AgentCore Runtime
Directoryagent-core-code (when
infraisagentcore)- runtime.tf Packages your MCP Server’s code and delegates to
agent-core
- runtime.tf Packages your MCP Server’s code and delegates to
Directoryagent-core-container (when
infraisagentcore-ecr)- runtime.tf Builds and publishes your MCP Server’s image and delegates to
agent-core
- runtime.tf Builds and publishes your MCP Server’s image and delegates to
If you selected none for infra, no CDK constructs or Terraform modules are generated — the MCP server is configured for local STDIO / HTTP use only. The auth option is ignored in this mode since there is no hosted endpoint to authenticate.
Architecture
Section titled “Architecture”When deployed to Bedrock AgentCore Runtime, your server’s code is packaged as a zip and run in the AgentCore managed runtime. AI assistants invoke the AgentCore Runtime data plane endpoint, which forwards tools/* and resources/* calls to your server over the streamable HTTP transport.
With infra: agentcore-ecr, the MCP server is built into a container image, pushed to Amazon ECR and run in AgentCore Runtime. This gives you OS-level control over the runtime environment, at the cost of a longer build and deploy cycle than the zip packaging above.
With infra: none, no AWS infrastructure is generated. The MCP server is configured for local STDIO and HTTP transports only, and is consumed by AI assistants running on the same machine.
Working with Your MCP Server
Section titled “Working with Your MCP Server”Adding Tools
Section titled “Adding Tools”Tools are functions that the AI assistant can call to perform actions. Each tool lives in its own file under tools/ that exports a register<Name>Tool function, which you then call from server.ts. For example, add tools/my-tool.ts:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';import { z } from 'zod';
export const registerMyTool = (server: McpServer) => { server.registerTool( 'toolName', { description: 'tool description', // Input schema using Zod inputSchema: { param1: z.string(), param2: z.number() }, }, async ({ param1, param2 }) => { // Tool implementation const result = `${param1} ${param2}`; return { content: [{ type: 'text' as const, text: result }], }; }, );};Then register it inside createServer in server.ts:
import { registerMyTool } from './tools/my-tool.js';
export const createServer = async () => { const server = new McpServer({ name: 'my-server', version: '1.0.0' });
registerMyTool(server);
return server;};Adding Resources
Section titled “Adding Resources”Resources provide context to the AI assistant. Like tools, each resource lives in its own file under resources/ that exports a register<Name>Resource function called from server.ts. You can add static resources from files or dynamic resources:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
const fetchSomeData = async (): Promise<string> => 'some dynamic context';
export const registerMyResource = (server: McpServer) => { const exampleContext = 'some context to return';
server.registerResource( 'resource-name', 'example://resource', {}, async (uri) => ({ contents: [{ uri: uri.href, text: exampleContext }], }), );
// Dynamic resource server.registerResource( 'dynamic-resource', 'dynamic://resource', {}, async (uri) => { const data = await fetchSomeData(); return { contents: [{ uri: uri.href, text: data }], }; }, );};Register it inside createServer in server.ts the same way as a tool:
import { registerMyResource } from './resources/my-resource.js';
export const createServer = async () => { const server = new McpServer({ name: 'my-server', version: '1.0.0' });
registerMyResource(server);
return server;};Configuring with AI Assistants
Section titled “Configuring with AI Assistants”Configuration Files
Section titled “Configuration Files”Most AI assistants that support MCP use a similar configuration approach. You’ll need to create or update a configuration file with your MCP server details:
{ "mcpServers": { "your-mcp-server": { "command": "npx", "args": ["tsx", "/path/to/your-mcp-server/stdio.ts"] } }}Hot Reload
Section titled “Hot Reload”While developing your MCP server, you may wish to configure the --watch flag so that the AI assistant always sees the latest versions of tools/resources:
{ "mcpServers": { "your-mcp-server": { "command": "npx", "args": ["tsx", "--watch", "/path/to/your-mcp-server/stdio.ts"] } }}Assistant-Specific Configuration
Section titled “Assistant-Specific Configuration”Please refer to the following documentation for configuring MCP with specific AI Assistants:
Running Your MCP Server
Section titled “Running Your MCP Server”Local Development
Section titled “Local Development”To run your MCP server (and everything connected to it, such as a local database) locally, use the project’s dev target:
pnpm nx dev your-projectyarn nx dev your-projectnpx nx dev your-projectbunx nx dev your-projectIf you have added multiple components to your project (MCP servers, agents, etc.), this starts them all. To run just this MCP server, target its <your-server-name>-dev target:
pnpm nx your-server-name-dev your-projectyarn nx your-server-name-dev your-projectnpx nx your-server-name-dev your-projectbunx nx your-server-name-dev your-projectInspector
Section titled “Inspector”The generator configures a target named <your-server-name>-inspect, which starts your MCP server locally (via the <your-server-name>-dev target, including any connected dependencies such as a local database) and launches the MCP Inspector pre-configured to connect to it over Streamable HTTP transport.
pnpm nx your-server-name-inspect your-projectyarn nx your-server-name-inspect your-projectnpx nx your-server-name-inspect your-projectbunx nx your-server-name-inspect your-projectThis will start the inspector at http://localhost:6274. Get started by clicking on the “Connect” button.
The easiest way to test and use an MCP server is by using the inspector or configuring it with an AI assistant (as above).
You can however run your server with STDIO transport directly using the <your-server-name>-serve-stdio target.
pnpm nx your-server-name-serve-stdio your-projectyarn nx your-server-name-serve-stdio your-projectnpx nx your-server-name-serve-stdio your-projectbunx nx your-server-name-serve-stdio your-projectThis command uses tsx --watch to automatically restart the server when files change.
Streamable HTTP
Section titled “Streamable HTTP”If you would like to run your MCP server locally using Streamable HTTP transport, you can use the <your-server-name>-serve target.
pnpm nx your-server-name-serve your-projectyarn nx your-server-name-serve your-projectnpx nx your-server-name-serve your-projectbunx nx your-server-name-serve your-projectThis command uses tsx --watch to automatically restart the server when files change.
Deploying Your MCP Server to Bedrock AgentCore Runtime
Section titled “Deploying Your MCP Server to Bedrock AgentCore Runtime”Infrastructure as Code
Section titled “Infrastructure as Code”If you selected agentcore or agentcore-ecr for infra, the relevant CDK or Terraform infrastructure is generated which you can use to deploy your MCP server to Amazon Bedrock AgentCore Runtime.
A CDK construct is generated for your MCP Server, named based on the name you chose when running the generator, or <ProjectName>McpServer by default.
You can use this CDK construct in a CDK application:
import { MyProjectMcpServer } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // Add the MCP server to your stack new MyProjectMcpServer(this, 'MyProjectMcpServer'); }}A Terraform module is generated for you, named based on the name you chose when running the generator, or <ProjectName>-mcp-server by default.
Pass the shared runtime_config_appconfig module’s outputs into the MCP server module, along with the shared artefact store its packaging uses. Under the default agentcore packaging the server’s code is staged in the shared asset bucket, so instantiate the core/asset-bucket module once per deployment, as the Lambda and API modules already do:
module "asset_bucket" { source = "../../common/terraform/src/core/asset-bucket"}
module "my_project_mcp_server" { source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"
appconfig_application_id = module.runtime_config_appconfig.application_id appconfig_application_arn = module.runtime_config_appconfig.application_arn
asset_bucket_name = module.asset_bucket.bucket_name asset_bucket_arn = module.asset_bucket.bucket_arn}Under agentcore-ecr the server’s image is published to the shared asset registry instead, so pass core/asset-ecr’s outputs rather than the bucket’s. One registry serves every container in the workspace, so no MCP server needs a repository of its own:
module "asset_ecr" { source = "../../common/terraform/src/core/asset-ecr"}
module "my_project_mcp_server" { source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"
appconfig_application_id = module.runtime_config_appconfig.application_id appconfig_application_arn = module.runtime_config_appconfig.application_arn
asset_ecr_repository_url = module.asset_ecr.repository_url asset_ecr_repository_arn = module.asset_ecr.repository_arn}Authentication
Section titled “Authentication”The generator provides an auth option to configure authentication for your MCP server. You can choose between IAM (default) or Cognito authentication when generating your MCP server.
By default, your MCP server will be secured using IAM authentication, simply deploy it without any arguments:
import { MyProjectMcpServer } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { new MyProjectMcpServer(this, 'MyProjectMcpServer'); }}You can grant access to invoke your MCP server on Bedrock AgentCore Runtime using the grantInvokeAccess method. For example you may wish for an agent generated with the py#agent generator to call your MCP server:
import { MyProjectAgent, MyProjectMcpServer } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const agent = new MyProjectAgent(this, 'MyProjectAgent'); const mcpServer = new MyProjectMcpServer(this, 'MyProjectMcpServer');
mcpServer.grantInvokeAccess(agent); }}# MCP Servermodule "my_project_mcp_server" { # Relative path to the generated module in the common/terraform project source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"
appconfig_application_id = module.runtime_config_appconfig.application_id appconfig_application_arn = module.runtime_config_appconfig.application_arn}To grant access to invoke your MCP server, you will need to add a policy such as the following, referencing the module.my_project_mcp_server.agent_core_runtime_arn output:
{ Effect = "Allow" Action = [ "bedrock-agentcore:InvokeAgentRuntime" ] Resource = [ module.my_project_mcp_server.agent_core_runtime_arn, "${module.my_project_mcp_server.agent_core_runtime_arn}/*" ]}Cognito Authentication
Section titled “Cognito Authentication”When you select Cognito authentication, the generator configures the MCP server to use Cognito for authentication.
The generated construct accepts an identity prop which configures Cognito authentication:
import { MyProjectMcpServer, UserIdentity } from '@my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { const identity = new UserIdentity(this, 'Identity');
new MyProjectMcpServer(this, 'MyProjectMcpServer', { identity, }); }}The UserIdentity construct can be generated using the ts#website#auth generator, or you can create your own CDK UserPool and UserPoolClient.
The generated module accepts user_pool_id and user_pool_client_ids variables for Cognito authentication:
module "user_identity" { source = "../../common/terraform/src/core/user-identity"}
module "my_project_mcp_server" { source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"
appconfig_application_id = module.runtime_config_appconfig.application_id appconfig_application_arn = module.runtime_config_appconfig.application_arn
user_pool_id = module.user_identity.user_pool_id user_pool_client_ids = [module.user_identity.user_pool_client_id]}Bundle Target
Section titled “Bundle Target”The generator automatically configures a bundle target which uses Rolldown to create a deployment package:
pnpm nx bundle <project-name>yarn nx bundle <project-name>npx nx bundle <project-name>bunx nx bundle <project-name>Rolldown configuration can be found in rolldown.config.ts, with an entry per bundle to generate. Rolldown manages creating multiple bundles in parallel if defined.
The bundle target uses http.ts as the entrypoint for the Streamable HTTP MCP server to host on Bedrock AgentCore Runtime.
Package Target
Section titled “Package Target”The generator configures a <your-server-name>-package target which assembles the deployable code package: the bundled index.js plus a vendored install of the AWS Distro for OpenTelemetry, which AgentCore requires to be present in the package. The generated infrastructure uploads this directory as a .zip — via AgentRuntimeArtifact.fromCodeAsset under CDK, or archived into the shared asset bucket under Terraform.
Docker Target
Section titled “Docker Target”The generator configures a <your-server-name>-docker target which copies the Dockerfile from your MCP server source directory into the bundle output directory. This co-locates the Dockerfile with the bundled artifacts, allowing CDK to build the Docker image directly using AgentRuntimeArtifact.fromAsset.
A docker target is also generated which prepares the docker context for all MCP servers if you have multiple defined.
Image Scanning
Section titled “Image Scanning”The Docker image built for this project can be scanned for vulnerabilities using Trivy, running from the ECR-hosted Trivy image.
A trivy target is added to your project which scans the built image and exits non-zero if any HIGH or CRITICAL severity vulnerability is found. The generated Dockerfile uses a base image with no known fixable vulnerabilities of these severities at time of generation, and upgrades bundled tooling (such as npm) to keep it that way.
The scan uses the same container engine as your image build (docker or finch), so no additional tooling is required. The scan is not cached, since the image it reads lives in the container engine rather than on disk — so it always scans the real image, and fails loudly rather than reporting a cached pass for an image that is no longer there. Each run therefore takes tens of seconds per image and refreshes Trivy’s vulnerability database, so it needs network access. The vended trivy root script scans every image in the workspace:
pnpm trivyyarn trivynpm run trivybun trivySuppressing Trivy Findings
Section titled “Suppressing Trivy Findings”There may be instances where you want to suppress a specific vulnerability, for example when no fix is yet available and you have assessed the risk as acceptable.
Add the vulnerability ID (one per line) to the .trivyignore file in the root of your project (i.e. next to your project.json):
# node-tar arbitrary file write - not exploitable in our usageCVE-2024-XXXXXFor more details on filtering findings, refer to the Trivy filtering documentation.
Observability
Section titled “Observability”Your MCP server is automatically configured with observability using the AWS Distro for Open Telemetry (ADOT), by configuring auto-instrumentation in your Dockerfile.
You can find traces in the CloudWatch AWS Console, by selecting “GenAI Observability” in the menu. Note that for traces to be populated you will need to enable Transaction Search.
For more details, refer to the AgentCore documentation on observability.
Connections
Section titled “Connections”Use the connection generator to integrate this project with others in your workspace. The following connections involve this project: