I tried operating a lakehouse with natural language from Amazon QuickSight using aws-dataprocessing-mcp-server hosted on Amazon Bedrock AgentCore Runtime

I tried operating a lakehouse with natural language from Amazon QuickSight using aws-dataprocessing-mcp-server hosted on Amazon Bedrock AgentCore Runtime

I tried hosting AWS Labs' aws-dataprocessing-mcp-server on Amazon Bedrock AgentCore Runtime to enable natural language querying and modification of a lakehouse from Amazon QuickSight. I will introduce the implementation challenges encountered along the way, including switching to streamable-http and handling DNS rebinding protection, along with their solutions.
2026.05.23

This page has been translated by machine translation. View original

This is Ishikawa from the Cloud Business Division. I tried hosting the aws-dataprocessing-mcp-server published by AWS Labs on Amazon Bedrock AgentCore Runtime, enabling natural language operations on data lakes and lakehouses from Amazon Quick.

https://aws.amazon.com/jp/blogs/machine-learning/integrating-aws-api-mcp-server-with-amazon-quick-suite-using-amazon-bedrock-agentcore-runtime/

The AWS Labs MCP server collection includes servers specialized for specific use cases. This time I focused on aws-dataprocessing-mcp-server for data analytics operations, and ran it with the Quick + AgentCore Runtime + Cognito JWT authentication integration pattern.

https://github.com/awslabs/mcp/tree/main/src/aws-dataprocessing-mcp-server

aws-dataprocessing-mcp-server is distributed via PyPI / uvx and is stdio-only. To run it on AgentCore Runtime, you need to build a custom container that listens on streamable-http and work around several "gotchas." This article covers that implementation as well.

What is aws-dataprocessing-mcp-server

aws-dataprocessing-mcp-server is a server for operating AWS data processing services such as AWS Glue and Amazon Athena via the Model Context Protocol (MCP). It provides 36 tools covering common data engineering operations including Glue Data Catalog queries, Athena query execution, and table and database management.

The main tool categories are as follows.

  • Glue Data Catalog: manage_aws_glue_databases, manage_aws_glue_tables, manage_aws_glue_connections, manage_aws_glue_partitions
  • Glue ETL: manage_aws_glue_jobs, manage_aws_glue_workflows, manage_aws_glue_triggers, manage_aws_glue_crawlers
  • Glue Interactive Sessions: manage_aws_glue_sessions, manage_aws_glue_statements
  • EMR: manage_aws_emr_clusters, manage_aws_emr_ec2_steps, manage_aws_emr_serverless_applications
  • Athena: manage_aws_athena_query_executions, manage_aws_athena_workgroups, manage_aws_athena_data_catalogs
  • Common: create_data_processing_role, list_s3_buckets, add_inline_policy

By default it starts in read-only mode, and adding the --allow-write flag also enables write operations. For this article, we verified with read-only mode to prioritize safety.

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

What is Amazon Bedrock AgentCore Runtime

Amazon Bedrock AgentCore Runtime is a serverless execution environment for hosting AI agents and MCP servers. It supports communication via the Model Context Protocol (MCP) and Agent to Agent (A2A) protocols, and isolates sessions at the microVM level. It handles JWT authentication, sending observability data to CloudWatch / X-Ray, and launching from ECR container images all in one place.

https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-mcp.html

Overall Architecture

The request flow is as follows.

What I Tried

Prerequisites

  • AWS account (this verification was performed in a sandbox in ap-northeast-1)
  • Amazon Quick Enterprise edition
  • Container runtime capable of building linux/arm64 images (this verification used Mac + Podman)
  • MCP version at time of verification: awslabs.aws-dataprocessing-mcp-server: 0.1.31

Step1: Designing the Custom Container for AgentCore Runtime

The server.py of aws-dataprocessing-mcp-server starts FastMCP() with default settings and therefore listens on stdio transport. Since AgentCore Runtime requires streamable-http, port 8000, and the /mcp path, we write a thin wrapper that does the following.

  1. Override FastMCP.__init__ to pass TransportSecuritySettings(enable_dns_rebinding_protection=False)
  2. Override FastMCP.run to set mcp.settings.host = "0.0.0.0", port = 8000, stateless_http = True, then start with streamable-http
  3. Make the same handler respond to both /mcp and /mcp/, and suppress 307 redirects with redirect_slashes=False

The core of the wrapper server_http.py is as follows.

import uvicorn
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings
from starlette.routing import Route

_original_init = FastMCP.__init__

