Enterprise MCP на Bedrock AgentCore с Okta SSO и Trino
Lambda OAuth-механизм, ретранслятор PKCE, шлюз и среда выполнения AgentCore, а также подключения Trino для каждого пользователя без утечки общих токенов.
Источники: Building Enterprise MCP on AWS Bedrock AgentCore: Okta SSO + Trino
В этом руководстве пошагово описывается процесс создания рабочей системы из сырьевых материалов для реализации Enterprise MCP на AWS Bedrock AgentCore: Okta SSO + Trino. Основное внимание уделяется практическим шагам, четким проверкам и коду, который можно просто добавить в репозиторий без необходимости догадываться о его назначении. Для получения обзора необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии системы. Рядом с функциональными результатами следует записывать время выполнения и стоимость токенов или запросов. Отслеживание затрат на раннем этапе помогает избежать неожиданных счетов при переходе от демо-среды к общедоступным средам.
Кратко
Кратко говоря, перед изменением кода необходимо определить входные данные, ответственного за выполнение шага и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Необходимо предоставлять инструменты с узкими схемами и четкими метками о побочных эффектах. Хостам нужно знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят их.
MCP Client → Lambda OAuth Proxy → Bedrock AgentCore Gateway → AgentCore Runtime → Trino
Почему передача необработанного токена инструменту нарушает требования корпоративных систем
Чтобы понять, почему передача необработанного токена инструменту приводит к сбоям в проверках на уровне предприятия, необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывать скрытое состояние. Необходимо документировать как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не элементами последующей доработки. Инструменты с узкими схемами и четкими метками побочных эффектов должны быть открыты для использования. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят операцию.
Архитектура в одном взгляде
Для краткого обзора архитектуры необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы вместо обширных скриптов. При сбое шага он должен указывать на конкретную причину, а не на запутанную цепочку операций. Используйте инструменты с узкими схемами и четкими метками побочных эффектов. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят операцию. Для краткого обзора архитектуры необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения, а также стоимость токенов или запросов рядом с функциональными результатами. Отображение стоимости на раннем этапе предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам.
1. Клиент MCP
Для клиента MCP необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь кодовый граф. Аутентификация происходит на шлюзе, а повторная авторизация — на уровне обработки данных. Одного лишь токена не достаточно для определения границ аренды.
2. Прокси Lambda MCP (мозг системы OAuth)
Для пункта 2. Прокси Lambda MCP («мозг» OAuth) требует определения входных данных, ответственного за выполнение шага и критериев завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо задокументировать как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не последующими улучшениями. Аутентификация происходит на шлюзе, а повторная авторизация — на уровне обработки данных. Одного только токена-носителя недостаточно для определения границ аренды.
3. Шлюз AWS Bedrock AgentCore
Для пункта 3. AWS Bedrock AgentCore Gateway необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной структурой всего процесса. Аутентификация осуществляется на шлюзе, а повторная авторизация — на уровне обработки данных. Одного только токена не достаточно для определения границ аренды ресурсов. Для пункта 3. AWS Bedrock AgentCore Gateway необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения, стоимость токенов или запросов. Отслеживание затрат на раннем этапе предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам.
4. Среда выполнения AWS Bedrock AgentCore
Для среды выполнения AWS Bedrock AgentCore необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не пытаясь угадать скрытое состояние. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Для каждого вызова необходимо записывать название инструмента, хэш аргументов, время задержки и результат. Без такой информации отладка циклов агента занимает часы.
Полный процесс аутентификации
Для полного цикла аутентификации необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо одновременно задокументировать успешный сценарий работы и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки. Для каждого вызова следует записывать название инструмента, хеш аргументов, время задержки и результат. Без такой информации отладка агента занимает часы.
┌─────────────────────┐
│ MCP Client │
│ Claude / Claude Code│
└──────────┬──────────┘
│
│ OAuth 2.1 + PKCE
▼
┌─────────────────────┐
│ Lambda MCP Proxy │◄──────────────► Okta
│ OAuth / Identity │ Login + JWT
└──────────┬──────────┘
│
│ Bearer JWT
▼
┌─────────────────────────────┐
│ Bedrock AgentCore Gateway │
│ CUSTOM_JWT validation │
└────────────┬────────────────┘
│ SigV4
▼
┌─────────────────────────────┐
│ Bedrock AgentCore Runtime │
│ FastMCP Server │
│ │
│ list_catalogs │
│ describe_table │
│ execute_query │
└────────────┬────────────────┘
│
│ User JWT
▼
┌─────────────────────────────┐
│ Enterprise Query Engine │
│ Per-user authorization │
└─────────────────────────────┘
Подробный обзор 1: Обучение прокси Lambda говорить по стандарту OAuth 2.1
Для раздела «Углубленное изучение 1: Обучение прокси Lambda говорить на языке OAuth 2.1» определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной структурой обработки данных. Фиксируйте название инструмента, хеш аргументов, время задержки и результат каждого вызова. Без такой информации отладка циклов занимает часы. Для раздела «Углубленное изучение 1: Обучение прокси Lambda говорить на языке OAuth 2.1» определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения и стоимость токенов или запросов вместе с функциональными результатами. Отслеживание затрат на раннем этапе предотвращает неожиданные счета при выполнении процесса.
переходит от демо-среды к общедоступным средам.# GET /.well-known/protected-resource
{
"resource": f"{BASE_URL}/mcp",
"authorization_servers": [BASE_URL],
}
# GET /.well-known/oauth-authorization-server
{
"issuer": BASE_URL,
"authorization_endpoint": f"{BASE_URL}/authorize",
"token_endpoint": f"{BASE_URL}/token",
"registration_endpoint": f"{BASE_URL}/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
}
Передача PKCE без нарушений
Для передачи PKCE без нарушений необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь код. Необходимо предоставлять инструменты с узкими схемами и четкими метками побочных эффектов. Хостам нужно знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят операцию.
relay_payload = json.dumps({"u": original_redirect, "s": original_state}).encode()
relay_state = base64.urlsafe_b64encode(relay_payload).decode().rstrip("=")
params = {
"client_id": OKTA_CLIENT_ID,
"response_type": "code",
"redirect_uri": f"{BASE_URL}/callback", # <- proxy's callback
"state": relay_state, # <- client's state, wrapped
"code_challenge": qs["code_challenge"], # <- untouched
"code_challenge_method": "S256",
}
Динамическая регистрация клиентов
Для динамической регистрации клиентов необходимо заранее определить параметры ввода, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо одновременно задокументировать успешный сценарий работы и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки. Необходимо предоставлять инструменты с узкими схемами и четкими метками о побочных эффектах. Хостам нужно знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят операцию.
def handle_register(event):
req = json.loads(event.get("body") or "{}")
return _json_resp(201, {
"client_id": OKTA_CLIENT_ID,
"redirect_uris": req.get("redirect_uris", []),
"grant_types": req.get("grant_types", ["authorization_code"]),
"response_types": req.get("response_types", ["code"]),
"token_endpoint_auth_method": "none",
})
Углубленный анализ 2: Проблемы, возникающие при использовании FastMCP после AgentCore
Для углубленного анализа 2: были зафиксированы сбои, когда FastMCP находился за AgentCore; перед изменением кода необходимо определить входные данные, ответственного за шаг и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы вместо обширных скриптов. При сбое шага причина должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Используйте инструменты с узкими схемами и четкими метками побочных эффектов. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят их. Для углубленного анализа 2: были зафиксированы сбои, когда FastMCP находился за AgentCore; перед изменением кода необходимо определить входные данные, ответственного за шаг и критерии завершения. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения, а также стоимость токенов или запросов рядом с функциональными результатами. Очевидность затрат на раннем этапе предотвращает проблемы.
Появляются неожиданные счета, когда путь переходит от демо-среды к общим средам.
Острая проблема №1: в контейнер не поступают пользовательские заголовки HTTP
Чтобы решить проблему №1 — отсутствие передачи пользовательских заголовков HTTP в контейнер, необходимо заранее определить входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь граф. Авторизуйтесь у шлюза и повторно авторизуйтесь на уровне передачи данных. Один только токен-носитель не является границей аренды.
meta = payload.setdefault("params", {}).setdefault("_meta", {})
meta["okta_token"] = token # the validated user identity
meta["trino_target"] = routing_hint # optional routing metadata
class CredentialExtractMiddleware:
async def __call__(self, scope, receive, send):
# buffer the whole body
body = await _read_full_body(receive)
# pull identity out of params._meta
meta = json.loads(body).get("params", {}).get("_meta", {})
token = meta.get("okta_token") # AgentCore also injects the validated caller token as a header —
# prefer that when present, fall back to _meta / Authorization otherwise
token = token or _caller_token_from_headers(scope) _req_okta_token.set(token) # replay the buffered body so the MCP app can read it too
await self.app(scope, _replay(body, receive), send)
Острая проблема №2: AgentCore удаляет заголовок Accept
Для режима острого края №2: AgentCore удаляет заголовок Accept, определяет входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии.
Необходимо задокументировать как успешный, так и восстановительный пути выполнения. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки.
Аутентификация происходит на шлюзе, а повторная авторизация — на уровне данных. Один только токен-носитель не является границей аренды.
class InjectAcceptMiddleware:
async def __call__(self, scope, receive, send):
if scope["type"] == "http":
headers = [(k, v) for k, v in scope["headers"] if k != b"accept"]
headers.append((b"accept", b"application/json, text/event-stream"))
scope = {**scope, "headers": headers}
await self.app(scope, receive, send)
Углубленный анализ 3: Сопоставление токенов доступа Okta с изолированными сессиями Trino
Для раздела «Углубленный анализ 3: Сопоставление токенов доступа Okta с изолированными сессиями Trino» определите входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое шага причина должна быть связана с конкретной функцией, а не с запутанной структурой обработки данных. Аутентифицируйтесь у шлюза и повторно авторизуйтесь на уровне плоскости данных. Один только токен-носитель не является границей между тенантами. Для раздела «Углубленный анализ 3: Сопоставление токенов доступа Okta с изолированными сессиями Trino» определите входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения, стоимость токенов или запросов вместе с функциональными результатами. Отслеживание затрат на раннем этапе предотвращает неожиданные счета.
Это происходит, когда путь изменяется с демо-среды на общедоступные среды.
def get_connection():
token = _req_okta_token.get()
user = _jwt_email(token) # e.g. "alice@company.com"
return backend.connect(
host=BACKEND_HOST,
user=user, # identity for attribution/authorization
auth=JWTAuthentication(token), # the backend re-validates this itself
http_scheme="https",
)
Разрешение доступа и обеспечение режима только для чтения в конечном итоге находятся на уровне данных.
Чтобы определить параметры ввода, ответственного за выполнение шага и критерии завершения перед изменением кода, необходимо учитывать, что операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения; файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь граф составляющих. Необходимо записывать имя инструмента, хеш аргументов, время задержки и результат каждого вызова — без такой информации отладка агентов занимает часы.
forbidden = ("INSERT", "UPDATE", "DELETE", "DROP", "CREATE",
"ALTER", "TRUNCATE", "MERGE", "REPLACE", "GRANT", "REVOKE")
if any(query.strip().upper().startswith(kw) for kw in forbidden):
raise ValueError("Write operations are not allowed.")
Два способа подключения клиента
Для двух способов подключения клиента необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо одновременно задокументировать успешный сценарий выполнения и сценарий восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не элементами последующей доработки. Для каждого вызова следует записывать название инструмента, хеш аргументов, время задержки и результат. Без такой информации отладка циклов занимает часы.
{ "mcpServers": { "data": { "type": "http", "url": "https://<lambda-url>/mcp" } } }
{ "mcpServers": { "data": { "type": "http", "url": "http://localhost:8766/mcp" } } }
Чему мы научились
Согласно тому, чему мы научились, необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Лучше использовать небольшие, тестируемые единицы вместо обширных скриптов. Когда шаг терпит неудачу, причина должна указывать на конкретную ответственность, а не на запутанную цепочку операций. Фиксируйте название инструмента, хеш аргументов, время задержки и результат каждого вызова. Отладка без такой информации тратит много времени. Согласно тому, чему мы научились, необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Записывайте время выполнения и стоимость токенов или запросов вместе с функциональными результатами. Отображение стоимости заранее предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам.
Заключение
В заключение определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, которое операторы могут проверять, не читая весь граф процессов. Обеспечьте доступ к инструментам с узкими схемами и четкими метками побочных эффектов. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят их.
Чек-лист операционной деятельности
Для чек-листа операционной деятельности определите входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии.
Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия создаваемым объектам, определите критерии успешности и откажитесь от молчаливого частичного выполнения задачи.
Аутентифицируйтесь у шлюза и повторно авторизуйтесь на уровне плоскости данных. Один только токен-носитель не является границей тенантности.
Создавайте контрольные точки после дорогостоящих операций. Механизм возобновления работы не должен повторно брать плату за один и тот же вызов большой языковой модели при повторной попытке обработки последующего узла.
Передавайте идентификаторы от клиента к хранилищу нижнего уровня, не превращая всех пользователей в счета службы.
При наличии бюджета добавляйте тесты на работоспособность, которые проверяют критически важные этапы в процессе интеграционного тестирования с использованием фикстур, а не реальных платных API.
Перед запуском в массовое использование необходимо заморозить версии, сделать запись критического пути выполнения и уточнить шаги возврата к предыдущей версии. В совместных средах требуются ограничения на количество запросов, проверка принадлежности пользователя и четкий ответственный за обновление секретов. Лучше выбирать надежность, чем креативные одноразовые демонстрации.
Примечание к развертыванию 1: фиксируйте версии изображений, устанавливайте лимиты на количество запросов и проверяйте изоляцию пользователей на тестовой группе перед более широким внедрением.
Примечание к развертыванию 2: фиксируйте версии изображений, устанавливайте лимиты на количество запросов и проверяйте изоляцию пользователей на тестовой группе перед более широким внедрением.
Примечание к развертыванию 3: фиксируйте версии изображений, устанавливайте лимиты на количество запросов и проверяйте изоляцию пользователей на тестовой группе перед более широким внедрением.
Примечание к развертыванию 4: фиксируйте версии изображений, устанавливайте лимиты на количество запросов и проверяйте изоляцию пользователей на тестовой группе перед более широким внедрением.
Примечание к развертыванию 5: фиксируйте версии изображений, устанавливайте лимиты на количество запросов и проверяйте изоляцию пользователей на тестовой группе перед более широким внедрением.
Примечание к развертыванию 6: фиксируйте изображения, устанавливайте лимиты запросов и проверяйте изоляцию тенантов на канарейке перед более широким внедрением.