1. מבוא
ב-Codelab הזה נסביר איך להשתמש ב-Agent Gateway ב-agent-to-anywhere (יציאה) כדי ליצור קישוריות יציאה עם כתובת IP סטטית ל-Gemini Enterprise. תגדירו אפליקציית Gemini Enterprise שתפעיל שרת Model Context Protocol (MCP) בהתאמה אישית, שמוגן על ידי רשימת היתרים של כתובות IP קפדנית ואימות OAuth 2.0, על ידי ניתוב כל התנועה היוצאת דרך Agent Gateway באמצעות הגדרת תבנית קישוריות. מחבר רשת של Private Service Connect (PSC) מחבר את Agent Gateway לרשת ה-VPC, ו-Cloud NAT מספק כתובת IP חיצונית יציבה לתעבורת נתונים יוצאת (egress) באינטרנט.
כברירת מחדל, חיבורים יוצאים מ-Gemini Enterprise לנקודות קצה באינטרנט הציבורי מגיעים ממאגרים דינמיים משותפים של כתובות IP של Google, שלא ניתן להצמיד אותם לכלל של רשימת כתובות IP להיתר בבעלות לקוח יחיד. שילוב של Agent Gateway עם AgentConnectivityTemplate במצב ALL_TRAFFIC מנתב את כל התנועה היוצאת של הסוכן לרשת ה-VPC, שבה Cloud NAT מתרגם חיבורים שמיועדים לאינטרנט לכתובת IPv4 חיצונית סטטית שמורה שנמצאת בשליטתכם, תוך שמירה על ניהול מרכזי של כלי MCP באמצעות Agent Registry ו-Identity-Aware Proxy (IAP).
מה יוצרים
- תבנית קישוריות של סוכן (
vpcEgress: ALL_TRAFFIC) ו-Agent Gateway שפועל במצב יציאה (agent-to-anywhere) עם אימות של נקודת הקצה של Agent Registry - רשת VPC, מחבר רשת PSC, Cloud Router ושער Cloud NAT שקשורים לכתובת IPv4 חיצונית סטטית שמורה
- שרת משאבים חיצוני מסוג HTTP MCP שאפשר להזרים ממנו (
mcp-sprockets) עם Caddy TLS בחזית, שמוגבל על ידי כללי חומת אש רק לכתובת ה-IP הסטטית של Cloud NAT, ומאמת אסימוני Bearer של Google OAuth 2.0 שהוקצו למשתמשים מולhttps://oauth2.googleapis.com/tokeninfo - מסך ההסכמה של OAuth 2.0 בפלטפורמת האימות של Google ופרטי הכניסה של לקוח OAuth 2.0 באפליקציית אינטרנט (
accounts.google.comו-oauth2.googleapis.com/token) - ערך בקטלוג שרתי ה-MCP של Agent Registry ומפרט הכלי (
toolspec.json) - תוסף שמפנה לבדיקת הרשאות של שרת proxy לאימות זהויות (IAP) ומדיניות ההרשאות של שער עבור Agent Gateway
- אפליקציית Gemini Enterprise שמשויכת ל-Agent Gateway ומחוברת למאגר נתונים של שרת MCP מותאם אישית ב-OAuth 2.0 שיובא מ-Agent Registry
איור 1. ארכיטקטורת Codelab
מה תלמדו
- איך מגדירים
AgentConnectivityTemplateעםvpcEgress: ALL_TRAFFICכדי לנתב את כל תעבורת הנתונים היוצאת של Agent Gateway לרשת VPC - איך שומרים כתובת IPv4 חיצונית סטטית ומקשרים אותה ל-Cloud NAT ברשת משנה של קובץ מצורף לרשת PSC
- איך פורסים שרת משאבים חיצוני מסוג HTTP MCP שניתן להזרמה, שמאמת אסימוני Bearer מסוג Google OAuth 2.0 (
oauth2.googleapis.com/tokeninfo) ומגביל את חומת האש שלו בכניסה לכתובת ה-IP הסטטית של Cloud NAT (${NAT_EXT_IP}/32) - איך מגדירים פרטי לקוח של Google Identity OAuth 2.0 ורושמים שרת MCP וסכימת הכלים שלו ב-Agent Registry
- איך מחברים שרת MCP רשום של OAuth 2.0 ל-Gemini Enterprise ומנתבים קריאות לכלים שהמשתמשים הקצו דרך Agent Gateway
- איך מאמתים תרגום של כתובות IP סטטיות, אימות זהות של משתמשי קצה ב-OAuth 2.0 (
authenticated_user) ואכיפה של מדיניות שער באמצעות פקודות CLI ו-Cloud Logging
מה צריך
- פרויקט ב-Google Cloud שהחיוב בו מופעל
- רישיון פעיל ל-Gemini Enterprise או תקופת ניסיון של 30 יום
- הרשאות IAM להקצאת שירותי רשת, מדיניות ארגונית, Gemini Enterprise ומשאבי Agent Platform
- מעטפת תואמת POSIX (
bashאוzsh) עם Google Cloud CLI (gcloud),curlו-jqמותקנים
סיימנו את חלק המבוא. עכשיו נעבור לקטע מושגים.
2. מושגים
סדר הפריסה
בשיעור Codelab הזה נסביר איך לפרוס קודם את תשתית הרשת והניהול, כדי שיהיו נתיבי יציאה סטטיים של כתובות IP ופקדי שער פעילים לפני שרושמים ומקשרים כלי MCP ל-Gemini Enterprise:
- תשתית הרשת: שמירת כתובות IPv4 חיצוניות סטטיות והקצאת VPC ליציאה, רשת משנה לצירוף PSC, צירוף רשת PSC, Cloud Router, שער Cloud NAT וכללי חומת אש חיצונית של רשת MCP.
- תבנית קישוריות לסוכן ו-Agent Gateway: פריסה של
AgentConnectivityTemplate(vpcEgress: ALL_TRAFFIC) אזוריAgentGatewayבמצב יציאה (AGENT_TO_ANYWHERE) עם שילוב של Agent Registry (registries). - מדיניות הרשאות: הגדרת תוסף ההרשאות של IAP (
AuthzExtension), מדיניות ההרשאות של שער (AuthzPolicy) ומדיניות גישה מאוחדת ב-IAM v3 (AccessPolicyו-PolicyBinding). - פריסה ורישום של שרת MCP: פורסים את שרת המשאבים החיצוני של
mcp-sprocketsOAuth 2.0 מאחורי Caddy TLS, מאמתים את האכיפה של Google OAuth 2.0tokeninfo, נועלים את חומת האש של הכניסה באופן קפדני לכתובת ה-IP הסטטית של Cloud NAT (${NAT_EXT_IP}/32) ורושמים את המפרטים של השירות והכלי (get_sprocketsו-get_sprocket_by_id) ב-Agent Registry. - אפליקציית Gemini Enterprise ולקוח OAuth 2.0: מגדירים את ספק הזהויות של Google, את רישיון הניסיון, את מסך ההסכמה של OAuth 2.0 ואת פרטי הכניסה של לקוח אפליקציית האינטרנט (
OAUTH_CLIENT_IDו-OAUTH_CLIENT_SECRET), יוצרים את אפליקציית Gemini Enterprise (Engine) ומקשרים את התעבורה היוצאת אל Agent Gateway (agentGatewaySetting). - ייבוא מחבר נתונים מותאם אישית של MCP: יוצרים ומפעילים את
REGISTRY_MCPמחבר הנתונים (:setUpDataConnector) שהוגדר עםauth_type: "OAUTH"כדי לקשר את שרת ה-MCP הרשום שמשמש כמאגר נתונים לאפליקציית Gemini Enterprise. - אימות: משלימים את תהליך ההסכמה החד-פעמי של משתמש OAuth 2.0, מפעילים כלי MCP דרך העוזר הדיגיטלי של Gemini Enterprise (מאשרים גם את
egress_source_ipוגם אתauthenticated_user), ומאמתים את אכיפת המדיניות ואת תרגום ה-NAT של כתובת ה-IP הסטטית ב-Cloud Logging.
ארכיטקטורה של שרת משאבים של MCP שמוגן באמצעות OAuth + ספק זהויות מרכזי
בפריסות ארגוניות, ניהול הזהויות והפעלת הכלי מתבצעים בהתאם להפרדת התפקידים הסטנדרטית של OAuth 2.0 בין ספק זהויות מרכזי (שרת הרשאות) לבין שרת משאבים מוגן של MCP:
תפקיד OAuth 2.0 | רכיב ב-Codelab הזה | אחריות |
בעלי המשאב | משתמש קצה ( | המשתמש נכנס לאפליקציית האינטרנט של Gemini Enterprise ומעניק הסכמה במהלך תהליך ההרשאה של OAuth 2.0 עם 3 רגליים, שמתבצע פעם אחת. |
לקוח OAuth 2.0 | Gemini Enterprise | מחזיק ב- |
שרת ההרשאות | Google Identity OAuth 2.0 ( | מאמת את המשתמש במהלך ההפניה האוטומטית להסכמה בדפדפן ומנפיק ל-Gemini Enterprise את אסימוני הגישה והרענון של OAuth 2.0. |
שרת משאבים | חיצוני | השירות אוכף זהות רשת בשכבה 3 באמצעות חומת האש של ה-VPC שלו ( |
מכיוון שההפניה האוטומטית של הסכמה בדפדפן (accounts.google.com) והחלפת האסימונים בערוץ האחורי (oauth2.googleapis.com/token) מתרחשות מול נקודות קצה של OAuth 2.0 שמנוהלות על ידי Google, חומת האש של שרת ה-MCP החיצוני מקבלת רק בקשות JSON-RPC של MCP בזמן ריצה (POST /mcp) מ-Agent Gateway דרך Cloud NAT, ולכן אפשר לנעול את חומת האש של הכניסה באופן מוחלט ל-${NAT_EXT_IP}/32.
מודל המשאבים של Discovery Engine
אפליקציית Gemini Enterprise (שמיוצגת כמשאב Engine ב-Discovery Engine API) היא שכבת התיאום המרכזית וממשק השיחה למשתמשי הקצה. הוא מנהל סשנים של צ'אטים עם משתמשים, מבסס מודלים גנרטיביים על נתונים ארגוניים ומתאם בין הפעלות דינמיות של כלים.
אפליקציות Gemini Enterprise מקיימות אינטראקציה עם נתונים ומערכות באמצעות מאגרי נתונים:
- מאגרי נתונים של ידע: הטמעה ואינדוקס של תוכן סטטי (למשל, Cloud Storage, Google Drive, BigQuery) לצורך יצירה משופרת באחזור (RAG).
- מחברי נתונים (ספקי פעולות): מתחברים לממשקי API דינמיים של צד שלישי או לממשקי API בהתאמה אישית. מאגר נתונים של שרת MCP מותאם אישית חושף כלים שמוגדרים על ידי Model Context Protocol (MCP), ומאפשר למודל להפעיל באופן דינמי פונקציות חיצוניות במהלך שיחה.
יציאת נתונים מ-Gemini Enterprise
Gemini Enterprise מעביר בקשות לכלים של שרת MCP בהתאמה אישית אל Agent Gateway כשגם agentGatewaySetting ב-Engine וגם use_agent_gateway_egress: true ב-DataConnector מוגדרים.
איור 2. ארכיטקטורת יציאה של Gemini Enterprise
אפליקציית Gemini Enterprise מארגנת את ניתוב הכלים בארבעה תחומים מרכזיים:
- ווידג'ט (
default_search_widget_config):- מגיש את ממשק לקוח האינטרנט. הווידג'ט מקבל הנחיות מהמשתמש, מפעיל את ההפניה האוטומטית להסכמת משתמש חד-פעמית של OAuth 2.0 כשכלי מסוים דורש הרשאה, ומתחיל סשנים של צ'אט עם המנוע הבסיסי.
- העוזר הראשי (
assistants/default_assistant/agents/default/core_assistant):- סוכן ה-AI בממשק שיחה ברמת הבסיס במנוע. כשמעריכים שאילתת משתמש, עוזר הליבה בודק את הכלים הזמינים ומקצה את ההרצה לסוכן המשנה של Agent Gateway הסינתטי.
- מאגר נתונים ומחבר נתונים:
-
DataStore: מוקצה בתוךCollectionייעודי כשמריצים את:setUpDataConnector, הוא מקשר (dataStoreIds) את הסכימות של כלי Agent Registry המיובא (get_sprockets,get_sprocket_by_id), את סוגי הארגומנטים ואת ההוראות לסוכן אל Gemini EnterpriseEngine. -
DataConnector: מנהל את חיבור הפעולהREGISTRY_MCP(createBapConnection: true) לשרת ה-MCP המרוחק (instance_uri), פותר את משאב שרת ה-MCP של Agent Registry (registry_mcp_server_name), מצרף את אסימון ה-Bearer של OAuth 2.0 שנשמר בכספת של המשתמש (Authorization: Bearer [access_token]) ומאפשר יציאה של Agent Gateway (use_agent_gateway_egress: true).
-
- Agent Identity, Agent Registry ו-Agent Gateway:
- כשמחבר הנתונים שולח את הקריאה היוצאת לכלי, הוא מנתב את התנועה לשער שצוין ב-
agentGatewaySetting. הכלי Core Assistant יוצר אסימון זהות SPIFFE שמאשר את הזהות של עומס העבודה:principal://agents.global.org-.../agents/default/core_assistant. - Agent Gateway משתלב עם Agent Registry באמצעות השדה
registriesכדי לפתור באופן דינמי נקודות קצה של יעד וסכימות של כלים רשומים, מעריך מדיניות הרשאות באמצעות IAP ומעביר בקשות מורשות (תוך שמירה על כותרת המשתמשAuthorization: Bearer) לרשת ה-VPC באמצעותAgentConnectivityTemplate.
- כשמחבר הנתונים שולח את הקריאה היוצאת לכלי, הוא מנתב את התנועה לשער שצוין ב-
קישוריות של שער VPC ו-NAT של כתובות IP סטטיות
Agent Gateway תומך בקישוריות לרשת VPC באמצעות משאב AgentConnectivityTemplate שאליו מתבצעת הפניה על ידי agentConnectivityTemplate בשער. התבנית egressNetworkConfig מגדירה איך תעבורת נתונים נכנסת ל-VPC דרך מחבר רשת של PSC:
-
PRIVATE_RANGES_ONLY(ברירת מחדל): ניתוב רק של טווחי כתובות IP פרטיות מסוג RFC 1918, RFC 6598 ו-Class E דרך חיבור הרשת של PSC. תעבורת נתונים שמיועדת לאינטרנט עוקפת את ה-VPC ויוצאת ישירות דרך יציאת ברירת המחדל של Google לאינטרנט. -
ALL_TRAFFIC(משמש ב-Codelab הזה): מעביר את כל התעבורה היוצאת מ-Agent Gateway – כולל האינטרנט הציבורי ונקודות הקצה של שותפי MCP חיצוניים – לרשת המשנה של ה-VPC דרך מחבר הרשת של ה-PSC (10.10.10.2).
כשפועלים במצב ALL_TRAFFIC, נדרשות שתי הגדרות עבור רשת המשנה של קובץ ה-PSC:
- גישה פרטית ל-Google (
--enable-private-ip-google-access): מאפשרת להמשיך להפנות קריאות פנימיות לממשקי Google Cloud API (*.googleapis.com) באופן פרטי בתוך רשת Google. - Cloud Router ו-Cloud NAT (
MANUAL_ONLY): תנועה שיוצאת לאינטרנט ונכנסת לתת-הרשת של קובץ ה-PSC מצורף, עוברת במסלול ברירת המחדל (0.0.0.0/0) דרך Cloud NAT, שמבצע תרגום כתובת רשת של המקור (SNAT) לכתובת ה-IPv4 החיצונית הסטטית השמורה (${NAT_EXT_IP}). לכן, חומות אש חיצוניות יכולות להגביל את הגישה הנכנסת ל-${NAT_EXT_IP}/32בלבד.
הגבלות ודרישות
- StreamableHTTP בלבד: אין תמיכה בשידור הנתונים הקודם של אירועים שנשלחים מהשרת (SSE). בשרתי MCP צריך להשתמש ב-StreamableHTTP.
- ביטול מדיניות הארגון: לפני שמבצעים רישום של מחבר הנתונים, צריך לבטל באופן מפורש את מדיניות הארגון עבור מאגרי נתונים מותאמים אישית של MCP.
- התאמה אזורית: Gemini Enterprise אוכף התאמה אזורית בין מיקום האפליקציה, Agent Gateway ו-Agent Registry:
המיקום של אפליקציית Gemini Enterprise | מיקום של Agent Gateway ו-NAT | מיקום Agent Registry |
|
|
|
|
|
|
|
|
|
סיימנו את החלק של ההסברים… עכשיו נעבור לקטע הגדרה.
3. הגדרה
התפקידים שצריך ב-IAM
כדי להשלים את ה-Codelab הזה, צריך את התפקידים הבאים:
דומיין | התפקידים שצריך ב-IAM |
פרויקט ו-IAM |
|
Networking & Gateway |
|
Gemini Enterprise והמרשם |
|
עומסי עבודה (workloads) |
|
ניראות (observability) |
|
אפשר גם להשתמש בתפקיד בסיסי רחב כמו roles/owner בשילוב עם roles/orgpolicy.policyAdmin (כי roles/owner לבד לא יכול לשנות את מדיניות הארגון).
גישה לפרויקט
ב-Codelab הזה משתמשים בפרויקט אחד ב-Google Cloud. בשלבי ההגדרה נעשה שימוש בפקודות של gcloud CLI ושל מעטפת Linux.
כדי להתחיל, ניגשים לשורת הפקודה של הפרויקט בענן ב-Google Cloud:
- Cloud Shell בכתובת
shell.cloud.google.com, או - טרמינל מקומי עם
gcloudCLI מותקן
הגדרת מזהה הפרויקט
# set target project id
gcloud config set project SET_YOUR_PROJECT_ID_HERE
אימות סשן
# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login
הגדרת משתני סביבה של מעטפת
# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
export ZONE="us-central1-a"
echo ${SLUG}
echo ${REGION}
echo ${ZONE}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
export USER_IDENTITY=$(gcloud config get-value account)
echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
echo ${USER_IDENTITY}
# create resource vars for agent platform (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-ata"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export MCP_NAME="mcp-sprockets"
echo ${AGW_NAME}
echo ${AGW_URI}
echo ${MCP_NAME}
# create resource vars for gemini enterprise (automatic)
export GE_LOCATION="global"
export GE_APP_ORG_NAME="${SLUG}, Inc."
export GE_APP_DISPLAY_NAME="${SLUG} ${GE_LOCATION} app"
export GE_APP_NAME="app-${SLUG}-${GE_LOCATION}"
export GE_APP_INIT="${GE_APP_NAME}_$(date +%s)"
echo ${GE_LOCATION}
echo ${GE_APP_ORG_NAME}
echo ${GE_APP_DISPLAY_NAME}
echo ${GE_APP_NAME}
echo ${GE_APP_INIT}
הגדרת דומיינים מהימנים של זהות הסוכן
ההצהרה if-then-else בודקת אם הפרויקט שייך לארגון כדי להגדיר את מתחם האמון (trust domain) הנכון עבור זהויות הישות המורשית של הסוכן הראשי.
# set var for trust domain
if [[ -n "${ORG_ID}" ]]; then
export TRUST_DOMAIN="agents.global.org-${ORG_ID}.system.id.goog"
else
export TRUST_DOMAIN="agents.global.proj-${PROJ_NO}.system.id.goog"
fi
echo "trust domain: ${TRUST_DOMAIN}"
הגדרת פרויקט לחיוב ולמכסה
# set cli quota project
gcloud config set billing/quota_project ${PROJ_ID}
# set api quota project
gcloud auth application-default set-quota-project ${PROJ_ID}
יצירת ספרייה מקומית לקובצי תצורה
# create config folder
mkdir -p cfg
עדכון של gcloud cli (מומלץ)
אם אתם מריצים התקנה בניהול עצמי של Google Cloud SDK (כלומר, מחוץ ל-Cloud Shell), אתם צריכים לעדכן את הרכיבים לגרסה האחרונה.
# update gcloud cli
gcloud components update
הפעלת שירותי API
# enable google cloud apis
gcloud services enable \
agentregistry.googleapis.com \
aiplatform.googleapis.com \
cloudapiregistry.googleapis.com \
cloudresourcemanager.googleapis.com \
compute.googleapis.com \
discoveryengine.googleapis.com \
dns.googleapis.com \
iam.googleapis.com \
iap.googleapis.com \
logging.googleapis.com \
monitoring.googleapis.com \
networkconnectivity.googleapis.com \
networksecurity.googleapis.com \
networkservices.googleapis.com \
orgpolicy.googleapis.com \
serviceextensions.googleapis.com \
serviceusage.googleapis.com
כללי מדיניות של הארגון
אילוצים מנוהלים על מדיניות הארגון ומדיניות ארגז החול ב-Google Cloud יכולים להגביל את התכונות שבהן נעשה שימוש ב-Codelab הזה:
-
compute.vmExternalIpAccess: מגביל כתובות IPv4 חיצוניות במכונות וירטואליות ב-Compute Engine בארגז חול נעול. -
discoveryengine.managed.disableCustomMcpServerConnector: מגביל את היצירה של מחברי נתונים שמשתמשים בשרת MCP מותאם אישית (custom_mcp) כמקור נתונים (ההגדרה הזו נאכפת כברירת מחדל). -
discoveryengine.managed.allowedEgressFqdns: מגביל דומיינים יוצאים (instance_uriFQDN) של מחברי נתונים כש-VPC Service Controls (VPC-SC) פעיל או כשהפרויקט מופיע ב-enforcedProjects. -
discoveryengine.managed.allowedDataSources: מגביל את סוגי מחברי הנתונים המותרים (dataSource) כש-VPC-SC פעיל או כשהפרויקט מופיע ב-enforcedProjects.
ביטול של מגבלות שקשורות למדיניות הארגון שעברו בירושה ברמת הפרויקט.
השבתת אילוץ MCP בהתאמה אישית
# disable data connector constraint (allow custom mcp servers)
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/discoveryengine.managed.disableCustomMcpServerConnector
spec:
rules:
- enforce: false
EOF
השבתת אילוצים מותנים של מחבר נתונים
# disable egress fqdn constraint on project
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/discoveryengine.managed.allowedEgressFqdns
spec:
rules:
- enforce: false
EOF
# disable allowed data sources constraint on project
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/discoveryengine.managed.allowedDataSources
spec:
rules:
- enforce: false
EOF
השבתת אילוץ של מדיניות גישה
# disable iam v3 constraint (allow v3 access policies)
gcloud org-policies set-policy /dev/stdin << EOF
name: projects/${PROJ_NO}/policies/iam.managed.disableAccessPolicyBinding
spec:
rules:
- enforce: false
EOF
איך מאפשרים כתובות IP חיצוניות
# allow external ip address for vms on project
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/compute.vmExternalIpAccess
spec:
rules:
- allowAll: true
EOF
אימות מדיניות הארגון
בודקים שהערך של compute.vmExternalIpAccess הוא allowAll: True וששאר המדיניות היא enforce: False:
# verify effective org policy constraints on project
for policy in \
discoveryengine.managed.disableCustomMcpServerConnector \
discoveryengine.managed.allowedEgressFqdns \
discoveryengine.managed.allowedDataSources \
iam.managed.disableAccessPolicyBinding \
compute.vmExternalIpAccess; do
gcloud org-policies describe ${policy} \
--project=${PROJ_ID} \
--effective \
--format="flattened[no-pad](name.basename(),spec.rules[0])"
done
אימות של הקישורים של סוכני השירות (אמצעי זהירות)
בפרויקט חדש, מערכת Google Cloud מקצה באופן אוטומטי את סוכן השירות של Agent Gateway ומעניקה לו את ההרשאה roles/agentgateway.serviceAgent כשמפעילים לראשונה את networkservices.googleapis.com. אם אתם משתמשים מחדש בפרויקט קיים, יכול להיות שפעולות ניקוי קודמות הסירו את הקישורים של סוכן השירות שמוגדר כברירת מחדל. כדי לוודא שהזהות והקישור לתפקיד תקינים, מריצים את הפקודות הבאות:
# ensure network services service account has been created
gcloud beta services identity create \
--service=networkservices.googleapis.com \
--project="${PROJ_ID}"
# ensure network services service account has service agent roles applied
gcloud projects add-iam-policy-binding "${PROJ_ID}" \
--member="serviceAccount:service-${PROJ_NO}@gcp-sa-agentgateway.iam.gserviceaccount.com" \
--role="roles/agentgateway.serviceAgent"
כאן מסתיים חלק ההגדרה... ממשיכים לקטע רשת.
4. רשת
בקטע הזה, שומרים את כתובות ה-IPv4 החיצוניות הסטטיות, יוצרים את רשת ה-VPC הפנימית ליציאת נתונים ואת רשת ה-VPC החיצונית המבודדת, מקצים את הצירוף לרשת PSC, מגדירים את Cloud NAT ויוצרים כללי חומת אש.
שמירת כתובות IP חיצוניות סטטיות
שומרים שתי כתובות IPv4 חיצוניות סטטיות אזוריות ל-Cloud NAT ולמכונה הווירטואלית של שרת ה-MCP החיצוני:
# reserve static external ipv4 address for cloud nat
gcloud compute addresses create ip-${SLUG}-nat \
--region=${REGION}
# reserve static external ipv4 address for the mcp server
gcloud compute addresses create ip-${SLUG}-mcp \
--region=${REGION}
אימות כתובות IP חיצוניות סטטיות
כדי להשתמש ב-Gemini Enterprise, נקודות הקצה של MCP מרוחקות צריכות להשתמש ב-HTTPS עם אישור TLS מהימן באופן ציבורי. במקום לרשום דומיין בהתאמה אישית למעבדה הזו, משתמשים ב-sslip.io, שירות DNS חינמי עם תו כללי לחיפוש, שמבצע התאמת נתונים (resolve) של כל שם מארח שמכיל כתובת IP (כמו mcp.[IP].sslip.io) ישירות לכתובת ה-IP הזו. מכיוון שרשויות אישורים כמו Let's Encrypt לא מנפיקות אישורים לכתובות IP חשופות, שם המארח sslip.io מספק ל-Caddy proxy הפוך שם דומיין תקין כדי להקצות באופן אוטומטי אישור TLS מהימן לשרת MCP.
מייצאים את כתובות ה-IPv4 ויוצרים את sslip.io שם הדומיין וכתובת ה-URL של שרת ה-MCP החיצוני:
# export static ip addresses and set vars for domain
export NAT_EXT_IP=$(gcloud compute addresses describe ip-${SLUG}-nat \
--region=${REGION} \
--format="value(address)")
export MCP_EXT_IP=$(gcloud compute addresses describe ip-${SLUG}-mcp \
--region=${REGION} \
--format="value(address)")
export MCP_DOMAIN="mcp.${MCP_EXT_IP}.sslip.io"
export MCP_URL="https://${MCP_DOMAIN}/mcp"
echo "cloud nat external ip : ${NAT_EXT_IP}"
echo "mcp server external ip : ${MCP_EXT_IP}"
echo "mcp url : ${MCP_URL}"
פלט לדוגמה:
cloud nat external ip : 203.0.113.10
mcp server external ip : 203.0.113.20
mcp url : https://mcp.203.0.113.20.sslip.io/mcp
יצירת רשת פנימית
כשמגדירים את תבנית הקישוריות של Agent Gateway לשימוש ב-egressNetworkConfig עם networkAttachment, רשת המשנה שמארחת את מחבר הרשת של PSC צריכה להיות לפחות בלוק CIDR של /28, וצריך להפעיל בה גישה פרטית ל-Google (--enable-private-ip-google-access).
יוצרים רשת VPC גלובלית לתעבורת נתונים יוצאת (egress) של Agent Gateway פנימי:
# create vpc network
gcloud compute networks create vnet-${SLUG}-internal --subnet-mode=custom
# create subnet for agent gateway psc na
gcloud compute networks subnets create subnet-${REGION}-agw \
--network=vnet-${SLUG}-internal \
--range=10.10.10.0/28 \
--region=${REGION} \
--enable-private-ip-google-access
יצירת כללים פנימיים לחומת האש
יוצרים מדיניות חומת אש שמאפשרת את כל תעבורת הנתונים היוצאת (egress) עם הפעלת הרישום ביומן. המידע הזה ישמש למעקב אחרי תנועת הנתונים שיוצאת מ-Agent Gateway לרשת ה-VPC הפנימית. Cloud NGFW תומך ברמות Essentials ו-Standard לאבטחת רשת ולניטור תעבורה.
# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG}-internal --global
# create fw policy rule
gcloud compute network-firewall-policies rules create 1001 \
--description="allow all out and log" \
--firewall-policy=fw-policy-${SLUG}-internal \
--global-firewall-policy \
--action=allow \
--direction=EGRESS \
--layer4-configs=all \
--dest-ip-ranges=0.0.0.0/0 \
--enable-logging
# bind fw policy to network
gcloud compute network-firewall-policies associations create \
--name=fw-policy-bind-${SLUG}-internal \
--firewall-policy=fw-policy-${SLUG}-internal \
--network=vnet-${SLUG}-internal \
--global-firewall-policy
יצירת קובץ מצורף לרשת PSC
יוצרים קובץ מצורף לרשת Private Service Connect (PSC) שמוגדר לקבל באופן אוטומטי חיבורים מ-Agent Gateway. מחבר הרשת יוצר את הצד של רשת ה-VPC של הצרכן בחיבור, כדי לקשר בצורה מאובטחת לצד של הספק של Agent Gateway לתעבורת נתונים יוצאת. מידע נוסף על דרישות של רשתות משנה ומפרטים של טווחי כתובות IP זמין במאמר הגדרת קישוריות VPC.
# create psc network attachment
gcloud compute network-attachments create psc-na-${REGION}-agw \
--region=${REGION} \
--subnets=subnet-${REGION}-agw \
--connection-preference=ACCEPT_AUTOMATIC
אימות של צירוף לרשת של נציבות שירות המדינה
# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw --region=${REGION}
מאחזרים את ה-URI של משאב ה-PSC של מחבר הרשת ומאחסנים אותו במשתנה הסביבה PSC_NA_URI. כתובת ה-URI הזו תהיה מוזכרת בהגדרת Agent Gateway (networkConfig.egress.networkAttachment) כדי להקצות את ממשק ה-PSC לתעבורת נתונים יוצאת (egress) לרשת ה-VPC:
# fetch psc network attachment uri
export PSC_NA_URI=$(gcloud compute network-attachments describe psc-na-${REGION}-agw \
--region=${REGION} \
--format="value(selfLink.scope(v1))")
echo ${PSC_NA_URI}
יצירת Cloud NAT
Cloud NAT משמש לתרגום כתובת ה-IP של המקור של תעבורת הנתונים היוצאת של Agent Gateway לכתובת ה-IP הציבורית השמורה לצורך ניתוב באינטרנט הציבורי.
רק מקורות מרשת המשנה של מחבר רשת תעבורת נתונים יוצאת (egress) של Agent Gateway מוגדרים ככאלה שעומדים בדרישות לשימוש ב-Cloud NAT.
# create router for nat
gcloud compute routers create cr-nat-${SLUG}-${REGION} \
--network=vnet-${SLUG}-internal \
--asn=16550 \
--region=${REGION}
# create nat
gcloud compute routers nats create nat-${SLUG}-${REGION} \
--router=cr-nat-${SLUG}-${REGION} \
--region=${REGION} \
--nat-custom-subnet-ip-ranges="subnet-${REGION}-agw" \
--nat-external-ip-pool="ip-${SLUG}-nat" \
--enable-logging \
--log-filter=ALL
אימות של Cloud NAT
בודקים את הסטטוס של Cloud NAT ומוודאים שהמקור natIP הצפוי מוגדר:
# verify cloud nat configuration
gcloud compute routers nats describe nat-${SLUG}-${REGION} \
--router=cr-nat-${SLUG}-${REGION} \
--region=${REGION} \
--format="yaml(name,natIps,subnetworks.name)"
פלט לדוגמה:
name: nat-foo-us-central1
natIps:
- https://www.googleapis.com/compute/v1/projects/[PROJ_ID]/regions/us-central1/addresses/ip-foo-nat
יצירת רשת חיצונית
יוצרים רשת VPC נפרדת ומבודדת לאירוח המכונה הווירטואלית של שרת ה-MCP החיצוני.
# create vpc network
gcloud compute networks create vnet-${SLUG}-external --subnet-mode=custom
# create subnet for mcp server
gcloud compute networks subnets create subnet-${REGION}-mcp \
--network=vnet-${SLUG}-external \
--range=240.240.240.0/24 \
--region=${REGION} \
--enable-private-ip-google-access
יצירת כללים חיצוניים לחומת האש
יוצרים שני כללי חומת אש לתעבורת נתונים נכנסת ברשת ה-VPC החיצונית:
-
2001: מאפשר יציאת TCP נכנסת80כדי ש-Let's Encrypt יוכל להשלים את אתגר אימות הדומיין ACME HTTP-01 עבור${MCP_DOMAIN}. -
2002: מאפשרת בהתחלה יציאת TCP נכנסת443מ-0.0.0.0/0כדי לאמת את המוכנות ל-TLS ואת האכיפה של OAuth 2.0tokeninfoממעטפת מקומית לפני הנעילה שלה באופן מוחלט ל-${NAT_EXT_IP}/32.
# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG}-external --global
# create fw policy rules
gcloud compute network-firewall-policies rules create 2001 \
--description="allow in any http" \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy \
--action=allow \
--direction=INGRESS \
--layer4-configs=tcp:80 \
--src-ip-ranges=0.0.0.0/0 \
--dest-ip-ranges=240.240.240.0/24 \
--enable-logging
gcloud compute network-firewall-policies rules create 2002 \
--description="allow in any https" \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy \
--action=allow \
--direction=INGRESS \
--layer4-configs=tcp:443 \
--src-ip-ranges=0.0.0.0/0 \
--dest-ip-ranges=240.240.240.0/24 \
--enable-logging
# bind fw policy to network
gcloud compute network-firewall-policies associations create \
--name=fw-policy-bind-${SLUG}-external \
--firewall-policy=fw-policy-${SLUG}-external \
--network=vnet-${SLUG}-external \
--global-firewall-policy
עד כאן בנושא הרשת... עכשיו נעבור לקטע Agent Gateway.
5. Agent Gateway
בקטע הזה נפרס את תבנית הקישוריות של הסוכן באמצעות vpcEgress: ALL_TRAFFIC, נפרס את Agent Gateway האזורי, נאציל הרשאה ל-Identity-Aware Proxy (IAP) ונגדיר כללים של מדיניות מאוחדת לניהול זהויות והרשאות גישה (IAM) כדי לשלוט בבקשות יוצאות מהרשת.
יצירת תבנית קישוריות
מפרט תבנית הקישוריות מגדיר:
-
egressNetworkConfig.networkAttachment: מצביע על רשת ה-PSC המצורפת, ומחבר את Agent Gateway לרשת ה-VPC. -
egressNetworkConfig.vpcEgress: מוגדר ל-ALL_TRAFFICכדי שכל החיבורים היוצאים של הסוכן (כולל נקודות קצה של MCP באינטרנט הציבורי) ינותבו לרשת ה-VPC ויצאו באמצעות תרגום של כתובת ה-IP של המקור ב-Cloud NAT.
יוצרים ומייבאים את קובץ התצורה של תבנית הקישוריות:
# create connectivity template config file (vpcEgress: ALL_TRAFFIC)
cat > cfg/act-${AGW_NAME}.yaml << EOF
accessPath: AGENT_TO_ANYWHERE
name: projects/${PROJ_NO}/locations/${REGION}/agentConnectivityTemplates/act-${AGW_NAME}
deploymentModel: CENTRALIZED
egressNetworkConfig:
networkAttachment: ${PSC_NA_URI}
vpcEgress: ALL_TRAFFIC
EOF
# import connectivity template file (create template)
gcloud network-services agent-connectivity-templates import act-${AGW_NAME} \
--source="cfg/act-${AGW_NAME}.yaml" \
--location=${REGION}
יצירת Agent Gateway
במפרט של Agent Gateway מוגדרים:
-
agentConnectivityTemplate: מצביע על תבנית הקישוריות, כדי שהשער יוכל להשתמש בצירוף רשת PSC לקישוריות VPC. -
registries: משייך את שער הגישה ל-Agent Registry האזורי כדי לפתור נקודות קצה של MCP שמופיעות בקטלוג וסכימות של כלים.
יוצרים ומייבאים את קובץ ההגדרות של Agent Gateway:
# create agent gateway config file
cat > cfg/${AGW_NAME}.yaml << EOF
name: ${AGW_NAME}
protocols:
- MCP
googleManaged:
governedAccessPath: AGENT_TO_ANYWHERE
agentConnectivityTemplate: projects/${PROJ_NO}/locations/${REGION}/agentConnectivityTemplates/act-${AGW_NAME}
registries:
- "//agentregistry.googleapis.com/projects/${PROJ_ID}/locations/${REGION}"
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
--source="cfg/${AGW_NAME}.yaml" \
--location=${REGION}
אימות הפריסה של Agent Gateway
מאשרים את ההגדרה של תבנית הקישוריות ושל Agent Registry:
# verify agent gateway deployment details
gcloud network-services agent-gateways describe ${AGW_NAME} \
--location=${REGION} \
--format="yaml(name,agentConnectivityTemplate,registries)"
פלט לדוגמה:
agentConnectivityTemplate: projects/[PROJ_NO]/locations/us-central1/agentConnectivityTemplates/act-foo-us-central1
name: projects/[PROJ_ID]/locations/us-central1/agentGateways/agw-foo-us-central1-ata
registries:
- //agentregistry.googleapis.com/projects/[PROJ_ID]/locations/us-central1
בודקים את מחבר הרשת של PSC כדי לוודא שהשער מחובר:
# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw \
--region=${REGION} \
--format="yaml(connectionEndpoints)"
פלט לדוגמה:
connectionEndpoints:
- ipAddress: 10.10.10.2
projectIdOrNum: '[TENANT_PROJ_NO]'
status: ACCEPTED
subnetwork: https://www.googleapis.com/compute/v1/projects/[PROJ_ID]/regions/us-central1/subnetworks/subnet-us-central1-agw
הענקת הרשאה
Agent Gateway מאבטח את תעבורת הנתונים היוצאת של כלי ומנהל אותה באמצעות מדיניות הרשאות (networksecurity.authzPolicies) שמשולבת עם מדיניות מאוחדת למתן הרשאות גישה (UAP) של שרת proxy לאימות זהויות (IAP).
למרות ש-Agent Gateway תומך בכללים בסיסיים של ALLOW ושל DENY, בסביבות ארגוניות נדרש ניהול מרכזי שמתמקד בזהויות. באמצעות מדיניות גישה מאוחדת ב-IAM (או מדיניות גישה), אתם יכולים לנהל כללי גישה ליציאה באמצעות מדיניות גישה רגילה ב-IAM v3.
איור 3. ארכיטקטורת הרשאות
תהליך ההרשאה מחבר בין שלושה רכיבים:
- מדיניות הרשאות ל-Gateway (
authzPolicy):- משאב אזורי שמטרגט את Agent Gateway.
- ההגדרה בוצעה באמצעות
policyProfile: REQUEST_AUTHZו-action: CUSTOMכדי להפנות את כל בדיקות ההרשאה היוצאות לתוסף ההרשאה של IAP.
- IAP service extension (
authzExtension):- משאב אזורי שמקצה הרשאת בקשה לשרת proxy לאימות זהויות (
iap.googleapis.com). - המערכת מעריכה את כללי המדיניות במצב
ENFORCEבאמצעות גרסת המדיניותV2.
- משאב אזורי שמקצה הרשאת בקשה לשרת proxy לאימות זהויות (
- מדיניות מאוחדת של IAM וקישור (
accessPolicyו-policyBinding):- משאבי IAM v3 גלובליים שמכילים כללי גישה פרטניים.
- מאמת את זהות המנהל (principal) של SPIFFE של הסוכן שקורא, מאמת את הרשאת
iap.googleapis.com/resources.egressViaIAPהאוניברסלית ומעריך תנאים של Common Expression Language (CEL) ביחס למאפייני היעד.
פריסת תוסף שמפנה לבדיקת הרשאות
# create authz extension config file
cat > "cfg/${AGW_NAME}-svc-ext-authz-iap.yaml" << EOF
name: ${AGW_NAME}-svc-ext-authz-iap
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
iapPolicyVersion: "V2"
EOF
# import authz extension config file (create authz extension)
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap \
--source="cfg/${AGW_NAME}-svc-ext-authz-iap.yaml" \
--location=${REGION}
אימות תוסף ההרשאות
בודקים שתוסף ההרשאה פעיל:
# list authz extensions
gcloud service-extensions authz-extensions list \
--location=${REGION} \
--format="table(
name.basename():label=NAME,
createTime.date(tz=LOCAL):label=CREATED,
service:label=SERVICE,
metadata:label=METADATA,
timeout:label=TIMEOUT
)"
פריסת מדיניות הרשאות
יוצרים הגדרת מדיניות הרשאות ב-network-security שמטרגטת את Agent Gateway ומאצילה את אימות הבקשות לתוסף שמפנה לבדיקת הרשאות של IAP:
# create authz policy config file
cat > cfg/${AGW_NAME}-authz-policy-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-iap
target:
resources:
- "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
authzExtension:
resources:
- "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap"
EOF
# import authz policy config file (create authz policy)
gcloud network-security authz-policies import ${AGW_NAME}-authz-policy-iap \
--source=cfg/${AGW_NAME}-authz-policy-iap.yaml \
--location=${REGION}
אימות מדיניות ההרשאות
בודקים שמדיניות ההרשאות פעילה:
# list authz policies
gcloud network-security authz-policies list \
--location=${REGION} \
--format="table(
name.basename():label=NAME,
action:label=ACTION,
policyProfile:label=POLICY_PROFILE,
customProvider.authzExtension.resources[0].basename():label=CUSTOM_PROVIDER_RESOURCE
)"
יצירת מדיניות גישה ב-IAM
הסמכת סוכנים (Agent Gateway) מעבירה עכשיו את בדיקות ההרשאות ל-IAP ומקבלת את המטא-נתונים של היעד מ-Agent Registry. בשלב הבא, מגדירים כלל מדיניות גישה מאוחדת ב-IAM כדי לשלוט בהפעלת כלי יוצאים.
IAP מעריך ביטויי מאפיינים ב-CEL ביחס למאפייני היעד הבאים של Agent Registry:
- סטטוס רשום (
destination.is_registered):- ערך בוליאני (
true/false) שמציין אם היעד רשום ב-Agent Registry.
- ערך בוליאני (
- שם שרת MCP (
destination.agent_registry.mcp_server.name):- שם משאב קנוני של שרת MCP שרשום ב-Agent Registry.
- שיטת MCP (
destination.agent_registry.mcp_server.method):- שיטת ה-MCP שמופעלת (לדוגמה,
tools/call, tools/list, initialize).
- שיטת ה-MCP שמופעלת (לדוגמה,
- שם הכלי (
destination.agent_registry.mcp_server.tool.name):- שם הכלי הספציפי שהופעל (למשל,
get_sprocket_by_idאוget_sprockets), שמאפשר הרשאה ברמת הכלי בשרתי MCP רשומים.
- שם הכלי הספציפי שהופעל (למשל,
הגדרת כלל של מדיניות הגישה ב-IAM
מניפסט הכללים של מדיניות IAM מציין:
- חשבונות משתמש: זהות חשבון המשתמש ב-SPIFFE שמייצגת את הסוכן של העוזר הדיגיטלי המרכזי של Gemini Enterprise.
- הרשאות: נדרשת ההרשאה האוניברסלית
iap.googleapis.com/resources.egressViaIAPלכל תעבורת הנתונים היוצאת שמנוהלת על ידי IAP. - תנאים: ביטוי CEL (
destination.is_registered == true) שמבטיח שהסוכן יוכל להפעיל רק נקודות קצה שמופיעות בקטלוג של Agent Registry.
יוצרים את קובץ המניפסט של כלל המדיניות:
# create access policy rule file
cat > cfg/uap-policy-${SLUG}-rules.json << EOF
[
{
"description": "allow ge assistant to any registered service",
"effect": "ALLOW",
"principals": [
"principal://${TRUST_DOMAIN}/resources/discoveryengine/projects/${PROJ_NO}/locations/global/engines/${GE_APP_INIT}/assistants/default_assistant/agents/default/core_assistant"
],
"operation": {
"permissions": [
"iap.googleapis.com/resources.egressViaIAP"
]
},
"conditions": {
"iap.googleapis.com": {
"expression": \
"destination.is_registered == true"
}
}
}
]
EOF
פריסת מדיניות גישה ב-IAM
יוצרים את מדיניות הגישה הגלובלית ב-IAM באמצעות הכללים שמוגדרים בקובץ המניפסט:
# create iam access policy
gcloud iam access-policies create uap-policy-${SLUG} \
--details-rules=cfg/uap-policy-${SLUG}-rules.json \
--project=${PROJ_ID} \
--location=global
אימות מדיניות הגישה ב-IAM
בודקים שמדיניות הגישה ב-IAM נוצרה בהצלחה, ומעיינים בפרטי הכלל:
# show iam access policy details
gcloud iam access-policies describe uap-policy-${SLUG} \
--project=${PROJ_ID} \
--location=global
פלט לדוגמה:
details:
rules:
- conditions:
iap.googleapis.com:
expression: destination.is_registered == true
description: allow ge assistant to any registered service
effect: ALLOW
operation:
permissions:
- iap.googleapis.com/resources.egressViaIAP
principals:
- principal://agents.global.org-[ORG_ID].system.id.goog/resources/discoveryengine/projects/[PROJ_NO]/locations/global/engines/app-foo-global_[EPOCH]/assistants/default_assistant/agents/default/core_assistant
name: projects/[PROJ_ID]/locations/global/accessPolicies/uap-policy-foo
קישור מדיניות גישה ב-IAM לפרויקט
כדי להפעיל את האכיפה בכל Agent Gateway בפרויקט, יוצרים מדיניות מחייבת שמקשרת את מדיניות הגישה של IAM למשאב הפרויקט:
# bind iam access policy to project resource
gcloud iam policy-bindings create uap-bind-${SLUG} \
--policy="projects/${PROJ_ID}/locations/global/accessPolicies/uap-policy-${SLUG}" \
--target-resource="//cloudresourcemanager.googleapis.com/projects/${PROJ_ID}" \
--project=${PROJ_ID} \
--location=global
אימות הקישור של מדיניות הגישה ב-IAM
בודקים את נקודות הקישור הפעילות למדיניות הנכונה ולטירגוט הנכון:
# show policy binding details
gcloud iam policy-bindings describe uap-bind-${SLUG} \
--project=${PROJ_ID} \
--location=global
בזה מסתיים החלק שקשור ל-Agent Gateway… עכשיו נעבור לקטע שרת MCP.
6. שרת MCP
בקטע הזה נפרס שרת משאבים חיצוני מסוג OAuth 2.0 (mcp-sprockets) של FastMCP שחושף את הכלים get_sprockets ו-get_sprocket_by_id, ונוודא שהוא מבצע אימות קריפטוגרפי של אסימוני Bearer מסוג Google OAuth 2.0 מול https://oauth2.googleapis.com/tokeninfo. בשלב הבא תגבילו את כלל חומת האש החיצונית לכתובת ה-IP הסטטית של Cloud NAT.
פריסת שרת MCP חיצוני
סקריפט הפעלה מתקין את Caddy ואת FastMCP, מפעיל את שרת mcp-sprockets ב-127.0.0.1:8080 ומגדיר את Caddy להצגת HTTPS ב-${MCP_DOMAIN}.
בכל פעם ש-Gemini Enterprise קורא לכלי MCP (לדוגמה, get_sprockets ו-get_sprocket_by_id), השרת:
- קריאת כתובת ה-IP של המקור של המתקשר מהכותרת
X-Forwarded-For(egress_source_ip). - מחפשים את הכותרת
Authorization: Bearer [access_token]ומאמתים את הטוקן מול נקודת האימות של Google OAuth 2.0 (https://oauth2.googleapis.com/tokeninfo?access_token=[token]). - החזרת
"authenticated_user"([user_email]) לצד"egress_source_ip", שמוכיחה ששרת ה-MCP החיצוני אימת גם את כתובת ה-IP הסטטית של Cloud NAT וגם את הזהות של משתמש הקצה ב-Google OAuth 2.0.
# create startup script for the sprockets mcp server vm
cat > cfg/server-startup.sh << 'EOF'
#!/bin/bash
set -euo pipefail
MCP_DOMAIN=$(curl -s -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/instance/attributes/mcp-domain)
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y caddy python3-venv curl
mkdir -p /opt/mcp-sprockets
python3 -m venv /opt/mcp-sprockets/venv
/opt/mcp-sprockets/venv/bin/pip install --upgrade pip
/opt/mcp-sprockets/venv/bin/pip install "fastmcp>=2.14.0" "uvicorn>=0.34.0"
cat <<'PYEOF' > /opt/mcp-sprockets/main.py
import json
import urllib.parse
import urllib.request
from typing import Any, Dict
from fastmcp import FastMCP
from fastmcp.server.dependencies import get_http_headers
from mcp.types import ToolAnnotations
mcp = FastMCP("mcp-sprockets")
SPROCKETS = {
"SPK-101": {"id": "SPK-101", "model": "ANSI-40-12T", "teeth": 12, "pitch_in": 0.5, "material": "1045 Carbon Steel", "stock": 140, "unit_price": 18.50},
"SPK-102": {"id": "SPK-102", "model": "ANSI-40-24T", "teeth": 24, "pitch_in": 0.5, "material": "1045 Carbon Steel", "stock": 85, "unit_price": 29.00},
"SPK-103": {"id": "SPK-103", "model": "ANSI-60-16T", "teeth": 16, "pitch_in": 0.75, "material": "304 Stainless Steel", "stock": 60, "unit_price": 42.00},
"SPK-104": {"id": "SPK-104", "model": "ANSI-60-32T", "teeth": 32, "pitch_in": 0.75, "material": "304 Stainless Steel", "stock": 35, "unit_price": 68.00},
"SPK-105": {"id": "SPK-105", "model": "ANSI-80-20T", "teeth": 20, "pitch_in": 1.0, "material": "4140 Alloy Steel", "stock": 50, "unit_price": 95.00}
}
def verify_google_oauth_token(headers: Dict[str, str]) -> str:
token = headers.get("authorization", "")[7:].strip() if headers.get("authorization", "").lower().startswith("bearer ") else ""
if not token:
return "unauthenticated (missing Bearer token)"
url = "https://oauth2.googleapis.com/tokeninfo?" + urllib.parse.urlencode({"access_token": token})
with urllib.request.urlopen(url, timeout=5) as resp:
info = json.loads(resp.read().decode("utf-8"))
return info.get("email", info.get("sub", "verified-oauth-user"))
@mcp.tool(
description="list all sprockets in the inventory catalog",
annotations=ToolAnnotations(
readOnlyHint=True,
destructiveHint=False,
idempotentHint=True,
openWorldHint=False
)
)
def get_sprockets() -> Dict[str, Any]:
headers = get_http_headers(include_all=True)
caller_ip = headers.get("x-forwarded-for", "unknown").split(",")[0].strip()
try:
user_email = verify_google_oauth_token(headers)
except Exception as exc:
return {"egress_source_ip": caller_ip, "authenticated_user": None, "error": f"OAuth token verification failed: {exc}"}
return {
"egress_source_ip": caller_ip,
"authenticated_user": user_email,
"sprockets": list(SPROCKETS.values())
}
@mcp.tool(
description="fetch details of a specific sprocket by its ID (eg, SPK-101)",
annotations=ToolAnnotations(
readOnlyHint=True,
destructiveHint=False,
idempotentHint=True,
openWorldHint=False
)
)
def get_sprocket_by_id(sprocket_id: str) -> Dict[str, Any]:
headers = get_http_headers(include_all=True)
caller_ip = headers.get("x-forwarded-for", "unknown").split(",")[0].strip()
try:
user_email = verify_google_oauth_token(headers)
except Exception as exc:
return {"egress_source_ip": caller_ip, "authenticated_user": None, "error": f"OAuth token verification failed: {exc}"}
sprocket = SPROCKETS.get(sprocket_id.upper())
if sprocket:
return {
"egress_source_ip": caller_ip,
"authenticated_user": user_email,
"sprocket": sprocket
}
return {
"egress_source_ip": caller_ip,
"authenticated_user": user_email,
"error": f"Sprocket '{sprocket_id}' not found."
}
app = mcp.http_app(stateless_http=True)
PYEOF
cat <<'SVCEOF' > /etc/systemd/system/mcp-sprockets.service
[Unit]
Description=Sprockets FastMCP Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/mcp-sprockets
ExecStart=/opt/mcp-sprockets/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8080 --forwarded-allow-ips="*"
Restart=always
[Install]
WantedBy=multi-user.target
SVCEOF
systemctl daemon-reload
systemctl enable --now mcp-sprockets.service
systemctl restart mcp-sprockets.service
cat <<CADDYEOF > "/etc/caddy/Caddyfile"
${MCP_DOMAIN} {
reverse_proxy 127.0.0.1:8080
}
CADDYEOF
systemctl restart caddy
EOF
# create vm instance
gcloud compute instances create vm-${MCP_NAME} \
--zone=${ZONE} \
--machine-type=e2-micro \
--image-family=debian-12 \
--image-project=debian-cloud \
--shielded-secure-boot \
--network-interface="subnet=subnet-${REGION}-mcp,address=${MCP_EXT_IP}" \
--metadata="mcp-domain=${MCP_DOMAIN}" \
--metadata-from-file="startup-script=cfg/server-startup.sh"
אימות המוּכנוּת (readiness) של שרת ה-MCP ואימות OAuth 2.0 tokeninfo
מחכים כמה דקות עד שסקריפט לטעינה בזמן ההפעלה יתקין את FastMCP ואת Caddy ויקצה את אישור ה-TLS של Let's Encrypt.
# poll until caddy serves valid tls and fastmcp responds to tools/list
for i in $(seq 1 30); do
RESP=$(curl -s -X POST "${MCP_URL}" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}' || true)
if echo "${RESP}" | grep -q "get_sprockets"; then
echo "mcp server and let's encrypt tls ready"
break
fi
echo "...waiting for mcp server and let's encrypt tls (attempt ${i}/30)..."
sleep 5
done
בודקים את פענוח ה-DNS של sslip.io, את אישור ה-TLS של Let's Encrypt שמוגש על ידי Caddy ואת התגובה של FastMCP tools/list:
# query mcp server for tools list
curl -v -X POST "${MCP_URL}" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}' \
2>&1 | grep -E "Connected to|subject:|issuer:|SSL certificate|HTTP/|event:|data:"
פלט לדוגמה:
* Connected to mcp.203.0.113.20.sslip.io (203.0.113.20) port 443
* subject: CN=mcp.203.0.113.20.sslip.io
* issuer: C=US; O=Let's Encrypt; CN=E5
* SSL certificate verify ok.
> POST /mcp HTTP/2
< HTTP/2 200
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"get_sprockets","description":"list all sprockets in the inventory catalog","inputSchema":{"properties":{},"type":"object"},"annotations":{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}},{"name":"get_sprocket_by_id","description":"fetch details of a specific sprocket by its ID (eg, SPK-101)","inputSchema":{"properties":{"sprocket_id":{"type":"string"}},"required":["sprocket_id"],"type":"object"},"annotations":{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}}]}}
לפני שנועלים את חומת האש החיצונית, מריצים בדיקה tools/call של העברת טוקן Bearer מסוג Google OAuth 2.0 (gcloud auth print-access-token) כדי לוודא ש-mcp-sprockets מאמת את הטוקן מול https://oauth2.googleapis.com/tokeninfo ומחזיר את כתובת האימייל שלכם "authenticated_user" לצד כתובת ה-IP של המקור שממנו מתבצעת הקריאה:
# query mcp server (test tools/call with google oauth 2.0 bearer token)
curl -s -X POST "${MCP_URL}" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_sprocket_by_id",
"arguments": {"sprocket_id": "SPK-105"}
}
}' | sed -n 's/^data: //p' | jq '.result.structuredContent'
פלט לדוגמה (שמציג את כתובת ה-IP הישירה של Cloud Shell לפני נעילת חומת האש ואת האימייל המאומת של Google OAuth):
{
"egress_source_ip": "198.51.100.154",
"authenticated_user": "user@example.com",
"sprocket": {
"id": "SPK-105",
"model": "ANSI-80-20T",
"teeth": 20,
"pitch_in": 1.0,
"material": "4140 Alloy Steel",
"stock": 50,
"unit_price": 95.0
}
}
נעילת חומת האש לכתובת IP סטטית של Cloud NAT
מעדכנים את מדיניות חומת האש של רשת ה-VPC החיצונית כך שהיציאה 443 ב-TCP תקבל תעבורת נתונים רק מכתובת ה-IP החיצונית הסטטית של Cloud NAT (${NAT_EXT_IP}/32):
# create fw policy rule allowing https only from cloud nat static ip
gcloud compute network-firewall-policies rules create 2003 \
--description="allow in any https from nat ip" \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy \
--action=allow \
--direction=INGRESS \
--layer4-configs=tcp:443 \
--src-ip-ranges="${NAT_EXT_IP}/32" \
--dest-ip-ranges=240.240.240.0/24 \
--enable-logging
# delete temporary open fw policy rules
gcloud compute network-firewall-policies rules delete 2001 \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy
gcloud compute network-firewall-policies rules delete 2002 \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy
בודקים אם הוגדרו כללים במדיניות חומת האש החיצונית:
# list all rules in the global network firewall policy
gcloud compute network-firewall-policies describe fw-policy-${SLUG}-external \
--global \
--flatten="rules[]" \
--format="table(
rules.priority:label=RULE,
rules.action:label=ACTION,
rules.direction:label=DIRECTION,
firstof(
rules.match.srcIpRanges,
rules.match.srcAddressGroups,
rules.match.srcSecureTags.name
).map().sub('projects/[^/]+/(locations/global/|global/)', '').list():label=SRC,
firstof(
rules.targetForwardingRules,
rules.match.destIpRanges,
rules.targetType
).map().sub('projects/[^/]+/(locations/global/|global/)', '').list():label=DST
)"
בודקים שחיבורים ישירים מחוץ לכתובת ה-IP הסטטית של Cloud NAT חסומים עכשיו:
# query mcp server
curl -i --connect-timeout 5 "${MCP_URL}" \
|| echo "Expected: Direct access blocked (not originating from ${NAT_EXT_IP})"
פלט לדוגמה:
curl: (28) Connection timed out after 5001 milliseconds
Expected: Direct access blocked (not originating from 203.0.113.10)
בזה מסתיים החלק על שרת ה-MCP... עכשיו נעבור לקטע Agent Registry.
7. Agent Registry
Agent Registry הוא מלאי מרכזי של כל הסוכנים, שרתי ה-MCP ונקודות הקצה (ממשקי API) בסביבת ה-AI. הוא גם קטלוג שאפשר להשתמש בו כדי לרשום, לחפש ולגלות כלים ושירותים רשומים אחרים לשימוש באפליקציות AI. Agent Gateway משתמש במודל הנתונים של המרשם כבסיס למדיניות כדי לאכוף בקרת גישה ליציאה. ההרשאות של תפקידי IAM שניתנות לסוכני חשבונות משתמשים נבדקות מול המדיניות שקשורה למשאב המרשם.
בקטע הזה נרשום את שרת ה-MCP ואת סכימת הכלים שלו (toolspec.json) ב-Agent Registry. כדי לתמוך באינטראקציה של Gemini Enterprise עם ממשקי API ושירותים של Google דרך Agent Gateway, תיצרו גם שירות יחיד של נקודת קצה של מאגר (oauth2-googleapis) שמציין את ממשקי Google API שמשמשים את Gemini Enterprise להחלפת קוד האימות באסימונים.
רישום שרת MCP
כדי לאפשר ל-Gemini Enterprise לגלות את הכלים שזמינים בשרת ה-MCP, צריך לספק קובץ מפרט כלים (cfg/toolspec.json) כשרושמים את השירות ב-Agent Registry.
יצירת מפרט של כלי MCP
# create tool spec file for the sprockets mcp server
cat <<'EOF' > cfg/toolspec.json
{
"tools": [
{
"name": "get_sprockets",
"description": "List all sprockets in the inventory catalog.",
"inputSchema": {
"type": "object",
"properties": {}
},
"isReadOnly": true,
"isDestructive": false,
"isIdempotent": true,
"isOpenWorld": false
},
{
"name": "get_sprocket_by_id",
"description": "Fetch details of a specific sprocket by its ID (e.g., SPK-101).",
"inputSchema": {
"type": "object",
"properties": {
"sprocket_id": {
"type": "string",
"description": "The sprocket identifier (e.g., SPK-101, SPK-105)."
}
},
"required": [
"sprocket_id"
]
},
"isReadOnly": true,
"isDestructive": false,
"isIdempotent": true,
"isOpenWorld": false
}
]
}
EOF
יצירת שירות שרת MCP
# create registry service (mcp server)
gcloud agent-registry services create ${MCP_NAME} \
--project=${PROJ_ID} \
--location=${REGION} \
--display-name="${MCP_DOMAIN}" \
--description="MANDATORY SPROCKETS INVENTORY & CATALOG AGENT: You MUST \
ALWAYS invoke this tool (get_sprockets or get_sprocket_by_id) for ANY question \
about sprockets, inventory catalog listings, sprocket IDs (SPK-101 through \
SPK-105), ANSI models (ANSI-40, ANSI-60, ANSI-80), tooth counts, pitch, \
material, stock levels, unit prices, authenticated_user, or egress_source_ip \
verification. NEVER guess or fabricate inventory data yourself and NEVER \
transfer sprocket queries to file_and_coding_agent or web search. Always \
delegate every sprocket or inventory lookup to this tool." \
--mcp-server-spec-type=tool-spec \
--mcp-server-spec-content=cfg/toolspec.json \
--interfaces=protocolBinding=JSONRPC,url="${MCP_URL}"
אימות שרת MCP ב-Agent Registry
מוודאים שהשירות מופיע כשרת MCP רשום באזור, יחד עם כתובת ה-URL של נקודת הקצה והכלים הזמינים:
# list registry regional mcp servers
gcloud agent-registry mcp-servers list \
--location=${REGION} \
--project=${PROJ_ID} \
--format="table(
name.basename():label=REGISTRY_ID,
displayName:label=DISPLAY_NAME,
interfaces[0].url:label=ENDPOINT_URL,
tools[].name.list():label=TOOLS
)"
פלט לדוגמה:
REGISTRY_ID DISPLAY_NAME ENDPOINT_URL TOOLS
agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8 mcp.203.0.113.20.sslip.io https://mcp.203.0.113.20.sslip.io/mcp get_sprockets,get_sprocket_by_id
כדי לראות שהשירות רושם את ההגדרות המדויקות של הכלים, את סכימות הקלט ואת הערות ההתנהגות של כל כלי, אפשר לעיין במפרט ההגדרות של השירות:
# describe mcp server tool specs
gcloud agent-registry services describe ${MCP_NAME} \
--location=${REGION} \
--project=${PROJ_ID} \
--format="yaml(mcpServerSpec.content.tools)"
רישום של נקודת קצה של OAuth
כדי לאפשר ל-backend של Gemini Enterprise DataConnector (שפועל כלקוח OAuth בתהליך ההרשאה) לשלוח בקשות POST לשרת ההרשאה של Google OAuth 2.0 (https://oauth2.googleapis.com/token) דרך Agent Gateway, צריך לרשום את נקודת הקצה של הטוקן ב-Agent Registry כדי ש-IAP יוכל להעריך את destination.is_registered == true.
יצירת שירות נקודת קצה
# create registry service (endpoint)
gcloud agent-registry services create oauth2-googleapis \
--project=${PROJ_ID} \
--location=${REGION} \
--display-name="oauth2.googleapis.com" \
--description="google oauth2 endpoint" \
--endpoint-spec-type=no-spec \
--interfaces=protocolBinding=JSONRPC,url=https://oauth2.googleapis.com
אימות הרישום של נקודת קצה
# list registry regional endpoints
gcloud agent-registry endpoints list \
--project=${PROJ_ID} \
--location=${REGION} \
--flatten="interfaces[]" \
--format="table(
displayName,
name.basename():label=ENDPOINT_ID,
interfaces.url:label=URL
)"
הפלט הצפוי:
DISPLAY_NAME ENDPOINT_ID URL
oauth2.googleapis.com agentregistry-00000000-0000-0000-0012-3456789abcde https://oauth2.googleapis.com
מדיניות גישה ב-IAM
מכיוון שמדיניות הגישה של IAM מגדירה מדיניות הרשאות לכל שירות רשום שמבוקש על ידי זהות הסוכן של אפליקציית Gemini Enterprise core_assistant, לא נדרשים קישורי מדיניות נוספים של IAM להחלפת טוקן OAuth.
- conditions:
iap.googleapis.com:
expression: destination.is_registered == true
description: allow ge assistant to any registered service
effect: ALLOW
כאן מסתיים החלק שקשור ל-Agent Registry... עכשיו נעבור לקטע Gemini Enterprise.
8. Gemini Enterprise
בקטע הזה מגדירים את ספק הזהויות, את רישיון הניסיון ואת פרטי הכניסה של לקוח Google Identity OAuth 2.0, יוצרים אפליקציית Gemini Enterprise (${GE_APP_ID}), מקשרים את התעבורה היוצאת ל-Agent Gateway (${AGW_URI}) ומחברים את שרת ה-MCP של sprockets הרשום כמאגר נתונים מותאם אישית של MCP מסוג OAuth 2.0.
יצירת אפליקציית Gemini Enterprise
השיטה הבאה משתמשת ב-API discoveryengine.googleapis.com כדי ליצור את המשאבים וההגדרות של אפליקציית Gemini Enterprise. הוראות להגדרה באמצעות ממשק המשתמש של מסוף Google Cloud מופיעות במאמר יצירת אפליקציה.
# create engine (ge app)
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines?engineId=${GE_APP_INIT}" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
-d @- <<EOF
{
"displayName": "${GE_APP_DISPLAY_NAME}",
"dataStoreIds": [],
"solutionType": "SOLUTION_TYPE_SEARCH",
"industryVertical": "GENERIC",
"appType": "APP_TYPE_INTRANET",
"searchEngineConfig": {
"searchTier": "SEARCH_TIER_ENTERPRISE",
"searchAddOns": [
"SEARCH_ADD_ON_LLM"
]
},
"commonConfig": {
"companyName": "${GE_APP_ORG_NAME}"
},
"observabilityConfig": {
"observabilityEnabled": true,
"sensitiveLoggingEnabled": true
}
}
EOF
אימות יצירת האפליקציה
# fetch engine (ge app) id
export GE_APP_ID=$(curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" -H "X-Goog-User-Project: ${PROJ_ID}" \
| jq -r --arg name "${GE_APP_DISPLAY_NAME}" '.engines[] | select(.displayName==$name) | .name | split("/") | last')
echo "engine (ge app) id: ${GE_APP_ID}"
כדי לראות את ההגדרה שנוצרה, צופים בפרטי המנוע:
# get engine (ge app) details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}"
הפעלת ספק זהויות
מפעילים את Google Identity כספק הזהויות לאימות משתמשי קצה באפליקציית Gemini Enterprise.
בשיטה הבאה משתמשים ב-discoveryengine.googleapis.com API כדי להגדיר את ספק הזהויות של אפליקציית Gemini Enterprise. הוראות להגדרה באמצעות ממשק המשתמש של מסוף Google Cloud מופיעות במאמר הגדרת ספק זהויות.
# set identity provider
curl -s -X PATCH "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/aclConfig" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
-d @- <<EOF
{
"idpConfig": {
"idpType": "GSUITE"
}
}
EOF
אימות ספק הזהויות
# show identity provider
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/aclConfig" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}"
הפלט "idpType": "GSUITE" תואם לספק הזהויות של Google.
(אופציונלי) הפעלת רישיון ניסיון ל-Gemini Enterprise
אם אתם משתמשים בפרויקט שהוקצו לו רישיונות ל-Gemini Enterprise, אתם יכולים לדלג על השלב הזה. אם אתם משתמשים בפרויקט חדש בלי רישיון, ממשיכים לפעול לפי השלבים הבאים.
יוצרים משאב של הגדרת רישיון כדי להקצות מושבי משתמשים ב-Gemini Enterprise למשך 30 ימים. הפעולה הזו תגדיר את רישיון ברירת המחדל לניסיון החדש, כך שלכל משתמש שמתחבר יוקצה מושב באופן אוטומטי:
# configure free trial subscription
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/licenseConfigs?licenseConfigId=free_trial_gemini" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
-d @- <<EOF
{
"subscriptionTier": "SUBSCRIPTION_TIER_SEARCH_AND_ASSISTANT",
"freeTrial": true
}
EOF
אימות החלת הרישיון
# show license config
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/licenseConfigs/free_trial_gemini" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}"
בודקים אם יש "subscriptionTerm": "SUBSCRIPTION_TERM_ONE_MONTH" ו"freeTrial": true.
# verify auto-registration enabled on default user store
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/userStores/default_user_store" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}"
בודקים אם יש ../free_trial_gemini" ו"enableLicenseAutoRegister": true.
הגדרת הסכמה ופרטי כניסה ב-OAuth 2.0
כדי לאפשר ל-Gemini Enterprise (לקוח OAuth 2.0) לקבל אסימון Bearer של Google OAuth 2.0 שהוקצה למשתמש ולהציג אותו לmcp-sprocketsשרת המשאבים בשמכם, צריך להגדיר את מסך ההסכמה של Google Auth Platform OAuth וליצור מזהה לקוח של אפליקציית אינטרנט ב-OAuth 2.0 בפרויקט.
1. מגדירים את מסך ההסכמה של OAuth
מדפיסים את כתובת ה-URL הישירה של מסוף Google Cloud עבור Google Auth Platform Overview בפרויקט:
# print google auth platform consent screen url
echo "https://console.cloud.google.com/auth/overview?project=${PROJ_ID}"
- פותחים את כתובת ה-URL בדפדפן ולוחצים על Get started (אם עדיין לא הגדרתם את הפרויקט).
- בקטע App Information, מגדירים את App name ל-
mcp-sprockets-oauth, בוחרים את כתובת האימייל (${USER_IDENTITY}) בתור User support email ולוחצים על Next. - בקטע קהל, בוחרים באפשרות פנימי (מומלץ אם החשבון שלכם שייך לארגון Google Workspace) או באפשרות חיצוני (אם אתם משתמשים בחשבון אישי). אם בוחרים באפשרות חיצוני, מוסיפים את
${USER_IDENTITY}בקטע **משתמשי בדיקה של קהל**, ואז לוחצים על הבא. - מזינים את כתובת האימייל בקטע פרטים ליצירת קשר, מאשרים את הסכם המדיניות ולוחצים על יצירה.
2. יצירת מזהה לקוח ב-OAuth 2.0 לאפליקציית אינטרנט
הדפסה של כתובת ה-URL הישירה של מסוף Google Cloud לדף Clients:
# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"
- פותחים את כתובת ה-URL ולוחצים על + Create client (יצירת לקוח).
- בוחרים באפשרות אפליקציית אינטרנט בתור Application type ומגדירים את Name ל-
ge-mcp-sprockets-client. - מסמנים את התיבה This client will be used by an AI-powered agent (הלקוח הזה ישמש סוכן מבוסס-AI) כדי להגדיר את לקוח ה-OAuth עבור תהליכי עבודה של סוכני AI שפועלים בשם המשתמשים.
- בקטע Authorized redirect URIs (כתובות URI מורשות להפניה אוטומטית), לוחצים על + Add URI (הוספת כתובת URI) ומוסיפים את כתובת ה-URI של קריאת החזרה (callback) של Gemini Enterprise OAuth:
https://vertexaisearch.cloud.google.com/oauth-redirect
- לוחצים על Create, מעתיקים את מזהה הלקוח ואת הסוד של הלקוח שנוצרו, ומייצאים אותם בסשן של מעטפת הפקודות:
מגדירים את מזהה הלקוח ואת סוד הלקוח ב-OAuth 2.0 שנוצרו בשלב הקודם:
# set oauth 2.0 client id (paste in id)
export OAUTH_CLIENT_ID="SET_YOUR_OAUTH_CLIENT_ID_HERE"
# set oauth 2.0 client secret (paste in secret)
export OAUTH_CLIENT_SECRET="SET_YOUR_OAUTH_CLIENT_SECRET_HERE"
בודקים שמזהה הלקוח מוגדר:
# print oauth 2.0 client id
echo "oauth client id: ${OAUTH_CLIENT_ID}"
איגוד אל Agent Gateway
ניתוב תנועה יוצאת מ-Gemini Enterprise דרך Agent Gateway יוצר גבול מרכזי לאכיפה ולניתוב של כתובות IP סטטיות לכל הפעלות כלי סוכן ה-AI:
- אכיפת מדיניות מרכזית: Agent Gateway פועל כ-proxy מוטבע שבודק בקשות לכלים יוצאים מול מדיניות הרשאות לפני שהתנועה יוצאת מסביבת הסוכן.
- יציאה דטרמיניסטית מ-VPC ומ-Cloud NAT: קישור של Gemini Enterprise ל-Agent Gateway שהוגדר עם
vpcEgress: ALL_TRAFFICמבטיח שההפניות לכלים ינותבו דרך צירוף הרשת של PSC ויצאו באמצעות כתובת ה-IP הסטטית השמורה של Cloud NAT. - יכולת ביקורת מאוחדת: מאפשרת רישום מרכזי של בקשות ושל נתיבי ביקורת בכל שרתי ה-MCP המחוברים.
מטמיעים תיקון במנוע agentGatewaySetting כדי להפעיל:
# bind engine (ge app) to agent gateway
curl -s -X PATCH "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}?updateMask=agentGatewaySetting.defaultEgressAgentGateway.name" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
-d @- <<EOF
{
"agentGatewaySetting": {
"defaultEgressAgentGateway": {
"name": "${AGW_URI}"
}
}
}
EOF
אימות הקישור של Agent Gateway
מאחזרים את הגדרת האפליקציה כדי לאשר את agentGatewaySetting הקישור:
# verify engine (ge app) agent gateway configuration
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
| jq '{name: .name, displayName: .displayName, agentGatewaySetting: .agentGatewaySetting}'
פלט לדוגמה:
{
"name": "projects/[PROJ_NO]/locations/global/collections/default_collection/engines/app-foo-global_[EPOCH]",
"displayName": "foo global app",
"agentGatewaySetting": {
"defaultEgressAgentGateway": {
"name": "projects/[PROJ_ID]/locations/us-central1/agentGateways/agw-foo-us-central1-ata"
}
}
}
יצירת מאגר נתונים של שרת MCP בהתאמה אישית
בקטע הזה נסביר איך לקשר את mcp-sprockets ל-Gemini Enterprise באמצעות יצירה של מאגר נתונים מותאם אישית של MCP עם OAuth 2.0, שמוגדר עם Google Identity (https://accounts.google.com/o/oauth2/v2/auth ו-https://oauth2.googleapis.com/token).
התהליך הזה כולל שני שלבים באמצעות Discovery Engine API:
- יצירה (
:setUpDataConnector): יוצרת משאב ייעודי שלCollection(${MCP_NAME}-collection-%timestamp), מצרפת אתDataConnector(custom_mcp) עםauth_type: "OAUTH"ומקצה אתDataStore(..._mcp_data) שמשמש כגיבוי. - הפעלה (
PATCH .../dataConnector?updateMask=actionConfig): מפעילה את זמן הריצה של פעולת המחבר (actionState: "ACTIVE") באמצעות מפרט כלי Agent Registry, פרמטרים של Google Identity OAuth 2.0 ומקשרת אתDataStore(dataStoreIds) ל-Gemini EnterpriseEngine.
# fetch mcp server agent registry resource name
export MCP_REGISTRY_URI=$(gcloud agent-registry mcp-servers list \
--location=${REGION} \
--project=${PROJ_ID} \
--filter="displayName:${MCP_DOMAIN}" \
--format="value(name)")
echo "mcp registry name: ${MCP_REGISTRY_URI}"
echo "mcp url: ${MCP_URL}"
יצירת מחבר נתונים
# create custom mcp data connector with google identity oauth 2.0 and link to engine
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}:setUpDataConnector" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"collectionId": "${MCP_NAME}-collection-$(date +%s)",
"collectionDisplayName": "${MCP_NAME}-collection",
"dataConnector": {
"dataSource": "custom_mcp",
"dataSourceVersion": 1,
"params": {
"oauth_access_token": "unused"
},
"refreshInterval": "86400s",
"entities": [
{
"entityName": "mcp_data"
}
],
"connectorModes": [
"FEDERATED"
],
"actionConfig": {
"isActionConfigured": true,
"createBapConnection": true,
"actionParams": {
"auth_type": "OAUTH",
"instance_uri": "${MCP_URL}",
"auth_uri": "https://accounts.google.com/o/oauth2/v2/auth",
"auth_uri_params": "&access_type=offline&prompt=consent",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "${OAUTH_CLIENT_ID}",
"client_secret": "${OAUTH_CLIENT_SECRET}",
"scopes": "openid email profile",
"client_secret_basic_override": true,
"mcp_server_source": "REGISTRY_MCP",
"registry_mcp_server_name": "${MCP_REGISTRY_URI}",
"mcp_agent_instructions": "MANDATORY SPROCKETS INVENTORY & CATALOG AGENT: Always invoke get_sprockets or get_sprocket_by_id for any question about sprockets, inventory catalog listings, sprocket IDs (SPK-101 through SPK-105), ANSI models (ANSI-40, ANSI-60, ANSI-80), tooth counts, pitch, material, stock levels, unit prices, authenticated_user, or egress_source_ip verification.",
"use_agent_gateway_egress": true,
"agent_gateway_engine": "projects/${PROJ_ID}/locations/global/collections/default_collection/engines/${GE_APP_ID}"
}
}
}
}
EOF
אימות של יצירת מחבר נתונים
# fetch collection id
export GE_COLLECTION_ID=$(curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
| jq -r --arg dname "${MCP_NAME}-collection" '.collections[] | select(.displayName == $dname) | .name | split("/") | last' | head -n 1)
echo "ge collection id: ${GE_COLLECTION_ID}"
בודקים שהשדה "registry_mcp_server_name" מאוכלס ב-UUID של Agent Registry עבור שרת ה-MCP, ושהשדה "auth_type" הוא OAUTH:
# show data connector details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/${GE_COLLECTION_ID}/dataConnector" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
| jq '{name, state, actionState, connectorModes, bapConfig, auth_type: .actionConfig.actionParams.auth_type, registry_mcp_server_name: .actionConfig.actionParams.registry_mcp_server_name}'
צופים ברשומה של שרת MCP בממשק המשתמש של מסוף Google Cloud:
echo "mcp server registry page url: https://console.cloud.google.com/agent-platform/agent-registry/mcp-servers/${REGION}/${MCP_REGISTRY_URI##*/}/overview?project=${PROJ_ID}"
הפעלת מחבר נתונים
# activate and bind data connector with google identity oauth 2.0
curl -s -X PATCH "https://discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/global/collections/${GE_COLLECTION_ID}/dataConnector?updateMask=actionConfig" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
-H "Content-Type: application/json" \
-d @- <<EOF
{
"name": "projects/${PROJ_ID}/locations/global/collections/${GE_COLLECTION_ID}/dataConnector",
"actionConfig": {
"isActionConfigured": true,
"createBapConnection": true,
"actionParams": {
"auth_type": "OAUTH",
"instance_uri": "${MCP_URL}",
"auth_uri": "https://accounts.google.com/o/oauth2/v2/auth",
"auth_uri_params": "&access_type=offline&prompt=consent",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "${OAUTH_CLIENT_ID}",
"client_secret": "${OAUTH_CLIENT_SECRET}",
"scopes": "openid email profile",
"client_secret_basic_override": true,
"mcp_server_source": "REGISTRY_MCP",
"registry_mcp_server_name": "${MCP_REGISTRY_URI}",
"mcp_agent_instructions": "MANDATORY SPROCKETS INVENTORY & CATALOG AGENT: Always invoke get_sprockets or get_sprocket_by_id for any question about sprockets, inventory catalog listings, sprocket IDs (SPK-101 through SPK-105), ANSI models (ANSI-40, ANSI-60, ANSI-80), tooth counts, pitch, material, stock levels, unit prices, authenticated_user, or egress_source_ip verification.",
"use_agent_gateway_egress": true,
"agent_gateway_engine": "projects/${PROJ_ID}/locations/global/collections/default_collection/engines/${GE_APP_ID}"
}
}
}
EOF
אימות של קישורים לשרתי MCP בהתאמה אישית
# show engine (ge app) details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
| jq '{name: .name, dataStoreIds: .dataStoreIds, agentGatewaySetting: .agentGatewaySetting}'
בודקים את מאגר הנתונים המקושר "dataStoreIds": ["mcp-sprockets-collection-[EPOCH]_mcp_data"].
# show collection details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
| jq --arg app "${GE_APP_ID}" '.collections[] | select(.dataConnector.actionConfig.actionParams.agent_gateway_engine // "" | endswith($app)) | .dataConnector | {name: .name, state: .state, actionState: .actionState, connectorModes: .connectorModes, auth_type: .actionConfig.actionParams.auth_type}'
בודקים אם יש "state": "ACTIVE" ו"auth_type": "OAUTH".
פעולות שקשורות לכלים
כשבודקים את mcp-sprockets-collection מאגר הנתונים בלוח הבקרה של Gemini Enterprise, אפשר לראות שלא נעשה שימוש בכרטיסייה פעולות והלחצן ↻ טעינה מחדש של פעולות מותאמות אישית מושבת. זו התנהגות צפויה.
צופים בדף הפרטים של מאגר הנתונים בממשק המשתמש של מסוף Google Cloud:
echo "data store details page url: https://console.cloud.google.com/gemini-enterprise/locations/${GE_LOCATION}/collections/${GE_COLLECTION_ID}/connector/details?project=${PROJ_ID}"
בהתאם לאופן שבו מקשרים שרת MCP בהתאמה אישית ל-Gemini Enterprise, גילוי הכלים והניהול שלהם מתבצעים באחת משתי דרכים:
- MCP מותאם אישית ישירות (
BYO_MCPתהליך עבודה): כשמגדירים שרת MCP מותאם אישית ישירות ב-Gemini Enterprise בלי Agent Registry, מאגר הנתונים עצמו מנהל את קטלוג הכלים (connectorModes: ["FEDERATED", "ACTIONS"]). צריך לפתוח את הכרטיסייה פעולות, ללחוץ על ↻ טעינה מחדש של פעולות מותאמות אישית כדי לאחזר את סכימתtools/list, ולהפעיל או להשבית כל כלי בנפרד (addו-subtract) בממשק המשתמש. - ייבוא מ-Agent Registry (
REGISTRY_MCPתהליך העבודה שמשמש במעבדת התכנות הזו): כשמייבאים שרת MCP מ-Agent Registry, Agent Registry משמש כמקור האמת המוסמך לנקודת הקצה של ה-MCP, למטא-נתונים של הממשק ולספריית הכלים שלו (connectorModes: ["FEDERATED"]). Gemini Enterprise מפעיל באופן אוטומטי את כלי ה-MCP הרשומים בזמן הריצה דרך ההגדרה של מנוע Agent Gateway, בלי שתצטרכו לטעון מחדש או להפעיל ולכבות פעולות באופן ידני בממשק המשתמש של מאגר הנתונים.
כאן מסתיים החלק של Gemini Enterprise… עכשיו נעבור לקטע אימות.
9. אימות
בקטע הזה, משלימים את תהליך ההסכמה מהמשתמש ב-Google OAuth 2.0, מפעילים כלי MCP בשרת החיצוני mcp-sprockets מ-Gemini Enterprise, ומוודאים ששרת ה-MCP החיצוני אימת גם את כתובת ה-IP הסטטית של Cloud NAT שהוזמנה (egress_source_ip) וגם את הזהות שלכם ב-Google OAuth 2.0 (authenticated_user).
גישת משתמש
יוצרים את כתובת ה-URL של אפליקציית האינטרנט של Gemini Enterprise:
# fetch app user url
export GE_WIDGET_ID=$(curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}/widgetConfigs/default_search_widget_config" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
-H "Content-Type: application/json" \
| jq -r '.configId')
export GE_APP_USER_URL="https://vertexaisearch.cloud.google.com/home/cid/${GE_WIDGET_ID}"
echo "app user url: ${GE_APP_USER_URL}"
השלמת ההסכמה ל-OAuth
לפני שמריצים שאילתה לסוכן, צריך להשלים תהליך חד-פעמי של מתן הסכמה ל-OAuth 2.0, שמעניק ל-Gemini Enterprise הרשאה לקבל אסימון גישה לזהות שלכם ב-Google (${USER_IDENTITY}). כך Gemini Enterprise יכול לאמת קריאות כלים יוצאות דרך Agent Gateway, כדי ששרת mcp-sprockets החיצוני יוכל לאמת את הזהות שלכם בכל בקשה.
- פותחים את
${GE_APP_USER_URL}בדפדפן ולוחצים על Get started (תחילת העבודה). - בתיבת הצ'אט, לוחצים על סמל המחברים (סמל חלקי הפאזל) ואז על אישור לצד
mcp-sprockets-collection. - ייפתח חלון חדש עם ההנחיה בחירת חשבון כדי להמשיך אל
mcp-sprockets-oauth(שם האפליקציה של מסך ההסכמה ל-OAuth שהגדרתם). בוחרים את חשבון המשתמש (${USER_IDENTITY}). - במסך ההסכמה ל-OAuth של Google, בודקים את ההרשאות הנדרשות שמאפשרות ל-Google (ספק הזהויות) לשתף את זהות המשתמש (
openid, email, profile) עםmcp-sprockets-oauth, ואז לוחצים על המשך. - לוחצים שוב על סמל המחברים ומוודאים שהמתג של
mcp-sprockets-collectionמופעל, כדי לוודא שההרשאה הצליחה.
בדיקת שאילתות של סוכנים
בדיקות של ממשק המשתמש של הצ'אט עם Assistant
שולחים את שאילתת הבדיקה הבאה בצ'אט:
look up sprocket SPK-105 and confirm the model, material, unit_price, authenticated_user, and egress_source_ip
מנסים שאילתה שנייה בצ'אט:
list all sprockets in the inventory catalog and report the authenticated_user and egress_source_ip
מוודאים שהעוזר הדיגיטלי מחזיר את התשובות הנכונות ומציג תג אינטראקטיבי של ציטוט פעולה.
לדוגמה, הסימן Sprocket Lookup SPK-105 (5s)Agent Gateway מתחת לכל תשובה מאשר שהכלי הופעל.
לראות שהעוזר הדיגיטלי מחזיר את המפרט של גלגל השיניים יחד עם "authenticated_user" התאמה ל-${USER_IDENTITY} ו"egress_source_ip" התאמה ל-${NAT_EXT_IP} שהזמנת.
בדיקות של Assistant streamAssist API
אחרי שחשבון המשתמש שלכם מאשר את מחבר OAuth 2.0, אתם יכולים גם להפעיל את default_assistant:streamAssist API ישירות מה-CLI ולוודא ששרת mcp-sprockets החיצוני מחזיר גם את כתובת ה-IP הסטטית של Cloud NAT (${NAT_EXT_IP}) וגם את הזהות המאומתת שלכם ב-Google OAuth 2.0 (${USER_IDENTITY}):
שולחים את שאילתת הבדיקה הבאה:
# query engine (ge app) streamAssist
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}/assistants/default_assistant:streamAssist" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
-H "Content-Type: application/json" \
-d @- <<EOF | jq -r '.[].answer.replies[]?.groundedContent?.content?.text // empty'
{
"query": {
"text": "look up sprocket SPK-103 and confirm the model, material, unit_price, authenticated_user, and egress_source_ip"
},
"toolsSpec": {
"vertexAiSearchSpec": {
"dataStoreSpecs": [
{
"dataStore": "projects/${PROJ_NO}/locations/${GE_LOCATION}/collections/default_collection/dataStores/${GE_COLLECTION_ID}_mcp_data"
}
]
}
}
}
EOF
פלט לדוגמה:
Here are the details for sprocket SPK-103 from the inventory catalog:
- Model: ANSI-60-16T
- Material: 304 Stainless Steel
- Unit Price: $42.00
- Authenticated User (Google OAuth 2.0): user@example.com
- Egress Source IP (Cloud NAT): 203.0.113.10
אפשר לנסות שאילתה אחרת:
# query engine (ge app) streamAssist
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}/assistants/default_assistant:streamAssist" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
-H "Content-Type: application/json" \
-d @- <<EOF | jq -r '.[].answer.replies[]?.groundedContent?.content?.text // empty'
{
"query": {
"text": "find a sprocket model i can get for under \$20 and report the details, authenticated_user, and egress_source_ip"
},
"toolsSpec": {
"vertexAiSearchSpec": {
"dataStoreSpecs": [
{
"dataStore": "projects/${PROJ_NO}/locations/${GE_LOCATION}/collections/default_collection/dataStores/${GE_COLLECTION_ID}_mcp_data"
}
]
}
}
}
EOF
עיון ביומנים ב-Cloud Logging
כדי לוודא ש-Gemini Enterprise העביר את הקריאות לכלים דרך Agent Gateway, מחבר רשת של PSC ו-Cloud NAT, בודקים את היומנים ב-Cloud Logging.
אימות יומני Agent Gateway
מוודאים ש-Agent Gateway перехватил запрос, разрешил цель в реестре агентов, оценил политику авторизации и переслал вызов инструмента:
# show agent gateway logs
gcloud logging read \
"resource.type=networkservices.googleapis.com/Gateway
AND resource.labels.location=${REGION}" \
--project=${PROJ_ID} \
--limit=8 \
--format="table(
timestamp.date('', tz=LOCAL):label=TIMESTAMP,
httpRequest.status:label=STATUS,
httpRequest.serverIp:label=SERVER_IP,
jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
jsonPayload.agentGatewayInfo.mcpInfo.parameter:label=TOOL,
jsonPayload.authzPolicyInfo.result:label=AUTHZ,
jsonPayload.agentGatewayInfo.agentRegistryResource.basename():label=REGISTRY_MCP
)"
פלט לדוגמה:
TIMESTAMP STATUS SERVER_IP MCP_METHOD TOOL AUTHZ REGISTRY_MCP
YYYY-MM-DDTHH:MM:SS.ZZZ 200 203.0.113.20:443 tools/call get_sprocket_by_id ALLOWED agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
YYYY-MM-DDTHH:MM:SS.ZZZ 200 203.0.113.20:443 tools/list ALLOWED agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
YYYY-MM-DDTHH:MM:SS.ZZZ 202 203.0.113.20:443 notifications/initialized ALLOWED agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
YYYY-MM-DDTHH:MM:SS.ZZZ 200 203.0.113.20:443 initialize ALLOWED agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
מוודאים שהפלט מכיל:
-
STATUS:200(ההרצה הסתיימה בהצלחה) ו-202(לחיצת היד שלnotifications/initialized). -
SERVER_IP: כתובת ה-IP והיציאה של שרת ה-MCP החיצוני (${MCP_EXT_IP}:443). -
MCP_METHODו-TOOL: הרצף של פרוטוקול ה-MCP (initialize,notifications/initialized,tools/listו-tools/callעםget_sprocket_by_id). -
AUTHZ:ALLOWED(יציאה מותרת עם הרשאה של IAP). REGISTRY_MCP: מזהה המשאב של Agent Registry שנפתר (agentregistry-...).
אימות יומנים של Cloud NAT
מוודאים ש-Cloud NAT תרגם את החיבור היוצא מכתובת ה-IP של מחבר הרשת של Agent Gateway PSC (10.10.10.2) לכתובת ה-IPv4 החיצונית הסטטית השמורה (${NAT_EXT_IP}) כשמתחברים אל ${MCP_EXT_IP}:443:
# show cloud nat logs
gcloud logging read \
"resource.type=nat_gateway
AND resource.labels.gateway_name=nat-${SLUG}-${REGION}
AND jsonPayload.connection.dest_ip=${MCP_EXT_IP}" \
--project=${PROJ_ID} \
--limit=8 \
--format="table(
timestamp.date('', tz=LOCAL):label=TIMESTAMP,
jsonPayload.allocation_status:label=ALLOCATION_STATUS,
jsonPayload.connection.src_ip:label=SRC_IP,
jsonPayload.connection.nat_ip:label=NAT_IP,
jsonPayload.connection.dest_ip:label=DEST_IP,
jsonPayload.connection.dest_port:label=PORT
)"
פלט לדוגמה:
TIMESTAMP ALLOCATION_STATUS SRC_IP NAT_IP DEST_IP PORT
YYYY-MM-DDTHH:MM:SS.ZZZ OK 10.10.10.2 203.0.113.10 203.0.113.20 443
אימות יומני חומת האש
מוודאים שמדיניות חומת האש הפנימית (fw-policy-${SLUG}-internal) מאפשרת תעבורת יציאה משער הסוכן ליעד באינטרנט:
# show firewall policy logs (internal)
gcloud logging read \
"logName:compute.googleapis.com%2Ffirewall
AND jsonPayload.rule_details.reference:fw-policy-${SLUG}-internal" \
--project=${PROJ_ID} \
--limit=8 \
--format="table(
timestamp.date('', tz=LOCAL):label=TIMESTAMP,
jsonPayload.connection.src_ip:label=SRC_IP,
jsonPayload.connection.dest_ip:label=DEST_IP,
jsonPayload.connection.dest_port:label=PORT,
jsonPayload.vpc.vpc_name:label=VPC_NETWORK,
jsonPayload.rule_details.reference.basename():label=POLICY,
jsonPayload.rule_details.priority:label=RULE,
jsonPayload.rule_details.direction:label=DIRECTION,
jsonPayload.disposition:label=DISPOSITION
)"
מוודאים שמדיניות חומת האש החיצונית (fw-policy-${SLUG}-external) אפשרה תעבורת ingress לשרת ה-MCP מכתובת ה-IP של ה-NAT הציבורי ברשת הפנימית:
# show firewall policy logs (external)
gcloud logging read \
"logName:compute.googleapis.com%2Ffirewall
AND jsonPayload.rule_details.reference:fw-policy-${SLUG}-external" \
--project=${PROJ_ID} \
--limit=8 \
--format="table(
timestamp.date('', tz=LOCAL):label=TIMESTAMP,
jsonPayload.connection.src_ip:label=SRC_IP,
jsonPayload.connection.dest_ip:label=DEST_IP,
jsonPayload.connection.dest_port:label=PORT,
jsonPayload.vpc.vpc_name:label=VPC_NETWORK,
jsonPayload.rule_details.reference.basename():label=POLICY,
jsonPayload.rule_details.priority:label=RULE,
jsonPayload.rule_details.direction:label=DIRECTION,
jsonPayload.disposition:label=DISPOSITION
)"
בזה מסתיים החלק של האימות. עכשיו עוברים לקטע ניקוי.
10. הסרת המשאבים
כדי למחוק את המשאבים וההגדרות שנוצרו בשיעור ה-Lab הזה, פועלים לפי השלבים הבאים.
הסרת רכיבים של Gemini Enterprise
# delete gemini enterprise engine (app)
curl -s -X DELETE "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}"
# delete custom mcp collection, data connector, and backing data store
curl -s -X DELETE "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/${GE_COLLECTION_ID}" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}"
# reset identity provider configuration
curl -s -X PATCH "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/aclConfig" \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "X-Goog-User-Project: ${PROJ_ID}" \
-H "Content-Type: application/json" \
-d '{"idpConfig":{"idpType":"IDP_TYPE_UNSPECIFIED"}}'
כדי להסיר את פרטי הכניסה של לקוח OAuth 2.0 (ge-mcp-sprockets-client), פותחים את הדף 'לקוחות של פלטפורמת האימות של Google' ולוחצים על סמל ההסרה (פח האשפה) לצד ge-mcp-sprockets-client:
# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"
הסרת שרת MCP ורכיבי Agent Registry
# delete external mcp server vm
gcloud -q compute instances delete vm-${MCP_NAME} --zone=${ZONE}
# delete agent registry mcp service
gcloud -q agent-registry services delete ${MCP_NAME} --location=${REGION}
# delete agent registry oauth2 endpoint
gcloud -q agent-registry services delete oauth2-googleapis --location=${REGION}
הסרת רכיבים של Agent Gateway
# delete gateway authz policy and iap service extension
gcloud -q network-security authz-policies delete ${AGW_NAME}-authz-policy-iap \
--location=${REGION}
gcloud -q service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap \
--location=${REGION}
# delete agent gateway and connectivity template
gcloud -q network-services agent-gateways delete ${AGW_NAME} \
--location=${REGION}
gcloud -q network-services agent-connectivity-templates delete act-${AGW_NAME} \
--location=${REGION}
# delete iam unified access policy binding and access policy
gcloud -q iam policy-bindings delete uap-bind-${SLUG} \
--location=global \
--project=${PROJ_ID}
gcloud -q iam access-policies delete uap-policy-${SLUG} \
--location=global \
--project=${PROJ_ID}
הסרת רכיבים של חומת האש
# delete network firewall policy associations
gcloud -q compute network-firewall-policies associations delete \
--name=fw-policy-bind-${SLUG}-internal \
--firewall-policy=fw-policy-${SLUG}-internal \
--global-firewall-policy
gcloud -q compute network-firewall-policies associations delete \
--name=fw-policy-bind-${SLUG}-external \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy
# delete custom firewall policy rules
gcloud -q compute network-firewall-policies rules delete 1001 \
--firewall-policy=fw-policy-${SLUG}-internal \
--global-firewall-policy
gcloud -q compute network-firewall-policies rules delete 2003 \
--firewall-policy=fw-policy-${SLUG}-external \
--global-firewall-policy
# delete network firewall policies
gcloud -q compute network-firewall-policies delete fw-policy-${SLUG}-internal \
--global
gcloud -q compute network-firewall-policies delete fw-policy-${SLUG}-external \
--global
הסרת רכיבי רשת
# delete cloud nat gateway and cloud router
gcloud -q compute routers nats delete nat-${SLUG}-${REGION} \
--router=cr-nat-${SLUG}-${REGION} \
--region=${REGION}
gcloud -q compute routers delete cr-nat-${SLUG}-${REGION} \
--region=${REGION}
# delete psc network attachment
gcloud -q compute network-attachments delete psc-na-${REGION}-agw \
--region=${REGION}
# delete subnets
gcloud -q compute networks subnets delete subnet-${REGION}-agw \
--region=${REGION}
gcloud -q compute networks subnets delete subnet-${REGION}-mcp \
--region=${REGION}
# delete reserved static external ip addresses
gcloud -q compute addresses delete ip-${SLUG}-nat ip-${SLUG}-mcp \
--region=${REGION}
# delete internal and external vpc networks
gcloud -q compute networks delete \
vnet-${SLUG}-internal \
vnet-${SLUG}-external
הסרת ביטולים של מדיניות הארגון וקבצים מקומיים
# delete project-level organization policy overrides
for policy in \
discoveryengine.managed.disableCustomMcpServerConnector \
discoveryengine.managed.allowedEgressFqdns \
discoveryengine.managed.allowedDataSources \
iam.managed.disableAccessPolicyBinding \
compute.vmExternalIpAccess; do
gcloud -q org-policies delete ${policy} --project=${PROJ_ID} 2>/dev/null || true
done
# remove local project files
rm -rf cfg
סיימנו את עבודת ניקוי הנתונים... עכשיו נעבור אל המסקנה!
11. סיכום
מעולה! יצרתם ארכיטקטורה מקצה לקצה שמאפשרת לאפליקציית Gemini Enterprise לגלות ולהפעיל כלים באופן מאובטח בשרת MCP מותאם אישית חיצוני שמוגן באמצעות OAuth 2.0 ומוגדר בו אישור של כתובות IP:
- יציאה מ-VPC עם כתובת IP סטטית (
ALL_TRAFFICו-Cloud NAT): כל תעבורת הנתונים היוצאת של Agent Gateway מנותבת דרך מחבר רשת PSC (10.10.10.2) והבקשות שמופנות לאינטרנט מתורגמות לכתובת IPv4 חיצונית סטטית שמורה (${NAT_EXT_IP}) באמצעות Cloud NAT. - שרת משאבים של OAuth 2.0 MCP ו-Agent Registry: הטמענו שרת HTTP FastMCP חיצוני שניתן להזרמה (
mcp-sprockets) שמוגן על ידי רשימת כתובות IP מותרות של חומת אש (${NAT_EXT_IP}/32) ואימות Google OAuth 2.0tokeninfo, ורשמנו את נקודת הקצה ואת סכימת הכלי (get_sprocketsו-get_sprocket_by_id) ב-Agent Registry. - שילוב של Gemini Enterprise עם OAuth 2.0 וניהול: הקצאת אפליקציית Gemini Enterprise, קישור של תנועת נתונים יוצאת של כלי ל-Agent Gateway, צירוף של שרת MCP רשום כמחבר נתונים של OAuth 2.0
REGISTRY_MCPואימות שלegress_source_ipוגם שלauthenticated_userלצד טלמטריה של Cloud Logging.

Cosmopup חושב ש-Codelabs הם מדהימים!
מה השלב הבא?
- במסמכי Gemini Enterprise Agent Platform אפשר למצוא מידע על תכונות מתקדמות ומדריכים.
- כדי לשפר את הבטיחות והאבטחה של ה-AI, אפשר להגדיר גדרות בטיחות של הגנה מוגברת על המודל ב-Agent Gateway.
- כדי לאכוף כללים עסקיים ותאימות לשאילתות בשפה טבעית, אפשר לעיין במדיניות בנושא ניהול סמנטי.
אתם מוזמנים לשלוח הערות, שאלות או תיקונים באמצעות טופס המשוב הזה.
תודה!