I Bundled Tools into AgentCore Gateway and Tried Using Them from Managed Harness

I Bundled Tools into AgentCore Gateway and Tried Using Them from Managed Harness

AgentCore's Managed Harness makes building agents easy, but preparing tools like RAG is a hassle. I built a CDK-deployable kit that bundles knowledge base search, web page retrieval, and AWS docs search into one Gateway.
2026.05.18

This page has been translated by machine translation. View original

Introduction

Hello, I'm Kanno from the Consulting Department, and I also love reality dating shows.

AgentCore's Managed Harness is incredibly convenient, isn't it? You can quickly build agents without writing any code. Are you all using it?

https://dev.classmethod.jp/articles/bedrock-agentcore-managed-harness-preview/

I tried it out too, but you have to prepare the tools side yourself. Especially for things like RAG, I felt that building it from scratch every time is a hassle... These days I find myself wishing it could be as easy as Bedrock Agents.

So this time, to make it easy for Harness users to use things like RAG, I built a toolkit with the following concept: bundle three tools into AgentCore Gateway — knowledge base search, web page retrieval, and AWS documentation search — and deploy it with CDK, so that anyone can connect to it from Managed Harness and use it.

Just connecting a single Gateway makes all three tools available, so the effort on the user side is greatly reduced.

The code for this project is published in the repository below. In the article I excerpt and explain the key points, but please refer to the repository for the full picture.

https://github.com/yuu551/agentcore-gateway-kit

AgentCore Gateway

Before getting to the main topic, let me briefly go over the role of Gateway.

Gateway is a hub that bundles existing APIs, Lambda functions, MCP Servers, and so on, so that agents can call them in a unified way.

Gatewayの概要

Even without Gateway, you can call Lambda or MCP Servers directly from Runtime. However, if your organization has many Lambda functions and MCP Servers, linking each of them individually to agents becomes hard to manage.

Gatewayを使う場合と使わない場合の比較

If you consolidate them in Gateway, the agent side only needs to connect to one Gateway to access all the tools behind it. Another benefit is that you don't need to change the agent's code even when tools are added or changed.

As in this case, if the infrastructure provider deploys the Gateway with CDK, users just add the Gateway from the Managed Harness console. The key point is that they don't need to be aware of what's inside the tools.

For details on Gateway and tips for production operation, please see the article below!

https://dev.classmethod.jp/articles/amazon-bedrock-agentcore-production-tips-ai-builders-day-2025/

Architecture

Here is the overall picture.

architecture

In this setup, multiple users each create their own AI agents with Managed Harness and access tools such as RAG via the Gateway. Tool developers only need to deploy once with CDK, and users can use the tools just by connecting the Gateway.

Three Targets are registered in the Gateway.

Target Tool name Description Implementation
kb-retrieve retrieve_documents Search documents from the knowledge base Lambda
web-tools fetch_webpage Retrieve text from web pages FastMCP on AgentCore Runtime
aws-knowledge search_documentation and others Search official AWS documentation AWS-hosted MCP Server

I deliberately used three kinds of Target implementations. The reason is that I wanted to try out how to write each of them in CDK: Lambda target / MCP Server on Runtime / external MCP Server.

Prerequisites

  • AWS CDK v2.254.0
  • @aws-cdk/aws-bedrock-agentcore-alpha v2.254.0-alpha.0
  • Node.js 22+ (verified with v25.9.0)
  • Docker (required for building the Runtime container)
  • Deployment region: us-east-1

Project structure

agentcore-gateway-kit/
├── bin/
│   └── app.ts                     # CDK entry point
├── lib/
│   ├── knowledge-base-stack.ts    # Stack 1: KB + S3 Vectors
│   └── rag-gateway-stack.ts       # Stack 2: Gateway + Lambda + Runtime
├── lambda/
│   └── kb-retrieve/
│       └── index.py               # KB Retrieve Lambda
├── mcp-server/
│   ├── server.py                  # FastMCP Web Tools
│   ├── requirements.txt
│   └── Dockerfile
├── sample-data/
│   └── sample.txt
├── cdk.json
├── package.json
└── tsconfig.json

I've made it a two-stack setup. By separating the Knowledge Base and the Gateway, you can operate in ways such as keeping the Knowledge Base while replacing only the Gateway side.

Implementation points

For the full code, please refer to the repository; here I'll focus on the points for building the Gateway and each Target with CDK.