def _patched_init(self, *args, **kwargs):
    kwargs.setdefault(
        "transport_security",
        TransportSecuritySettings(enable_dns_rebinding_protection=False),
    )
    _original_init(self, *args, **kwargs)

FastMCP.__init__ = _patched_init

_original_run = FastMCP.run

def _patched_run(self, transport=None, **kwargs):
    self.settings.host = "0.0.0.0"
    self.settings.port = 8000
    self.settings.stateless_http = True

    if transport == "streamable-http" or transport is None:
        app = self.streamable_http_app()
        app.router.redirect_slashes = False
        existing = next(
            r for r in app.router.routes if isinstance(r, Route) and r.path == "/mcp"
        )
        app.router.routes.append(
            Route("/mcp/", endpoint=existing.endpoint, methods=existing.methods)
        )
        uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info")
        return
    return _original_run(self, transport=transport)

FastMCP.run = _patched_run

from awslabs.aws_dataprocessing_mcp_server.server import main  # noqa: E402

if __name__ == "__main__":
    main()

The important point here is the first override of transport_security. FastMCP has enabled DNS rebinding protection by default since MCP Python SDK 1.21. Since AgentCore Runtime's proxy forwards to the internal microVM with the upstream AWS domain set in the Host header, 421 Misdirected Request will be returned unless it is disabled. Since JWT authentication is performed on the AgentCore Runtime side, disabling this here still maintains defense in depth.

https://github.com/modelcontextprotocol/python-sdk/issues/1798

Step2: Building the Docker Image

The Dockerfile is structured as follows.

FROM --platform=linux/arm64 python:3.12-slim

ENV PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1

WORKDIR /app

COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt

COPY server_http.py /app/server_http.py

RUN groupadd -r app && useradd -r -g app -d /home/app -m app
USER app

EXPOSE 8000

ENTRYPOINT ["python", "/app/server_http.py"]

Pin the versions in requirements.txt.

awslabs.aws-dataprocessing-mcp-server==0.1.31
mcp[cli]==1.23.0
uvicorn==0.32.0

Build specifying linux/arm64. On Apple Silicon Mac this will be a native build.

podman build --platform linux/arm64 -t dataprocessing-mcp-agentcore:v0.4 .

Do a quick smoke test locally using the curl command.

$ curl -s -X POST "http://localhost:8000/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0.1"}}}' | head -3

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{...},"serverInfo":{"name":"awslabs.aws-dataprocessing-mcp-server","version":"1.23.0"}, ...}}

I created a private repository in ap-northeast-1 and pushed the built image.

Step3: Creating the Amazon Cognito User Pool

Create a User Pool and App Client for Machine-to-Machine authentication.

aws cognito-idp create-user-pool \
  --pool-name dp-mcp-agentcore-pool \
  --policies '{"PasswordPolicy":{"MinimumLength":12,"RequireUppercase":true,"RequireLowercase":true,"RequireNumbers":true,"RequireSymbols":true}}' \
  --region ap-northeast-1

This gives us Pool ID ap-northeast-1_ogeMbwGMK. Next, create a Resource Server and scopes.

aws cognito-idp create-resource-server \
  --user-pool-id ap-northeast-1_ogeMbwGMK \
  --identifier dp-mcp \
  --name "Dataprocessing MCP Resource Server" \
  --scopes \
    ScopeName=read,ScopeDescription="Read data processing resources" \
    ScopeName=write,ScopeDescription="Write data processing resources" \
  --region ap-northeast-1

Create a domain prefix and App Client (for client_credentials).

aws cognito-idp create-user-pool-domain \
  --user-pool-id ap-northeast-1_ogeMbwGMK \
  --domain dp-mcp-1f6b687c \
  --region ap-northeast-1

aws cognito-idp create-user-pool-client \
  --user-pool-id ap-northeast-1_ogeMbwGMK \
  --client-name dp-mcp-m2m-client \
  --generate-secret \
  --allowed-o-auth-flows client_credentials \
  --allowed-o-auth-scopes "dp-mcp/read" "dp-mcp/write" \
  --allowed-o-auth-flows-user-pool-client \
  --supported-identity-providers COGNITO \
  --region ap-northeast-1

This gives us the Token URL https://dp-mcp-1f6b687c.auth.ap-northeast-1.amazoncognito.com/oauth2/token and Client ID / Client Secret.

20260522-amazon-bedrock-agentcore-with-amazon-quick-1

Step4: Creating the IAM Execution Role

