Copilot Studio에서 Snowflake 관리형 MCP 서버 연결하기
Microsoft Entra ID를 통한 위임 사용자 OAuth를 사용해 Snowflake 관리형 MCP 서버를 Copilot Studio 에이전트에 연결하는 엔드투엔드 가이드.
원문 번역 게시물 — 이 글은 The Custom Engine(Microsoft Copilot Studio CAT)의 hasharaf, missbets 원문 Wiring up a Snowflake-managed MCP server in Copilot Studio(2026-05-22)을 한글로 옮긴 것입니다. 원문 표현이 우선합니다.
저희는 최근 Snowflake 관리형 MCP 서버를 Copilot Studio 에이전트에 엔드투엔드로 연결해봤습니다. 공식 문서는 개별 구성 요소를 잘 다루지만, 이것들을 실제로 연결하려고 하면 비로소 드러나는 세부 사항들이 있습니다. manual OAuth가 필수라는 것, 리디렉션 URI 순서 함정이 있다는 것, 테스트 창에 별도의 최종 사용자 연결이 필요하다는 것, 그리고 Cortex Agent가 트라이얼 계정을 조용히 차단하는 숨은 전제 조건이라는 것입니다. 이 글은 처음부터 갖고 싶었던 가이드입니다.
아래의 모든 샘플 ID, 비밀, 호스트명, 테넌트, 이메일 주소는 플레이스홀더입니다. 모든
<PLACEHOLDER>를 본인의 값으로 교체하세요.
무엇을 만드는가
Snowflake 관리형 MCP 서버를 통해 Snowflake와 통신하는 Copilot Studio 에이전트입니다. 토큰은 위임 사용자 OAuth를 사용해 Entra ID를 통해 흐르므로, 모든 쿼리가 서비스 주체가 아닌 로그인한 사용자로 실행됩니다.
요점 정리
이 글에서 다섯 가지만 가져간다면 다음입니다:
- Snowflake 관리형 MCP는 런타임에 항상 Cortex Agent를 통해 라우팅됩니다. 계정에서 Cortex가 차단되어 있으면(트라이얼에서 흔한 경우), 도구 발견은 작동하지만 모든 호출이 실패합니다.
- Snowflake는 OAuth 동적 클라이언트 등록을 지원하지 않습니다. 처음부터 Copilot Studio에서 Manual OAuth를 사용하세요.
- 커넥터 리디렉션 URI는 MCP 도구를 만든 후에야 존재합니다. Copilot Studio가 생성한 후에 Azure에 추가합니다, 그 전이 아니라.
- 메이커 연결과 테스트 창(최종 사용자) 연결은 다른 것입니다. 둘 다 성공해야 합니다.
ALTER USER ... SET DEFAULT_SECONDARY_ROLES = ('ALL')은 대부분의 블로그 포스트가 빠뜨리는 줄입니다. 없으면session:role-any스코프가 런타임에 역할에 바인딩할 수 없습니다.
Cortex Agent: 숨은 전제 조건
Snowflake 관리형 MCP 서버는 기본 도구가 CORTEX_SEARCH_SERVICE_QUERY, GENERIC 저장 프로시저, SYSTEM_EXECUTE_SQL 중 무엇이든 런타임에 항상 Cortex Agent를 통해 도구를 호출합니다. Cortex Agent는 모든 MCP 호출의 런타임 오케스트레이터입니다.
Snowflake 계정에서 두 가지가 참이어야 합니다:
- Cortex Agent가 귀하의 Snowflake 지역에서 활성화되어 있어야 합니다.
- 계정이 Cortex Agent를 호출할 수 있어야 합니다. 표준 30일 트라이얼 계정은 Cortex가 조직 수준에서 차단되어 있습니다. 발견은 성공하지만 모든 호출이
MCP Server tool error: No tool result received calling Cortex Agent로 실패합니다.
트라이얼을 사용 중이라면 계속하기 전에 Snowflake 지원에 Cortex 활성화를 요청하거나 유료 계정으로 전환하세요. 이 글의 다른 모든 것은 여전히 작동하지만 에이전트가 실제로 답변하지 않습니다.
플레이스홀더 치트 시트
전체 가이드에서 이 플레이스홀더들을 사용합니다. 백트래킹하지 않도록 진행하면서 수집하세요.
| 플레이스홀더 | 찾는 위치 |
|---|---|
<TENANT_ID> |
Entra > 개요 > 테넌트 ID |
<TENANT_NAME> |
Entra > 개요 > 기본 도메인 |
<RESOURCE_APP_CLIENT_ID> |
리소스 앱 등록 > 개요 > 애플리케이션(클라이언트) ID |
<CLIENT_APP_CLIENT_ID> |
클라이언트 앱 등록 > 개요 > 애플리케이션(클라이언트) ID |
<CLIENT_SECRET_VALUE> |
클라이언트 앱 > 인증서 및 비밀(생성 시에만 표시) |
<SNOWFLAKE_ACCOUNT_HOST> |
Snowsight > 관리자 > 계정(<accountid>.snowflakecomputing.com 형태) |
<USER_UPN@yourtenant.onmicrosoft.com> |
최종 사용자의 Entra UPN |
1단계: Snowflake에 샘플 데이터 만들기
에이전트가 실제로 쿼리할 무언가가 필요합니다. Snowsight를 열고, 프로젝트 > 워크스페이스 > 새 SQL 파일로 이동해 실행합니다:
CREATE DATABASE IF NOT EXISTS PRODUCT_CUSTOMER_DB;
CREATE SCHEMA IF NOT EXISTS PRODUCT_CUSTOMER_DB.STORE_SCHEMA;
CREATE TABLE IF NOT EXISTS PRODUCT_CUSTOMER_DB.STORE_SCHEMA.PRODUCTS (
PRODUCT_ID INT AUTOINCREMENT PRIMARY KEY,
PRODUCT_NAME VARCHAR(255) NOT NULL,
DESCRIPTION VARCHAR(1000),
CATEGORY VARCHAR(100),
PRICE DECIMAL(10,2) NOT NULL,
STOCK_QUANTITY INT DEFAULT 0,
CREATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP(),
UPDATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);
CREATE TABLE IF NOT EXISTS PRODUCT_CUSTOMER_DB.STORE_SCHEMA.CUSTOMERS (
CUSTOMER_ID INT AUTOINCREMENT PRIMARY KEY,
FIRST_NAME VARCHAR(100) NOT NULL,
LAST_NAME VARCHAR(100) NOT NULL,
EMAIL VARCHAR(255) UNIQUE NOT NULL,
PHONE VARCHAR(20),
ADDRESS VARCHAR(500),
CITY VARCHAR(100),
STATE VARCHAR(100),
ZIP_CODE VARCHAR(20),
CREATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP(),
UPDATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);
-- 각 테이블에 수십 개의 행을 삽입합니다.
Snowsight의 소소한 특이점: 실행 버튼이 때로 커서 아래 구문만 실행합니다. 모든 구문이 실행되도록 전체 스크립트를 선택한 후 Cmd/Ctrl+Enter를 누르세요.
간단한 확인:
SELECT COUNT(*) AS PRODUCTS FROM PRODUCT_CUSTOMER_DB.STORE_SCHEMA.PRODUCTS;
SELECT COUNT(*) AS CUSTOMERS FROM PRODUCT_CUSTOMER_DB.STORE_SCHEMA.CUSTOMERS;
2단계: Cortex Search 서비스와 MCP 서버 만들기
Snowflake가 무거운 작업을 합니다. 검색 가능한 테이블마다 Cortex Search Service를 하나씩 만들고, 두 서비스를 LLM이 읽을 도구 사양을 가진 단일 MCP 서버로 래핑합니다.
CREATE OR REPLACE CORTEX SEARCH SERVICE PRODUCT_CUSTOMER_DB.STORE_SCHEMA.CUSTOMER_SEARCH
ON customer_info
ATTRIBUTES CITY, STATE
WAREHOUSE = COMPUTE_WH
TARGET_LAG = '1 hour'
AS (
SELECT
CUSTOMER_ID,
FIRST_NAME || ' ' || LAST_NAME || ' - ' || EMAIL || ' - ' || CITY || ', ' || STATE AS customer_info,
FIRST_NAME, LAST_NAME, EMAIL, CITY, STATE
FROM PRODUCT_CUSTOMER_DB.STORE_SCHEMA.CUSTOMERS
);
CREATE OR REPLACE CORTEX SEARCH SERVICE PRODUCT_CUSTOMER_DB.STORE_SCHEMA.PRODUCT_SEARCH
ON product_info
ATTRIBUTES CATEGORY
WAREHOUSE = COMPUTE_WH
TARGET_LAG = '1 hour'
AS (
SELECT
PRODUCT_ID,
PRODUCT_NAME || ' - ' || CATEGORY AS product_info,
PRODUCT_NAME, CATEGORY, PRICE, STOCK_QUANTITY
FROM PRODUCT_CUSTOMER_DB.STORE_SCHEMA.PRODUCTS
);
CREATE OR REPLACE MCP SERVER PRODUCT_CUSTOMER_DB.STORE_SCHEMA.MY_MCP_SERVER
FROM SPECIFICATION $$
tools:
- name: "customer_search"
type: "CORTEX_SEARCH_SERVICE_QUERY"
identifier: "PRODUCT_CUSTOMER_DB.STORE_SCHEMA.CUSTOMER_SEARCH"
title: "Customer Search"
description: "Search customers by name, email, city, or state."
- name: "product_search"
type: "CORTEX_SEARCH_SERVICE_QUERY"
identifier: "PRODUCT_CUSTOMER_DB.STORE_SCHEMA.PRODUCT_SEARCH"
title: "Product Search"
description: "Search products by name or category."
$$;
DESCRIBE MCP SERVER PRODUCT_CUSTOMER_DB.STORE_SCHEMA.MY_MCP_SERVER;
3단계: Snowflake 사용자를 Entra ID에 매핑하기
올바른 권한을 가진 역할과 LOGIN_NAME이 Entra UPN과 일치하는 Snowflake 사용자가 필요합니다. 나중에 설정하는 EXTERNAL_OAUTH 통합이 들어오는 upn 클레임을 해당 LOGIN_NAME에 매핑하므로 두 값이 정확히 일치해야 합니다(대소문자 무시).
CREATE ROLE IF NOT EXISTS SALESPROFESSIONAL;
GRANT USAGE ON DATABASE PRODUCT_CUSTOMER_DB TO ROLE SALESPROFESSIONAL;
GRANT USAGE ON SCHEMA PRODUCT_CUSTOMER_DB.STORE_SCHEMA TO ROLE SALESPROFESSIONAL;
GRANT USAGE ON CORTEX SEARCH SERVICE PRODUCT_CUSTOMER_DB.STORE_SCHEMA.CUSTOMER_SEARCH TO ROLE SALESPROFESSIONAL;
GRANT USAGE ON CORTEX SEARCH SERVICE PRODUCT_CUSTOMER_DB.STORE_SCHEMA.PRODUCT_SEARCH TO ROLE SALESPROFESSIONAL;
GRANT USAGE ON MCP SERVER PRODUCT_CUSTOMER_DB.STORE_SCHEMA.MY_MCP_SERVER TO ROLE SALESPROFESSIONAL;
GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE SALESPROFESSIONAL;
이제 위임 사용자:
CREATE USER IF NOT EXISTS SNOWSQL_DELEGATE_USER
LOGIN_NAME = '<USER_UPN@yourtenant.onmicrosoft.com>'
DISPLAY_NAME = 'SnowSQL Delegated User'
COMMENT = 'Delegate user for SnowSQL/MCP OAuth-based connectivity';
GRANT ROLE SALESPROFESSIONAL TO USER SNOWSQL_DELEGATE_USER;
-- OAuth 스코프가 session:role-any일 때 선택 사항(any-role 모드는 아래 보조 역할로 역할을 해결)
ALTER USER SNOWSQL_DELEGATE_USER SET DEFAULT_ROLE = SALESPROFESSIONAL;
ALTER USER SNOWSQL_DELEGATE_USER SET DEFAULT_WAREHOUSE = COMPUTE_WH;
-- OAuth 스코프가 session:role-any일 때 필수
ALTER USER SNOWSQL_DELEGATE_USER SET DEFAULT_SECONDARY_ROLES = ('ALL');
SHOW GRANTS TO USER SNOWSQL_DELEGATE_USER;
SHOW GRANTS TO ROLE SALESPROFESSIONAL;
마지막 ALTER USER 줄이 계속 빠뜨리던 것입니다. session:role-any 스코프에서 Snowflake는 세션 시작 시 보조 역할 해결을 통해 역할을 활성화하며, 이 해결은 DEFAULT_SECONDARY_ROLES가 ('ALL')로 설정된 경우에만 작동합니다.
4단계: 두 개의 Entra 앱 등록 만들기
Entra 테넌트에서 두 개의 앱 등록이 필요합니다. Snowflake의 공식 가이드를 끝까지 따르세요:
리소스 앱: Snowflake OAuth Resource
- API 노출 아래에서 애플리케이션 ID URI를
api://<RESOURCE_APP_CLIENT_ID>로 설정합니다. session:role-any라는 위임 스코프를 추가합니다. 에이전트를 특정 Snowflake 역할에 고정하려면 대신 더 좁은 스코프를 사용하세요.
클라이언트 앱: Snowflake OAuth Client
- 인증서 및 비밀 아래에서 클라이언트 비밀을 만들고 즉시 값을 복사하세요. 한 번만 표시됩니다.
- API 권한 아래에서 권한 추가를 클릭하고, 내 API를 선택한 후 리소스 앱을 선택하고
session:role-any위임 권한을 선택합니다. <TENANT_NAME>에 대한 관리자 동의 부여를 클릭하세요.
리디렉션 URI는 7단계에서 Copilot Studio가 생성한 후 같은 클라이언트 앱에 추가합니다. 지금은 건너뛰세요.
5단계: Snowflake에서 Entra 발급 토큰 신뢰하도록 설정
EXTERNAL_OAUTH 보안 통합은 Snowflake에게 액세스 토큰을 유효성 검사하는 방법과 upn 클레임을 Snowflake 사용자에게 매핑하는 방법을 알려줍니다.
USE ROLE ACCOUNTADMIN;
CREATE OR REPLACE SECURITY INTEGRATION external_oauth_azure_1
TYPE = EXTERNAL_OAUTH
ENABLED = TRUE
EXTERNAL_OAUTH_TYPE = AZURE
EXTERNAL_OAUTH_ISSUER = 'https://sts.windows.net/<TENANT_ID>/'
EXTERNAL_OAUTH_JWS_KEYS_URL = 'https://login.microsoftonline.com/<TENANT_ID>/discovery/v2.0/keys'
EXTERNAL_OAUTH_AUDIENCE_LIST = ('api://<RESOURCE_APP_CLIENT_ID>')
EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM = 'upn'
EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE = 'LOGIN_NAME'
EXTERNAL_OAUTH_ANY_ROLE_MODE = ENABLE; -- 스코프가 session:role-any일 때 필수
DESCRIBE INTEGRATION external_oauth_azure_1;
6단계: Copilot Studio에서 에이전트와 MCP 도구 만들기
- Copilot Studio로 이동한 후 에이전트 > 에이전트 만들기를 클릭합니다. Snowflake Sales Helper 같은 이름과 짧은 설명을 입력합니다.
- 에이전트를 열고 도구 탭으로 이동한 후 도구 추가 > MCP > 새 MCP 추가를 클릭합니다.
- MCP 폼을 작성합니다:
- 이름: Snowflake MCP 같은 이름.
- 설명: 짧고 최종 사용자 친화적인 문장.
- 서버 URL:
https://<SNOWFLAKE_ACCOUNT_HOST>/api/v2/databases/PRODUCT_CUSTOMER_DB/schemas/STORE_SCHEMA/mcp-servers/MY_MCP_SERVER여기서
<SNOWFLAKE_ACCOUNT_HOST>는<accountid>.snowflakecomputing.com형태입니다. 후행 슬래시 없음,/sse없음,/mcp없음. - 인증: OAuth 2.0.
- 동적 검색(Dynamic Discovery)에서 수동(Manual)으로 전환. Snowflake는 OAuth 동적 클라이언트 등록을 지원하지 않아 동적 검색은 조용히 실패합니다.
-
수동 OAuth 필드를 작성합니다:
필드 값 클라이언트 ID <CLIENT_APP_CLIENT_ID>클라이언트 비밀 <CLIENT_SECRET_VALUE>권한 부여 URL https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/authorize토큰 URL https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token새로 고침 URL https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token스코프 api://<RESOURCE_APP_CLIENT_ID>/session:role-any offline_access - 만들기를 클릭합니다.