Knowledge Base stack

I'm using S3 Vectors as the vector store. Unlike OpenSearch Serverless, it needs no additional infrastructure, and for verification purposes it's cheap and easy to handle.

S3 Vectors is defined with CDK L1 constructs (CfnVectorBucket / CfnIndex).

lib/knowledge-base-stack.ts
const vectorBucket = new s3vectors.CfnVectorBucket(this, 'VectorBucket', {
  vectorBucketName,
});

const vectorIndex = new s3vectors.CfnIndex(this, 'VectorIndex', {
  vectorBucketName,
  indexName: 'rag-gateway-index',
  dimension: VECTOR_DIMENSION,
  distanceMetric: 'cosine',
  dataType: 'float32',
});
vectorIndex.addDependency(vectorBucket);

You link it by specifying S3_VECTORS in the Knowledge Base's storageConfiguration.

lib/knowledge-base-stack.ts
const knowledgeBase = new bedrock.CfnKnowledgeBase(this, 'KnowledgeBase', {
  name: 'RagGatewayKnowledgeBase',
  roleArn: kbRole.roleArn,
  knowledgeBaseConfiguration: {
    type: 'VECTOR',
    vectorKnowledgeBaseConfiguration: {
      embeddingModelArn: `arn:aws:bedrock:${cdk.Aws.REGION}::foundation-model/${EMBEDDING_MODEL_ID}`,
    },
  },
  storageConfiguration: {
    type: 'S3_VECTORS',
    s3VectorsConfiguration: {
      vectorBucketArn: vectorBucket.attrVectorBucketArn,
      indexName: vectorIndex.indexName!,
    },
  },
});

For the embedding model, I'm using Amazon Titan Embed Text V2 (1024 dimensions).

Creating the Gateway

lib/rag-gateway-stack.ts
const gateway = new agentcore.Gateway(this, 'RagGateway', {
  gatewayName: 'rag-gateway-kit',
  description: 'RAG Gateway Kit: KB Retrieve + Web Fetch + AWS Knowledge',
  authorizerConfiguration: agentcore.GatewayAuthorizer.usingAwsIam(),
});

I'm using the L2 construct from @aws-cdk/aws-bedrock-agentcore-alpha. The L2 default is Cognito authentication, but Managed Harness connects with IAM, so Cognito would cause an error. I explicitly specify IAM with GatewayAuthorizer.usingAwsIam().

Lambda target (Knowledge Base Retrieve)

A Lambda target can be added with the L2 addLambdaTarget.

lib/rag-gateway-stack.ts
gateway.addLambdaTarget('KBRetrieveTarget', {
  gatewayTargetName: 'kb-retrieve',
  lambdaFunction: kbRetrieveFn,
  toolSchema: agentcore.ToolSchema.fromInline([
    {
      name: 'retrieve_documents',
      description: 'ナレッジベースからドキュメントを検索します。',
      inputSchema: {
        type: agentcore.SchemaDefinitionType.OBJECT,
        properties: {
          query: { type: agentcore.SchemaDefinitionType.STRING, description: '検索クエリ' },
          maxResults: { type: agentcore.SchemaDefinitionType.INTEGER, description: '最大取得件数' },
        },
        required: ['query'],
      },
    },
  ]),
});

The tool's input schema is defined with ToolSchema.fromInline. Note that you need to use the SchemaDefinitionType enum to specify types; using string literals (such as "object") results in a compile error.

Here's what the Lambda side does.

lambda/kb-retrieve/index.py
def handler(event, context):
    query = event.get('query', '')
    kb_id = event.get('knowledgeBaseId', KNOWLEDGE_BASE_ID)
    max_results = min(event.get('maxResults', 5), 100)

    response = client.retrieve(
        knowledgeBaseId=kb_id,
        retrievalQuery={'text': query},
        retrievalConfiguration={
            'vectorSearchConfiguration': { 'numberOfResults': max_results }
        },
    )

    results = []
    for r in response.get('retrievalResults', []):
        results.append({
            'content': r['content'].get('text', ''),
            'score': r.get('score'),
            'location': r.get('location', {}),
        })

    return { 'results': results, 'count': len(results) }

It's a simple implementation that just calls Bedrock's Retrieve API and returns the search results. The reason for using Retrieve rather than RetrieveAndGenerate is to have the Managed Harness-side model generate the answer.