The execution role passed to AgentCore Runtime requires at minimum the following permissions.

  • ECR pull (scoped to target repository ARN)
  • CloudWatch Logs (only /aws/bedrock-agentcore/runtimes/*)
  • X-Ray PutTrace family
  • bedrock-agentcore:GetWorkloadAccessToken* (workload-identity-directory/default)
  • Read-only APIs for Glue / Athena / S3

For the trust policy, set bedrock-agentcore.amazonaws.com as the Principal and restrict to your own account with aws:SourceAccount.

Step5: Creating the AgentCore Runtime

Create the AgentCore Runtime specifying the ECR image, IAM role, Cognito Discovery URL, and App Client ID.

$ aws bedrock-agentcore-control create-agent-runtime \
  --agent-runtime-name dp_mcp_agentcore \
  --agent-runtime-artifact 'containerConfiguration={containerUri=<AWS_ACCOUNT_ID>.dkr.ecr.ap-northeast-1.amazonaws.com/dataprocessing-mcp-agentcore:v0.4}' \
  --network-configuration 'networkMode=PUBLIC' \
  --protocol-configuration 'serverProtocol=MCP' \
  --role-arn arn:aws:iam::<AWS_ACCOUNT_ID>:role/AgentCoreRuntimeRole-DpMcp \
  --authorizer-configuration 'customJWTAuthorizer={discoveryUrl=https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_ogeMbwGMK/.well-known/openid-configuration,allowedClients=[38gvcra4aiq2kof9tim48ujoqb]}' \
  --environment-variables 'AWS_REGION=ap-northeast-1,FASTMCP_LOG_LEVEL=INFO' \
  --region ap-northeast-1

{
  "agentRuntimeArn": "arn:aws:bedrock-agentcore:ap-northeast-1:<AWS_ACCOUNT_ID>:runtime/dp_mcp_agentcore-O8HEgzH8Fj",
  "agentRuntimeId": "dp_mcp_agentcore-O8HEgzH8Fj",
  "agentRuntimeVersion": "1",
  "status": "CREATING"
}

It became READY within tens of seconds.

20260522-amazon-bedrock-agentcore-with-amazon-quick-2

Step6: Connecting from a Remote MCP Client

Obtain a JWT from Cognito via the client_credentials grant and connect to the AgentCore Runtime endpoint (/invocations?qualifier=DEFAULT) from a Python MCP client.

import asyncio, base64, json, urllib.parse, urllib.request
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

REGION = "ap-northeast-1"
ARN = "arn:aws:bedrock-agentcore:ap-northeast-1:<AWS_ACCOUNT_ID>:runtime/dp_mcp_agentcore-O8HEgzH8Fj"
DOMAIN = "dp-mcp-1f6b687c"
CLIENT_ID = "..."
CLIENT_SECRET = "..."

def get_jwt():
    body = urllib.parse.urlencode({"grant_type": "client_credentials",
                                   "scope": "dp-mcp/read dp-mcp/write"}).encode()
    creds = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
    req = urllib.request.Request(
        f"https://{DOMAIN}.auth.{REGION}.amazoncognito.com/oauth2/token",
        data=body,
        headers={"Authorization": f"Basic {creds}",
                 "Content-Type": "application/x-www-form-urlencoded"},
    )
    return json.loads(urllib.request.urlopen(req).read())["access_token"]

async def main():
    token = get_jwt()
    url = (f"https://bedrock-agentcore.{REGION}.amazonaws.com/runtimes/"
           f"{urllib.parse.quote(ARN, safe='')}/invocations?qualifier=DEFAULT")
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    async with streamablehttp_client(url, headers, timeout=120) as (r, w, _):
        async with ClientSession(r, w) as session:
            await session.initialize()
            tools = await session.list_tools()
            print(f"tool count: {len(tools.tools)}")
            result = await session.call_tool(
                "manage_aws_glue_databases",
                {"operation": "list-databases", "region_name": REGION},
            )
            for c in result.content:
                if c.type == "text":
                    print(c.text[:600])

asyncio.run(main())

The execution result is as follows.

tool count: 36
Successfully listed 7 databases
{"databases":[{"name":"default", ...},{"name":"qsdemo_dev", ...},{"name":"qsdemo_iot", ...},{"name":"qsdemo_sandbox", ...},{"name":"quick_workshop", ...}], "count":7, ...}

36 tools were registered and we were able to retrieve the list of Glue databases. We then called the Athena workgroups and databases within the Athena catalog.

=== manage_aws_athena_databases_and_tables (list-databases) ===
{"database_list":[{"Name":"default"},{"Name":"ishikawa"},{"Name":"iwasa"},{"Name":"qsdemo_dev"},{"Name":"qsdemo_iot"},{"Name":"qsdemo_sandbox"},{"Name":"quick_workshop"}],"count":7}

We were able to verify "viewing the catalog," which serves as the entry point for data analysis, through a tool call via natural language.

Step7: Registering the MCP Connector in Amazon Quick

Select Connectors, then select Model Context Protocol under the Create for your team tab.

20260522-amazon-bedrock-agentcore-with-amazon-quick-4

Under "Connection," configure the following.

  • Name
  • Description
  • MCP server endpoint
  • Connection Type (leave as the default Public Network)

20260522-amazon-bedrock-agentcore-with-amazon-quick-5

Under "Authentication," configure the following.

  • Authentication method: Select Service authentication
  • OAuth Configuration
  • Client ID
  • Client Secret
  • Token URL

20260522-amazon-bedrock-agentcore-with-amazon-quick-7

Under "Review," verify the contents.

20260522-amazon-bedrock-agentcore-with-amazon-quick-8

Under "Publish," register the MCP. Note that turning off Everyone in your organization does not allow individual configuration, so proceed with registering as-is.

20260522-amazon-bedrock-agentcore-with-amazon-quick-9

Step8: Verifying the Amazon Quick MCP Connector

Clicking the [Try it] button displays a chat screen.

20260522-amazon-bedrock-agentcore-with-amazon-quick-10

On the chat screen, this MCP is already selected as the Connector and execution of the following prompt begins.

20260522-amazon-bedrock-agentcore-with-amazon-quick-11

Let's enter a prompt right away. I type "Please show the list of tables in the ishikawa table." Before MCP executes, "Allow" is always required. This is a consideration to prevent unexpected MCP calls.

20260522-amazon-bedrock-agentcore-with-amazon-quick-12

Although I typed "ishikawa table" instead of "ishikawa database," the list for the "ishikawa database" was displayed.

20260522-amazon-bedrock-agentcore-with-amazon-quick-13

Step9: Updating Quick Sight Data with the MCP Connector

There is sometimes a need to update data on "Quick Sight." Moreover, being able to instruct changes in natural language makes it very convenient. Enter the following prompt to perform an update.

In the ishikawa.orders table, please mark rows where profit is negative and discount is 0.4 or greater as "high-discount deficit" and put 要確認 (needs review) in the returned column. Please also tell me how many rows were updated.

The results of the changes are also displayed in a way that is easy for people to understand.

20260522-amazon-bedrock-agentcore-with-amazon-quick-14

:
:
:

20260522-amazon-bedrock-agentcore-with-amazon-quick-15

When I also checked from Amazon Athena, I was able to confirm the changes according to the specified conditions.

20260522-amazon-bedrock-agentcore-with-amazon-quick-16

Discussion

  • The verification in this article was performed in read-only mode. When enabling write operations in production, you will need to add the --allow-write flag and --allow-sensitive-data-access flag to the ENTRYPOINT, and also expand the IAM execution role permissions to include write operations.
  • It is recommended to manage the Cognito Client Secret using AWS Secrets Manager or similar, rather than embedding it in version control or on the client side.
  • The reason enable_dns_rebinding_protection=False is set for DNS rebinding protection is to accommodate the AgentCore Runtime proxy's behavior of rewriting the Host header. While unauthorized external access is blocked by AgentCore Runtime's JWT authentication, please be careful if you reuse the container as-is in a different environment.
  • aws-dataprocessing-mcp-server is at version 0.1.31 (pre-1.0) at the time of writing. The tool configuration and arguments may change in the future. In this article, we pin the version within the container image.

Closing

By connecting Amazon Quick and aws-dataprocessing-mcp-server through Amazon Bedrock AgentCore Runtime, Glue / Athena operations can be integrated into a natural language interface. To run an OSS MCP server on AgentCore Runtime as-is, you will encounter the following "gotchas":

  • A wrapper to switch to streamable-http
  • Building the container for linux/arm64 and pushing to ECR
  • Handling FastMCP's DNS rebinding protection
  • Redirect behavior for the /mcp / /mcp/ paths

I hope the wrapper implementation and verification logs in this article are helpful for those who want to host other AWS Labs MCP servers (aws-iam-mcp-server, aws-s3-mcp-server, etc.) on AgentCore Runtime using the same pattern.

Share this article

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