MCP Server on Runtime (web page retrieval)

We register a FastMCP server running on AgentCore Runtime as a Gateway Target.

Here is the MCP Server code.

mcp-server/server.py
mcp = FastMCP("web-tools", host="0.0.0.0", stateless_http=True)

@mcp.tool()
def fetch_webpage(url: str, max_length: int = 5000) -> str:
    """Fetch a web page and return its text content."""
    response = httpx.get(url, follow_redirects=True, timeout=30, headers={
        'User-Agent': 'AgentCore-WebTools/1.0',
    })
    response.raise_for_status()

    html = response.text
    text = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL)
    text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL)
    text = re.sub(r'<[^>]+>', ' ', text)
    text = re.sub(r'\s+', ' ', text).strip()

    return text[:max_length]

The tool is defined with a FastMCP decorator. It's simple processing that strips HTML tags and returns only the text.

On the CDK side, the L2 AgentRuntimeArtifact.fromAsset automatically handles everything from building the Dockerfile to pushing to ECR.

lib/rag-gateway-stack.ts
const runtimeEndpointUrl = cdk.Fn.sub(
  'https://bedrock-agentcore.${AWS::Region}.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3A${AWS::Region}%3A${AWS::AccountId}%3Aruntime%2F${RuntimeId}/invocations',
  { RuntimeId: webToolsRuntime.agentRuntimeId },
);

const webToolsTarget = new bedrockagentcore.CfnGatewayTarget(this, 'WebToolsTarget', {
  gatewayIdentifier: gateway.gatewayId,
  name: 'web-tools',
  targetConfiguration: {
    mcp: {
      mcpServer: { endpoint: runtimeEndpointUrl },
    },
  },
  credentialProviderConfigurations: [{
    credentialProviderType: 'GATEWAY_IAM_ROLE',
    credentialProvider: {
      iamCredentialProvider: { service: 'bedrock-agentcore' },
    },
  }],
});

The endpoint URL is in a format that embeds the URL-encoded Runtime ARN.

For MCP Server targets, you need to specify the sub-property (service) of iamCredentialProvider in credentialProviderConfigurations. The L2 GatewayCredentialProvider.fromIamRole() doesn't output this sub-property, so for MCP Server targets I use L1 (CfnGatewayTarget). For Lambda targets, L2 is fine.

Another important point is dependencies.

lib/rag-gateway-stack.ts
webToolsTarget.addDependency(
  webToolsRuntime.node.defaultChild as cdk.CfnResource,
);
const gatewayDefaultPolicy = gateway.role.node.findChild('DefaultPolicy').node.defaultChild as cdk.CfnResource;
webToolsTarget.addDependency(gatewayDefaultPolicy);

When creating a Gateway Target, the Gateway connects to the endpoint and tries to fetch the tool list. At this point, it fails if the Runtime is not in the READY state, and it cannot even connect if the Gateway role lacks the InvokeAgentRuntime permission. I've explicitly added both dependencies.

AWS Knowledge MCP Server

This is an MCP Server hosted officially by AWS. You can use it just by specifying the endpoint URL.

https://awslabs.github.io/mcp/servers/aws-knowledge-mcp-server

lib/rag-gateway-stack.ts
new bedrockagentcore.CfnGatewayTarget(this, 'AWSKnowledgeTarget', {
  gatewayIdentifier: gateway.gatewayId,
  name: 'aws-knowledge',
  targetConfiguration: {
    mcp: {
      mcpServer: { endpoint: 'https://knowledge-mcp.global.api.aws' },
    },
  },
});

Since it's a public endpoint requiring no authentication, I've omitted credentialProviderConfigurations. Six tools — search_documentation / read_documentation / recommend / list_regions / get_regional_availability / retrieve_skill — are automatically registered in the Gateway.

How to add tools to the Gateway

The nice thing about Gateway is that you can add new tools just by adding targets. Here are examples of adding a Lambda target and an MCP Server target.

Adding a Lambda tool

You can add it with the L2 addLambdaTarget.

lib/rag-gateway-stack.ts
gateway.addLambdaTarget('MyNewTarget', {
  gatewayTargetName: 'my-tool',
  lambdaFunction: myFunction,
  toolSchema: agentcore.ToolSchema.fromInline([{
    name: 'my_tool',
    description: 'ツールの説明',
    inputSchema: {
      type: agentcore.SchemaDefinitionType.OBJECT,
      properties: { /* ... */ },
    },
  }]),
});

Lambda targets are simple since they can be completed with L2 alone.

Adding an MCP Server target

As mentioned earlier, MCP Server targets use L1 (CfnGatewayTarget).

lib/rag-gateway-stack.ts
new bedrockagentcore.CfnGatewayTarget(this, 'MyMcpTarget', {
  gatewayIdentifier: gateway.gatewayId,
  name: 'my-mcp-server',
  targetConfiguration: {
    mcp: {
      mcpServer: {
        endpoint: 'https://your-mcp-server.example.com',
      },
    },
  },
  credentialProviderConfigurations: [{
    credentialProviderType: 'GATEWAY_IAM_ROLE',
    credentialProvider: {
      iamCredentialProvider: {
        service: 'bedrock-agentcore',
      },
    },
  }],
});

The value of service varies depending on the connection destination. For an MCP Server on AgentCore Runtime, specify bedrock-agentcore.

Just by adding a Target and running cdk deploy, the new tool becomes immediately available from existing Managed Harnesses. Not having to touch the agent side is great from an operations standpoint.

This time I've written everything directly in the stack for simplicity, but creating helper functions or Constructs for each Target type would probably make it easier to manage as the number of tools grows.

Deployment

Execution command
npm install
npx cdk deploy --all

When you deploy, the sample documents about AgentCore under sample-data are automatically uploaded to the S3 bucket. However, simply placing files in S3 doesn't reflect them in the Knowledge Base. You need to run a sync from the console or CLI.

In the console, select the DataSource from the Knowledge Base screen and press the "Sync" button.

kb-sync

For the CLI, use the command below. KnowledgeBaseId and DataSourceId can be obtained from the CDK Output.

Execution command
aws bedrock-agent start-ingestion-job \
  --knowledge-base-id <KnowledgeBaseId> \
  --data-source-id <DataSourceId>

If you want to make your own documents searchable, either add documents under sample-data and run cdk deploy, or upload them to the S3 bucket and then run the sync.

Execution command
aws s3 cp my-document.txt s3://<DataSourceBucketName>/

aws bedrock-agent start-ingestion-job \
  --knowledge-base-id <KnowledgeBaseId> \
  --data-source-id <DataSourceId>

Verification

Once deployment is complete, let's create a Managed Harness from the console and check that it works!

Creating a Managed Harness

Open AWS Console → Bedrock → AgentCore → Managed Harness.
Create a new one from Advanced create harness.

harness-list

Enable Gateway in the Tools section and select the deployed rag-gateway-kit. Just by adding one Gateway, all the tools included in the three Targets become available.

harness-create

That was easy to create.

Once creation is complete, the Status becomes Ready. Let's verify operation from "Test harness".

harness-detail

Checking Knowledge Base search (retrieve_documents)

First, let's try searching the knowledge base. I sent "Search for information about AgentCore using RAG."

test-kb-search

It retrieved information from the knowledge base and generated an answer! It even summarized use-case scenarios for Gateway Kit in a table.

Checking web page retrieval (fetch_webpage)

Next is web page retrieval. I'll pass in a DevelopersIO article URL and ask about its content.

test-web-fetch

It retrieved the content of the URL and returned a structured response including the article overview, environment used, and verification results!

Checking AWS documentation search (search_documentation and others)

Finally, searching the official AWS documentation. I'll send "Tell me about S3."

test-aws-knowledge

Via the AWS Knowledge MCP Server, it retrieved information from the official documentation and summarized everything from the basic concepts of S3 to storage classes and the latest updates. If you open the tool call details, you can confirm that search_documentation was executed.

test-aws-knowledge-tool

We confirmed that all three tools work without problems via the Gateway!

Conclusion

I built, with CDK, a setup that bundles multiple tools in a Gateway so they can be used from Managed Harness. Consolidating three patterns — Lambda target / MCP Server on Runtime / external MCP Server — into a single Gateway so that many Harnesses can use them easily is great.

Being able to add new tools just by adding a Target to the Gateway is also a nice point. It might be possible to add features that Harness users frequently request to the Gateway! (For example, a web search feature using Brave.)

I hope this article was of some help. Thank you for reading to the end!

Share this article

AWSのお困り事はクラスメソッドへ