> Discover all available pages from the documentation index: https://mastra.zisheng.pro/ko/llms.txt # MCP클라이언트 그만큼`MCPClient`클래스는 Mastra 애플리케이션에서 여러 MCP 서버 연결과 해당 Tool을 관리하는 방법을 제공합니다. 연결 수명주기, Tool 네임스페이스 지정을 처리하고 구성된 모든 서버의 Tool에 대한 액세스를 제공합니다. ## 건설자 MCPClient 클래스의 새 인스턴스를 만듭니다. ```typescript constructor({ id?: string; servers: Record; timeout?: number; }: MCPClientOptions) ``` ### MCPClient옵션 **id** (`string`): 구성 인스턴스의 선택적 고유 식별자입니다. 동일한 구성으로 여러 인스턴스를 생성할 때 메모리 누수를 방지하려면 이 값을 사용하세요. **servers** (`Record`): 서버 구성의 맵으로, 각 키는 고유한 서버 식별자이고 값은 서버 구성입니다. **timeout** (`number`): 개별 서버 구성에서 재정의하지 않는 한 모든 서버에 적용되는 전역 시간 제한 값(밀리초)입니다. (Default: `60000`) ### `MastraMCPServerDefinition` `servers` 맵의 각 서버는 `MastraMCPServerDefinition` 타입을 사용하여 구성합니다. transport 타입은 제공된 매개변수에 따라 감지됩니다. - `command`가 제공되면 Stdio transport를 사용합니다. - `url`이 제공되면 먼저 Streamable HTTP transport 사용을 시도하고, 초기 연결에 실패하면 레거시 SSE transport로 대체합니다. **command** (`string`): Stdio 서버: 실행할 명령입니다. **args** (`string[]`): Stdio 서버: 명령에 전달할 인수입니다. **env** (`Record`): Stdio 서버: 명령에 설정할 환경 변수입니다. **inheritDefaultEnv** (`boolean`): Stdio 서버: 하위 프로세스 환경이 MCP SDK에서 기본으로 상속하는 환경으로 시작할지를 지정합니다. 기본값은 전체 프로세스 환경이 아니라 선별된 허용 목록입니다. POSIX에서는 HOME, LOGNAME, PATH, SHELL, TERM, USER를 상속하고, Windows에서는 APPDATA, HOMEDRIVE, HOMEPATH, LOCALAPPDATA, PATH, PROCESSOR\_ARCHITECTURE, SYSTEMDRIVE, SYSTEMROOT, TEMP, USERNAME, USERPROFILE을 상속합니다. false로 설정하면 env에 명시적으로 나열된 변수만 하위 프로세스에 전달됩니다. PATH가 없는 하위 프로세스에서는 절대 경로가 아닌 명령을 생성하지 못할 수 있습니다. (Default: `true`) **url** (`URL`): HTTP 서버(Streamable HTTP 또는 SSE): 서버의 URL입니다. **requestInit** (`RequestInit`): HTTP 서버: fetch API의 요청 구성입니다. **eventSourceInit** (`EventSourceInit`): SSE 대체 연결: SSE 연결을 위한 사용자 지정 fetch 구성입니다. SSE에서 사용자 지정 헤더를 사용할 때 필요합니다. **fetch** (`MastraFetchLike`): HTTP 서버: 모든 네트워크 요청에 사용되는 사용자 지정 fetch 구현입니다. 들어오는 요청에서 요청 범위 데이터(예: 인증 쿠키, bearer token)를 포함하는 선택적 세 번째 requestContext 매개변수를 받습니다. 제공하면 이 함수가 모든 HTTP 요청에 사용되므로 동적 인증 헤더를 추가하고, 요청 범위 자격 증명을 MCP 서버에 전달하고, 요청별 동작을 사용자 지정하거나, 요청/응답을 가로채 수정할 수 있습니다. fetch를 제공하면 사용자 지정 fetch 함수 내에서 이러한 사항을 처리할 수 있으므로 requestInit, eventSourceInit, authProvider는 선택 사항이 됩니다. **allowedHosts** (`string[]`): HTTP 서버: 클라이언트가 이 서버를 대신해 연결할 수 있도록 명시적으로 허용한 호스트 목록입니다. 각 항목은 URL 호스트(URL에 기본값이 아닌 포트가 있으면 호스트 이름과 포트)에 대해 일치 여부를 확인합니다. 예: "api.example.com" 또는 "localhost:8080". 호스트 이름은 대소문자를 구분하지 않고 정확히 일치해야 하며 와일드카드는 지원되지 않고 URL 스킴은 검사하지 않습니다. 빈 배열은 모든 요청을 거부합니다. 설정하지 않으면 제한이 적용되지 않습니다. 적용에 관한 자세한 내용은 아래 보안 섹션을 참조하세요. **logger** (`LogHandler`): 로깅을 위한 선택적 추가 핸들러입니다. **timeout** (`number`): 서버별 시간 제한(밀리초)입니다. **capabilities** (`ClientCapabilities`): 서버별 기능 구성입니다. **authProvider** (`OAuthClientProvider`): HTTP 서버: 자동 토큰 갱신 및 OAuth 흐름 관리를 위한 OAuth 인증 Provider입니다. 바로 사용할 수 있는 구현이 필요하면 MCPOAuthClientProvider를 사용하세요. **enableServerLogs** (`boolean`): 이 서버의 로깅을 활성화할지 여부입니다. (Default: `true`) **forwardInstructions** (`boolean`): Agent가 이 서버의 Tool을 사용할 때 해당 MCP 서버가 알린 지침을 Agent의 시스템 Prompt에 추가할지 여부입니다. 기본적으로 비활성화되어 있습니다. 지침이 Agent의 시스템 Prompt에 삽입되므로 신뢰하는 서버에만 활성화하세요. (Default: `false`) **instructionsMaxLength** (`number`): Agent의 시스템 Prompt에 추가할 서버 지침의 최대 문자 수입니다. (Default: `512`) **requireToolApproval** (`boolean | (params: RequireToolApprovalContext) => boolean | Promise`): 이 서버의 Tool을 실행하기 전에 사람의 승인을 요구합니다. true로 설정하면 모든 Tool에 승인이 필요합니다. 함수로 설정하면 Tool 이름, 인수, 요청 컨텍스트 및 서버가 알린 Tool 주석과 함께 함수가 호출되어 승인 필요 여부를 동적으로 결정합니다. ## Tool 승인 해당 서버의 Tool을 실행하기 전에 사람의 승인을 받도록 하려면 서버 정의에서 `requireToolApproval`을 사용하세요. 이 옵션은 기존 [Human-in-the-Loop](https://mastra.zisheng.pro/ko/docs/workflows/human-in-the-loop) 승인 흐름과 함께 작동합니다. ### 모든 Tool에 승인 요구하기 서버의 모든 Tool에 승인을 요구하려면 `requireToolApproval`을 `true`로 설정하세요. ```typescript const mcp = new MCPClient({ servers: { github: { url: new URL('http://localhost:3000/mcp'), requireToolApproval: true, }, }, }) ``` ### 기능을 사용한 동적 승인 승인이 필요한지 여부를 호출별로 결정하는 함수를 전달합니다. 함수는 Tool 이름, Model이 전달한 인수, 들어오는 요청의 요청 컨텍스트, 그리고 서버에서 알린 경우 Tool의 MCP `annotations`를 받습니다. ```typescript const mcp = new MCPClient({ servers: { github: { url: new URL('http://localhost:3000/mcp'), requireToolApproval: ({ toolName, args, requestContext }) => { // Read-only tools don't need approval if (toolName === 'list_repos') return false // Destructive tools with force flag always need approval if (toolName === 'delete_repo') return args.force === true // Non-admin users need approval for everything else return requestContext?.userRole !== 'admin' }, }, }, }) ``` 이 함수는 비동기 함수일 수도 있습니다. 들어오는 요청의 `requestContext`를 받으므로 인증 확인이나 기타 요청별 로직에 사용할 수 있습니다. ### 신뢰할 수 있는 서버의 Tool 주석 사용 MCP 서버를 신뢰한다면 해당 서버의 [Tool 주석](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-annotations)(`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`, `title`)을 승인 결정에 활용할 수 있습니다. ```typescript const mcp = new MCPClient({ servers: { github: { url: new URL('http://localhost:3000/mcp'), requireToolApproval: ({ annotations }) => { // Skip approval for tools the server has marked read-only if (annotations?.readOnlyHint) return false // Always require approval for destructive tools if (annotations?.destructiveHint) return true return true }, }, }, }) ``` MCP 사양에 따라 **클라이언트는 신뢰할 수 있는 서버에서 제공된 경우가 아니면 Tool 주석을 신뢰할 수 없는 것으로 간주해야 합니다**. 주석은 권고용 힌트일 뿐이며 어떠한 보안 경계도 제공하지 않습니다. 악의적이거나 버그가 있는 서버가 실제로는 그렇지 않은 Tool을 읽기 전용이라고 주장할 수 있습니다. 신뢰하는 서버에 대해서만 주석을 사용해 승인 요구 사항을 완화하세요. 동일한 주석은 `listTools()` 및 `listToolsets()`가 반환하는 Tool의 `tool.mcp.annotations` 아래에도 노출되므로, Tool을 Agent에 연결할 때 검사할 수 있습니다. ## 서버 지침 MCP 서버가 초기화 중에 지침을 알리면 `MCPClient`가 해당 서버의 지침을 저장합니다. 이러한 지침을 Agent의 시스템 Prompt로 전달하는 기능은 **명시적으로 활성화해야 합니다**. 서버에 `forwardInstructions: true`를 설정하면 해당 서버의 Tool을 `listTools()` 또는 `listToolsets()`를 통해 사용하는 Agent가 지침을 자동으로 받습니다. 지침은 서버 이름별로 그룹화되어 다음으로 잘립니다.`instructionsMaxLength` characters per server. ```typescript const mcp = new MCPClient({ servers: { db: { url: new URL('http://localhost:3000/mcp'), forwardInstructions: true, instructionsMaxLength: 512, }, }, }) const agent = new Agent({ id: 'db-agent', name: 'DB Agent', instructions: 'Help with database changes.', model, tools: await mcp.listTools(), }) ``` `forwardInstructions`를 생략하면(기본값) 지침은 계속 캐시되어 [`getServerInstructions()`](#getserverinstructions)를 통해 확인할 수 있지만, 어떤 Agent의 시스템 Prompt에도 추가되지 않습니다. > **보안 참고 사항:**서버 지침은 Agent의 시스템 Prompt에 그대로 전달되며 길이 제한에 따른 잘림만 적용됩니다. 악의적이거나 손상된 MCP 서버는 이를 이용해 Agent가 신뢰할 수 있는 시스템 지침으로 처리할 내용을 삽입할 수 있습니다. 신뢰하는 서버에만 `forwardInstructions`를 활성화하고, 타사 서버의 지침을 전달하기 전에 `getServerInstructions()`로 검토하는 것이 좋습니다. ## 보안 ### Stdio 서버용 하위 프로세스 환경 Stdio 하위 프로세스는 전체 상위 프로세스 환경을 상속하지 않습니다. 기본적으로 하위 프로세스 환경은 MCP SDK의 선별된 화이트리스트(POSIX:`HOME`, `LOGNAME`, `PATH`, `SHELL`, `TERM`, `USER`; Windows: `APPDATA`, `HOMEDRIVE`, `HOMEPATH`, `LOCALAPPDATA`, `PATH`, `PROCESSOR_ARCHITECTURE`, `SYSTEMDRIVE`, `SYSTEMROOT`, `TEMP`, `USERNAME`, `USERPROFILE`), 설정한 변수와 병합됨`env`. API 키와 같은 민감한 변수는 명시적으로 전달하지 않는 한 상속되지 않습니다. 더 엄격하게 격리하려면 구성된 `env` 항목만 하위 프로세스에 전달되도록 `inheritDefaultEnv: false`를 설정하세요. ```typescript const mcp = new MCPClient({ servers: { myTool: { command: '/usr/local/bin/my-mcp-server', inheritDefaultEnv: false, env: { MY_TOOL_API_KEY: process.env.MY_TOOL_API_KEY! }, }, }, }) ``` `env`에 배치한 변수는 그대로 전달되므로 신뢰할 수 없는 출처에서 가져온 서버 구성(예: 사용자가 제공한 구성 파일)은 신뢰할 수 없는 입력으로 취급하세요. ### 아웃바운드 호스트 제한`allowedHosts` HTTP 서버 URL이 신뢰할 수 없는 구성에서 온 경우 공격자가 제어하는 URL이 클라이언트를 내부 서비스로 유도할 수 있습니다(서버 측 요청 위조). 이러한 서버에는 `allowedHosts`를 설정하여 클라이언트가 연결할 수 있는 호스트를 제한하세요. ```typescript const mcp = new MCPClient({ servers: { remote: { url: new URL(untrustedConfig.serverUrl), allowedHosts: ['api.example.com'], }, }, }) ``` 시행 세부정보: - 기본 fetch 경로에서는 모든 리디렉션 홉을 포함하여 허용되지 않은 호스트에 대한 요청이 전송되기 **전에** 차단됩니다. 각 홉을 검증할 수 있도록 리디렉션을 수동으로 따르며(최대 5홉), 다른 오리진으로 이동할 때는 `Authorization` 헤더를 전달하지 않습니다. 스킴, 호스트 또는 포트가 변경되면 표준 fetch 동작과 마찬가지로 헤더가 제거됩니다. - 사용자 지정 `fetch` 또는 사용자 지정 `eventSourceInit.fetch`를 제공하면 요청 전에 초기 URL은 계속 검사되지만, 리디렉션 홉은 `response.url`을 사용해 **사후에** 검증됩니다. 외부로 향하는 홉이 발생할 수 있으며, 최종 URL이 허용되지 않은 호스트를 가리키면 응답이 폐기됩니다. `response.url`이 비어 있는 직접 생성한 `Response`는 이 사후 검사를 건너뜁니다. - `authProvider`를 통해 이루어지는 OAuth 요청(인증 서버 메타데이터 검색, 토큰 교환, 갱신)도 검증됩니다. 인증 서버가 MCP 서버와 다른 호스트에서 실행된다면 해당 호스트도 `allowedHosts`에 추가하세요. - 차단된 호스트는 명확한 오류와 함께 연결에 실패하며 재연결 로직에서 재시도되지 않습니다. `allowedHosts`는 의도적으로 최소한의 기능만 제공합니다. 정확한 호스트만 일치시키며 와일드카드나 스킴 검사를 지원하지 않습니다. 더 풍부한 정책(스킴 검사, IP 범위 규칙)이 필요하면 클라이언트의 모든 요청에서 호출되는 사용자 지정 `fetch` 구현을 제공하세요. ### Tool 응답을 신뢰할 수 없는 입력으로 처리 MCP 서버가 반환한 Tool 결과는 Model 입력으로 Agent의 컨텍스트에 전달됩니다. 악의적이거나 손상된 서버는 Tool 출력을 Prompt 삽입에 사용할 수 있습니다. transport 클라이언트는 Tool 응답을 정제하지 않습니다. 정제 정책은 Agent 계층에 속하며, 여기에서 Mastra의 [입력 및 출력 프로세서](https://mastra.zisheng.pro/ko/docs/agents/processors)를 사용해 콘텐츠가 Model에 도달하기 전후에 검사, 변환 또는 차단할 수 있습니다. 타사 서버를 사용할 때는 이를 `requireToolApproval` 및 위의 `forwardInstructions` 보안 참고 사항과 함께 적용하세요. ## 행동 양식 ### `listTools()` 충돌을 방지하기 위해 서버 이름으로 네임스페이스가 지정된 Tool 이름(`serverName_toolName` 형식)을 사용하여 구성된 모든 서버에서 모든 Tool을 검색합니다. Agent 정의에 전달하기 위한 용도입니다. ```ts new Agent({ id: 'agent', tools: await mcp.listTools() }) ``` ### `listToolsWithErrors()` 서버 이름으로 네임스페이스가 지정된 Tool 이름을 사용하여 구성된 모든 서버에서 모든 Tool을 검색합니다. 또한 Tool 연결 또는 나열에 실패한 서버에 대한 서버별 오류도 반환합니다. ```typescript const { tools, errors } = await mcp.listToolsWithErrors() new Agent({ id: 'agent', tools }) console.log(errors) ``` ### `listToolsets()` 네임스페이스가 지정된 Tool 이름(`serverName.toolName` 형식)을 Tool 구현에 매핑한 객체를 반환합니다. generate 또는 stream 메서드를 호출할 때 런타임에 전달하기 위한 용도입니다. ```typescript const res = await agent.stream(prompt, { toolsets: await mcp.listToolsets(), }) ``` ### `getServerInstructions()` 구성된 각 MCP 서버에 대해 현재 알려진 지침을 반환합니다. 아직 연결되지 않았거나 지침을 광고하지 않는 서버는 반환됩니다.`undefined`. ```typescript getServerInstructions(): Record ``` 예: ```typescript await mcp.listTools() const instructionsByServer = mcp.getServerInstructions() console.log(instructionsByServer.db) ``` ### `authenticate()` 리디렉션 URL이 loopback 주소를 가리키는 `MCPOAuthClientProvider`로 구성된 서버에 대해 대화형 OAuth 인증 코드 흐름을 실행합니다. 로컬 콜백 서버를 시작하고 Provider의 `onRedirectToAuthorization` 콜백을 통해 인증 URL을 전달한 다음, 브라우저에서 인증 코드가 반환되기를 기다렸다가 코드를 토큰으로 교환하고 다시 연결합니다. [대화형 브라우저 인증](#interactive-browser-authentication)을 참조하세요. 선택 사항인 `timeoutMs`는 브라우저가 인증 코드를 반환할 때까지 흐름이 기다리는 시간을 제한하며 기본값은 5분입니다. ```typescript async authenticate(serverName: string, options?: { timeoutMs?: number }): Promise ``` ### `getServerAuthState()` 구성된 서버의 OAuth 인증 상태를 반환합니다. 연결 시도가 인증 오류로 거부된 후에는 `'needs-auth'`, 서버가 Provider의 자격 증명을 수락한 후에는 `'authorized'`, `authProvider`가 없거나 아직 연결을 시도하지 않은 서버에는 `undefined`를 반환합니다. ```typescript getServerAuthState(serverName: string): 'needs-auth' | 'authorized' | undefined ``` ### `cancelAuthentication()` 서버에서 진행 중인 `authenticate()` 흐름을 취소하여 중단된 브라우저 인증으로 인해 클라이언트가 무기한 대기하지 않도록 합니다. 콜백 서버가 바인딩되기 전의 설정 단계를 포함해 흐름을 중단하고, 수신 대기 중인 로컬 콜백 서버가 있으면 닫으며, 대기 중인 `authenticate()` 호출은 거부됩니다. 흐름이 취소되면 `true`, 진행 중인 흐름이 없으면 `false`를 반환합니다. `getServerAuthState()`의 결과는 흐름이 얼마나 진행되었는지에 따라 달라집니다. `401` 거부 후 취소된 흐름은 `'needs-auth'` 상태를 유지하며 즉시 다시 시도할 수 있습니다. 연결을 시도하지 않은 상태에서 설정 중 취소하면 상태는 변경되지 않습니다(일반적으로 `undefined`). ```typescript async cancelAuthentication(serverName: string): Promise ``` ### `disconnect()` 모든 MCP 서버와의 연결을 끊고 리소스를 정리합니다. ```typescript async disconnect(): Promise ``` ### `toMCPServerProxies()` 구성된 서버마다 하나씩 `MCPClientServerProxy` 인스턴스의 맵을 반환합니다. 각 프록시는 기본 클라이언트 연결을 `MCPServerBase` 인스턴스로 래핑하므로 외부(Mastra 이외의) MCP 서버를 `mcpServers`에 등록하고 Studio에 표시할 수 있습니다. ```typescript async toMCPServerProxies(): Promise> ``` 결과를 `Mastra`의 `mcpServers` 구성에 전달합니다. ```typescript import { Mastra } from '@mastra/core/mastra' import { MCPClient } from '@mastra/mcp' const mcpClient = new MCPClient({ servers: { 'color-mixer': { command: 'node', args: ['path/to/color-mixer-server.js'], }, }, }) export const mastra = new Mastra({ mcpServers: { ...(await mcpClient.toMCPServerProxies()), }, }) ``` 이는 MCP 앱 확장 또는 기타 기능을 구현하는 외부 MCP 서버를 Mastra에 래핑하지 않고 Studio에 연결하는 데 유용합니다.`MCPServer`. ### `resources`재산 `MCPClient` 인스턴스에는 리소스 관련 작업에 액세스할 수 있는 `resources` 속성이 있습니다. ```typescript const mcpClient = new MCPClient({/* ...servers configuration... */}) // Access resource methods via mcpClient.resources const allResourcesByServer = await mcpClient.resources.list() const templatesByServer = await mcpClient.resources.templates() // ... and so on for other resource methods. ``` #### `resources.list()` 연결된 모든 MCP 서버에서 사용 가능한 모든 리소스를 서버 이름별로 그룹화하여 검색합니다. ```typescript async list(): Promise> ``` 예: ```typescript const resourcesByServer = await mcpClient.resources.list() for (const serverName in resourcesByServer) { console.log(`Resources from ${serverName}:`, resourcesByServer[serverName]) } ``` #### `resources.templates()` 연결된 모든 MCP 서버에서 사용 가능한 모든 리소스 템플릿을 서버 이름별로 그룹화하여 검색합니다. ```typescript async templates(): Promise> ``` 예: ```typescript const templatesByServer = await mcpClient.resources.templates() for (const serverName in templatesByServer) { console.log(`Templates from ${serverName}:`, templatesByServer[serverName]) } ``` #### `resources.read(serverName: string, uri: string)` 서버에서 특정 리소스의 내용을 읽습니다. ```typescript async read(serverName: string, uri: string): Promise ``` - `serverName`: 서버의 식별자(서버에 사용되는 키)`servers` constructor option). - `uri`: 읽을 리소스의 URI입니다. 예: ```typescript const content = await mcpClient.resources.read('myWeatherServer', 'weather://current') console.log('Current weather:', content.contents[0].text) ``` #### `resources.subscribe(serverName: string, uri: string)` 서버의 특정 리소스에 대한 업데이트를 구독합니다. ```typescript async subscribe(serverName: string, uri: string): Promise ``` 예: ```typescript await mcpClient.resources.subscribe('myWeatherServer', 'weather://current') ``` #### `resources.unsubscribe(serverName: string, uri: string)` 서버의 특정 리소스에 대한 업데이트 구독을 취소합니다. ```typescript async unsubscribe(serverName: string, uri: string): Promise ``` 예: ```typescript await mcpClient.resources.unsubscribe('myWeatherServer', 'weather://current') ``` #### `resources.onUpdated(serverName: string, handler: (params: { uri: string }) => void)` 특정 서버의 구독 리소스가 업데이트될 때 호출될 알림 핸들러를 설정합니다. ```typescript async onUpdated(serverName: string, handler: (params: { uri: string }) => void): Promise ``` 예: ```typescript mcpClient.resources.onUpdated('myWeatherServer', params => { console.log(`Resource updated on myWeatherServer: ${params.uri}`) // You might want to re-fetch the resource content here // await mcpClient.resources.read("myWeatherServer", params.uri); }) ``` #### `resources.onListChanged(serverName: string, handler: () => void)` 특정 서버에서 사용 가능한 리소스 목록이 변경될 때 호출될 알림 핸들러를 설정합니다. ```typescript async onListChanged(serverName: string, handler: () => void): Promise ``` 예: ```typescript mcpClient.resources.onListChanged('myWeatherServer', () => { console.log('Resource list changed on myWeatherServer.') // You should re-fetch the list of resources // await mcpClient.resources.list(); }) ``` ### `elicitation`재산 `MCPClient` 인스턴스에는 elicitation 관련 작업에 액세스할 수 있는 `elicitation` 속성이 있습니다. elicitation을 사용하면 MCP 서버가 사용자에게 구조화된 정보를 요청할 수 있습니다. ```typescript const mcpClient = new MCPClient({/* ...servers configuration... */}) // Set up elicitation handler mcpClient.elicitation.onRequest('serverName', async request => { // Handle elicitation request from server console.log('Server requests:', request.message) console.log('Schema:', request.requestedSchema) // Return user response return { action: 'accept', content: { name: 'John Doe', email: 'john@example.com' }, } }) ``` #### `elicitation.onRequest(serverName: string, handler: ElicitationHandler)` 연결된 MCP 서버가 추출 요청을 보낼 때 호출될 핸들러 기능을 설정합니다. 핸들러는 요청을 수신하고 응답을 반환해야 합니다. ##### `ElicitationHandler`기능 핸들러 함수는 다음을 포함하는 요청 객체를 받습니다. - `message`: 어떤 정보가 필요한지 설명하는 사람이 읽을 수 있는 메시지입니다. - `requestedSchema`: 예상 응답의 구조를 정의하는 JSON 스키마 핸들러는 다음 항목이 포함된 `ElicitResult`를 반환해야 합니다. - `action`: 다음 중 하나`'accept'`, `'decline'`, or `'cancel'` - `content`: 사용자의 데이터(액션이`'accept'`) **예:** ```typescript mcpClient.elicitation.onRequest('serverName', async request => { console.log(`Server requests: ${request.message}`) // Example: Simple user input collection if (request.requestedSchema.properties.name) { // Simulate user accepting and providing data return { action: 'accept', content: { name: 'Alice Smith', email: 'alice@example.com', }, } } // Simulate user declining the request return { action: 'decline' } }) ``` **완전한 대화형 예:** ```typescript import { MCPClient } from '@mastra/mcp' import { createInterface } from 'readline' const readline = createInterface({ input: process.stdin, output: process.stdout, }) function askQuestion(question: string): Promise { return new Promise(resolve => { readline.question(question, answer => resolve(answer.trim())) }) } const mcpClient = new MCPClient({ servers: { interactiveServer: { url: new URL('http://localhost:3000/mcp'), }, }, }) // Set up interactive elicitation handler await mcpClient.elicitation.onRequest('interactiveServer', async request => { console.log(`\n📋 Server Request: ${request.message}`) console.log('Required information:') const schema = request.requestedSchema const properties = schema.properties || {} const required = schema.required || [] const content: Record = {} // Collect input for each field for (const [fieldName, fieldSchema] of Object.entries(properties)) { const field = fieldSchema as any const isRequired = required.includes(fieldName) let prompt = `${field.title || fieldName}` if (field.description) prompt += ` (${field.description})` if (isRequired) prompt += ' *required*' prompt += ': ' const answer = await askQuestion(prompt) // Handle cancellation if (answer.toLowerCase() === 'cancel') { return { action: 'cancel' } } // Validate required fields if (answer === '' && isRequired) { console.log(`❌ ${fieldName} is required`) return { action: 'decline' } } if (answer !== '') { content[fieldName] = answer } } // Confirm submission console.log('\n📝 You provided:') console.log(JSON.stringify(content, null, 2)) const confirm = await askQuestion('\nSubmit this information? (yes/no/cancel): ') if (confirm.toLowerCase() === 'yes' || confirm.toLowerCase() === 'y') { return { action: 'accept', content } } else if (confirm.toLowerCase() === 'cancel') { return { action: 'cancel' } } else { return { action: 'decline' } } }) ``` ### `prompts`재산 `MCPClient` 인스턴스에는 Prompt 관련 작업에 액세스할 수 있는 `prompts` 속성이 있습니다. ```typescript const mcpClient = new MCPClient({/* ...servers configuration... */}) // Access prompt methods via mcpClient.prompts const allPromptsByServer = await mcpClient.prompts.list() const { prompt, messages } = await mcpClient.prompts.get({ serverName: 'myWeatherServer', name: 'current', }) ``` #### `prompts.list()` 연결된 모든 MCP 서버에서 사용 가능한 모든 Prompt를 서버 이름별로 그룹화하여 검색합니다. ```typescript async list(): Promise> ``` 예: ```typescript const promptsByServer = await mcpClient.prompts.list() for (const serverName in promptsByServer) { console.log(`Prompts from ${serverName}:`, promptsByServer[serverName]) } ``` #### `prompts.get({ serverName, name, args?, version? })` 서버에서 특정 Prompt와 해당 메시지를 검색합니다. ```typescript async get({ serverName, name, args?, version?, }: { serverName: string; name: string; args?: Record; version?: string; }): Promise<{ prompt: Prompt; messages: PromptMessage[] }> ``` 예: ```typescript const { prompt, messages } = await mcpClient.prompts.get({ serverName: 'myWeatherServer', name: 'current', args: { location: 'London' }, }) console.log(prompt) console.log(messages) ``` #### `prompts.onListChanged(serverName: string, handler: () => void)` 특정 서버에서 사용 가능한 Prompt 목록이 변경될 때 호출될 알림 핸들러를 설정합니다. ```typescript async onListChanged(serverName: string, handler: () => void): Promise ``` 예: ```typescript mcpClient.prompts.onListChanged('myWeatherServer', () => { console.log('Prompt list changed on myWeatherServer.') // You should re-fetch the list of prompts // await mcpClient.prompts.list(); }) ``` ### `tools`재산 `MCPClient` 인스턴스에는 Tool 목록 변경 알림을 구독하기 위한 `tools` 속성이 있습니다. Tool을 가져오려면 `listTools()` 또는 `listToolsets()`를 사용하세요. #### `tools.onListChanged(serverName: string, handler: () => void)` 특정 서버에서 사용 가능한 Tool 목록이 변경될 때(예: 서버가 런타임에 Tool을 추가하거나 제거할 때) 호출될 알림 핸들러를 설정합니다. ```typescript async onListChanged(serverName: string, handler: () => void): Promise ``` 예: ```typescript await mcpClient.tools.onListChanged('myWeatherServer', async () => { console.log('Tool list changed on myWeatherServer.') // You should re-fetch the tools // const tools = await mcpClient.listTools(); }) ``` ### `progress`재산 `MCPClient` 인스턴스에는 Tool 실행 중 MCP 서버가 내보내는 진행 상황 알림을 구독하기 위한 `progress` 속성이 있습니다. ```typescript const mcpClient = new MCPClient({ servers: { myServer: { url: new URL('http://localhost:4111/api/mcp/myServer/mcp'), // Enabled by default; set to false to disable enableProgressTracking: true, }, }, }) // Subscribe to progress updates for a specific server await mcpClient.progress.onUpdate('myServer', params => { console.log('📊 Progress:', params.progress, '/', params.total) if (params.message) console.log('Message:', params.message) if (params.progressToken) console.log('Token:', params.progressToken) }) ``` #### `progress.onUpdate(serverName: string, handler)` 지정된 서버에서 진행 업데이트를 수신하기 위한 핸들러 함수를 등록합니다. ```typescript async onUpdate( serverName: string, handler: (params: { progressToken: string; progress: number; total?: number; message?: string; }) => void, ): Promise ``` 참고: - `enableProgressTracking`이 true(기본값)이면 Tool 호출에 `progressToken`이 포함되어 업데이트를 특정 실행과 연결할 수 있습니다. - Tool을 실행할 때 `runId`를 제공하면 이 값이 `progressToken`으로 사용됩니다. 서버의 진행 상황 추적을 비활성화하려면 다음을 수행하십시오. ```typescript const mcpClient = new MCPClient({ servers: { myServer: { url: new URL('http://localhost:4111/api/mcp/myServer/mcp'), enableProgressTracking: false, }, }, }) ``` ## 이끌어 냄 추출은 MCP 서버가 사용자에게 구조화된 정보를 요청할 수 있도록 하는 기능입니다. 서버에 추가 데이터가 필요한 경우 사용자에게 메시지를 표시하여 클라이언트가 처리하는 추출 요청을 보낼 수 있습니다. 일반적인 예는 Tool 호출 중입니다. ### 추출 작동 방식 1. **서버 요청**: MCP 서버 Tool이 메시지와 스키마를 사용해 `server.elicitation.sendRequest()`를 호출합니다. 2. **클라이언트 핸들러**: 요청과 함께 elicitation 핸들러 함수가 호출됩니다. 3. **사용자 상호 작용**: 핸들러가 UI, CLI 등을 통해 사용자 입력을 수집합니다. 4. **응답**: 핸들러가 사용자의 응답(수락/거부/취소)을 반환합니다. 5. **Tool 계속 실행**: 서버 Tool이 응답을 받고 실행을 계속합니다. ### 유도 설정 도출을 사용하는 Tool이 호출되기 전에 도출 처리기를 설정해야 합니다. ```typescript import { MCPClient } from '@mastra/mcp' const mcpClient = new MCPClient({ servers: { interactiveServer: { url: new URL('http://localhost:3000/mcp'), }, }, }) // Set up elicitation handler mcpClient.elicitation.onRequest('interactiveServer', async request => { // Handle the server's request for user input console.log(`Server needs: ${request.message}`) // Your logic to collect user input const userData = await collectUserInput(request.requestedSchema) return { action: 'accept', content: userData, } }) ``` ### 응답 유형 추출 핸들러는 다음 세 가지 응답 유형 중 하나를 반환해야 합니다. - **수용하다**: 사용자 제공 데이터 및 제출 확인 ```typescript return { action: 'accept', content: { name: 'John Doe', email: 'john@example.com' }, } ``` - **감소**: 사용자가 정보 제공을 명시적으로 거부했습니다. ```typescript return { action: 'decline' } ``` - **취소**: 사용자가 요청을 거부하거나 취소했습니다. ```typescript return { action: 'cancel' } ``` ### 스키마 기반 입력 컬렉션 `requestedSchema`는 서버에 필요한 데이터의 구조를 제공합니다. ```typescript await mcpClient.elicitation.onRequest('interactiveServer', async request => { const { properties, required = [] } = request.requestedSchema const content: Record = {} for (const [fieldName, fieldSchema] of Object.entries(properties || {})) { const field = fieldSchema as any const isRequired = required.includes(fieldName) // Collect input based on field type and requirements const value = await promptUser({ name: fieldName, title: field.title, description: field.description, type: field.type, required: isRequired, format: field.format, enum: field.enum, }) if (value !== null) { content[fieldName] = value } } return { action: 'accept', content } }) ``` ### 모범 사례 - **항상 유도를 처리하세요.**: 추출을 사용할 수 있는 Tool을 호출하기 전에 처리기를 설정합니다. - **입력 검증**: 필수 입력 항목이 입력되어 있는지 확인하세요. - **사용자 선택 존중**: 거부 및 응답 취소를 적절하게 처리합니다. - **명확한 UI**: 어떤 정보를 요청하고 있으며 그 이유를 명확하게 명시하세요. - **보안**: 민감한 정보에 대한 요청은 자동으로 수락하지 않습니다. ## OAuth 인증 [MCP 인증 사양](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)에 따라 OAuth 인증이 필요한 MCP 서버에 연결하려면 `MCPOAuthClientProvider`를 사용하세요. ```typescript import { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp' // Create an OAuth provider const oauthProvider = new MCPOAuthClientProvider({ redirectUrl: 'http://localhost:3000/oauth/callback', clientMetadata: { redirect_uris: ['http://localhost:3000/oauth/callback'], client_name: 'My MCP Client', grant_types: ['authorization_code', 'refresh_token'], response_types: ['code'], }, onRedirectToAuthorization: url => { // Handle authorization redirect (open browser, redirect response, etc.) console.log(`Please visit: ${url}`) }, }) // Use the provider with MCPClient const client = new MCPClient({ servers: { protectedServer: { url: new URL('https://mcp.example.com/mcp'), authProvider: oauthProvider, }, }, }) ``` 각 서버에 고유한 `MCPOAuthClientProvider` 인스턴스를 할당하세요. Provider는 인증 중 서버별 세션 및 자격 증명 상태를 보유하므로 하나의 인스턴스를 여러 서버에서 공유하면 각 서버의 흐름이 서로를 덮어쓸 수 있습니다. 보호된 서버를 여러 개 구성할 때는 서버마다 별도의 Provider를 생성하세요. ### 대화형 브라우저 인증 인증이 필요해 서버가 연결을 거부하면 클라이언트는 즉시 실패하는 대신 `'needs-auth'` 상태로 전환됩니다. `authenticate()`를 호출하면 흐름이 완료됩니다. Provider의 loopback 리디렉션 URL에서 일회성 콜백 서버를 시작하며, 해당 포트가 사용 중이면 다음 순차 포트로 대체합니다. 그런 다음 SDK가 런타임에 검색 및 클라이언트 등록을 수행합니다. `onRedirectToAuthorization`은 인증 URL을 수신하므로 애플리케이션에서 사용자의 브라우저로 열 수 있습니다. 브라우저가 인증 코드를 반환하면 토큰 교환이 완료됩니다. ```typescript import { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp' const oauthProvider = new MCPOAuthClientProvider({ redirectUrl: 'http://127.0.0.1:5533/oauth/callback', clientMetadata: { redirect_uris: ['http://127.0.0.1:5533/oauth/callback'], client_name: 'My MCP Client', grant_types: ['authorization_code', 'refresh_token'], response_types: ['code'], }, onRedirectToAuthorization: url => { // Open the user's browser at the consent page console.log(`Please visit: ${url}`) }, }) const mcp = new MCPClient({ servers: { protectedServer: { url: new URL('https://mcp.example.com/mcp'), authProvider: oauthProvider, }, }, }) try { await mcp.listTools() } catch { if (mcp.getServerAuthState('protectedServer') === 'needs-auth') { await mcp.authenticate('protectedServer') } } ``` 같은 서버에 대한 동시 `authenticate()` 호출은 대기 중인 흐름에 합류합니다. 서로 다른 서버는 독립적으로 인증됩니다. 저장된 토큰이 유효하면 브라우저를 열지 않고 호출이 다시 연결됩니다. 흐름 자체를 구동하는 호스트는 내보낸 `createOAuthCallbackServer` 헬퍼를 사용해 인증 코드를 캡처할 수 있습니다. 이 헬퍼는 일회성 loopback 서버를 바인딩하고 OAuth `state` 매개변수를 검증한 후 코드로 resolve됩니다. 일반 HTTP 서버를 생성하므로 로컬 loopback 리디렉션에만 사용할 수 있습니다. HTTPS 리디렉션 URL을 사용하는 웹 애플리케이션은 자체 콜백 엔드포인트를 호스팅하고 이 헬퍼 대신 Provider를 직접 구동해야 합니다. ```typescript import { createOAuthCallbackServer, getCallbackUrlCandidates } from '@mastra/mcp' // getCallbackUrlCandidates() lists every URL the helper may bind, so register // all of them as redirect_uris during client registration to cover port fallback. const redirectUris = getCallbackUrlCandidates('http://127.0.0.1:5533/oauth/callback').map(url => url.toString(), ) const server = await createOAuthCallbackServer({ redirectUrl: 'http://127.0.0.1:5533/oauth/callback', state: expectedState, }) // server.url reflects the port actually bound — use it as the redirect_uri. try { const { code } = await server.waitForCode() // Exchange the code here. } finally { await server.close() } ``` ### 빠른 토큰 제공자 테스트용이거나 이미 유효한 액세스 토큰이 있는 경우: ```typescript import { MCPClient, createSimpleTokenProvider } from '@mastra/mcp' const provider = createSimpleTokenProvider('your-access-token', { redirectUrl: 'http://localhost:3000/callback', clientMetadata: { redirect_uris: ['http://localhost:3000/callback'], client_name: 'Test Client', }, }) const client = new MCPClient({ servers: { testServer: { url: new URL('https://mcp.example.com/mcp'), authProvider: provider, }, }, }) ``` ### 맞춤형 토큰 저장소 세션 전체에 걸쳐 영구 토큰 저장을 위해 다음을 구현합니다.`OAuthStorage` interface: ```typescript import { MCPOAuthClientProvider, OAuthStorage } from '@mastra/mcp' class DatabaseOAuthStorage implements OAuthStorage { constructor( private db: Database, private userId: string, ) {} async set(key: string, value: string): Promise { await this.db.query( 'INSERT INTO oauth_tokens (user_id, key, value) VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET value = ?', [this.userId, key, value, value], ) } async get(key: string): Promise { const result = await this.db.query( 'SELECT value FROM oauth_tokens WHERE user_id = ? AND key = ?', [this.userId, key], ) return result?.[0]?.value } async delete(key: string): Promise { await this.db.query('DELETE FROM oauth_tokens WHERE user_id = ? AND key = ?', [ this.userId, key, ]) } } const provider = new MCPOAuthClientProvider({ redirectUrl: 'http://localhost:3000/callback', clientMetadata: {/* ... */}, storage: new DatabaseOAuthStorage(db, 'user-123'), }) ``` ## 예 ### 정적 Tool 구성 전체 앱에서 MCP 서버로의 단일 연결을 사용하는 Tool에는 `listTools()`를 사용하고 해당 Tool을 Agent에 전달하세요. ```typescript import { MCPClient } from '@mastra/mcp' import { Agent } from '@mastra/core/agent' const mcp = new MCPClient({ servers: { stockPrice: { command: 'npx', args: ['tsx', 'stock-price.ts'], env: { API_KEY: 'your-api-key', }, log: logMessage => { console.log(`[${logMessage.level}] ${logMessage.message}`) }, }, weather: { url: new URL('http://localhost:8080/sse'), }, }, timeout: 30000, // Global 30s timeout }) // Create an agent with access to all tools const agent = new Agent({ id: 'multi-tool-agent', name: 'Multi-tool Agent', instructions: 'You have access to multiple tool servers.', model: 'openai/gpt-5.6-sol', tools: await mcp.listTools(), }) // Example of using resource methods async function checkWeatherResource() { try { const weatherResources = await mcp.resources.list() if (weatherResources.weather && weatherResources.weather.length > 0) { const currentWeatherURI = weatherResources.weather[0].uri const weatherData = await mcp.resources.read('weather', currentWeatherURI) console.log('Weather data:', weatherData.contents[0].text) } } catch (error) { console.error('Error fetching weather resource:', error) } } checkWeatherResource() // Example of using prompt methods async function checkWeatherPrompt() { try { const weatherPrompts = await mcp.prompts.list() if (weatherPrompts.weather && weatherPrompts.weather.length > 0) { const currentWeatherPrompt = weatherPrompts.weather.find(p => p.name === 'current') if (currentWeatherPrompt) { console.log('Weather prompt:', currentWeatherPrompt) } else { console.log('Current weather prompt not found') } } } catch (error) { console.error('Error fetching weather prompt:', error) } } checkWeatherPrompt() ``` ### 동적 Tool 세트 사용자마다 새로운 MCP 연결이 필요하면 `listToolsets()`를 사용하고 stream 또는 generate를 호출할 때 Tool을 추가하세요. ```typescript import { Agent } from '@mastra/core/agent' import { MCPClient } from '@mastra/mcp' // Create the agent first, without any tools const agent = new Agent({ id: 'multi-tool-agent', name: 'Multi-tool Agent', instructions: 'You help users check stocks and weather.', model: 'openai/gpt-5.6-sol', }) // Later, configure MCP with user-specific settings const mcp = new MCPClient({ servers: { stockPrice: { command: 'npx', args: ['tsx', 'stock-price.ts'], env: { API_KEY: 'user-123-api-key', }, timeout: 20000, // Server-specific timeout }, weather: { url: new URL('http://localhost:8080/sse'), requestInit: { headers: { Authorization: `Bearer user-123-token`, }, }, }, }, }) // Pass all toolsets to stream() or generate() const response = await agent.stream('How is AAPL doing and what is the weather?', { toolsets: await mcp.listToolsets(), }) ``` ## 인스턴스 관리 `MCPClient` 클래스에는 여러 인스턴스를 관리할 때 메모리 누수를 방지하는 기능이 내장되어 있습니다. 1. 별도의 `id` 없이 동일한 구성으로 여러 인스턴스를 생성하면 메모리 누수를 방지하기 위해 오류가 발생합니다. 2. 동일한 구성의 인스턴스가 여러 개 필요하면 각 인스턴스에 고유한 `id`를 제공하세요. 3. 동일한 구성으로 인스턴스를 다시 생성하기 전에 `await configuration.disconnect()`를 호출하세요. 4. 인스턴스가 하나만 필요하면 다시 생성되지 않도록 구성을 더 상위 범위로 옮기는 것이 좋습니다. 예를 들어,`id`: ```typescript // First instance - OK const mcp1 = new MCPClient({ servers: {/* ... */}, }) // Second instance with same config - Will throw an error const mcp2 = new MCPClient({ servers: {/* ... */}, }) // To fix, either: // 1. Add unique IDs const mcp3 = new MCPClient({ id: 'instance-1', servers: {/* ... */}, }) // 2. Or disconnect before recreating await mcp1.disconnect() const mcp4 = new MCPClient({ servers: {/* ... */}, }) ``` ## 서버 수명주기 MCPClient는 서버 연결을 정상적으로 처리합니다. 1. 여러 서버에 대한 자동 연결 관리 2. 개발 중 오류 메시지를 방지하기 위한 정상적인 서버 종료 3. 연결을 끊을 때 리소스를 적절하게 정리합니다. ## 런타임 정의 인증을 위해 사용자 정의 가져오기 사용 HTTP 서버의 경우 런타임에 정의된 인증이나 요청 가로채기를 처리하도록 사용자 정의 `fetch` 함수를 제공할 수 있습니다. 이 함수로 다른 사용자 정의 동작도 처리할 수 있습니다. 요청마다 토큰을 갱신하거나 들어오는 요청의 사용자 자격 증명을 MCP 서버로 전달해야 할 때 특히 유용합니다. 사용자 정의 `fetch` 함수는 선택적 세 번째 매개변수인 `requestContext`를 받습니다. 이 매개변수를 통해 미들웨어에서 설정했거나 Agent/Tool 실행 중 전달한 요청 범위 데이터(예: 인증 쿠키, 전달자 토큰)에 액세스할 수 있습니다. 초기 연결 핸드셰이크 중에는 `requestContext`가 `null`입니다. `fetch`를 제공하면 사용자 정의 fetch 함수 내에서 관련 사항을 처리할 수 있으므로 `requestInit`, `eventSourceInit`, `authProvider`는 선택 사항이 됩니다. ```typescript const mcpClient = new MCPClient({ servers: { apiServer: { url: new URL('https://api.example.com/mcp'), fetch: async (url, init, requestContext) => { const headers = new Headers(init?.headers) // Forward auth cookie from the incoming request const cookie = requestContext?.get('cookie') if (cookie) { headers.set('cookie', cookie) } return fetch(url, { ...init, headers }) }, }, }, }) // Use with an agent — requestContext is automatically forwarded const agent = new Agent({ id: 'my-agent', name: 'My Agent', instructions: 'You are a helpful assistant.', model: openai('gpt-5.4'), tools: await mcpClient.listTools(), }) await agent.generate('Hello!', { requestContext: myRequestContext, // forwarded to the custom fetch }) ``` ## 맞춤 가져오기 내에서 인증 실패 처리 인증을 사용할 수 없을 때 사용자 정의 `fetch`에서 `throw`하면 안 됩니다. MCP SDK의 Streamable HTTP 전송은 서버가 푸시한 알림을 수신하기 위해 수명이 긴 `GET /mcp` "독립형 리스너" 스트림을 백그라운드에서 엽니다. 해당 스트림의 오류는 지수 백오프로 재시도되며, `fetch`에서 예외가 발생하거나 스트림이 정상적으로 닫히면 초당 약 한 번씩 무기한 재연결하는 루프가 발생할 수 있습니다. 대신 합성 `Response`를 반환하세요. [MCP Streamable HTTP 명세](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports)에서는 서버가 GET SSE 스트림을 제공하지 않을 때 반환하는 신호로 `405 Method Not Allowed`를 정의하며, SDK는 이를 리스너를 정상적으로 중지하는 최종 상태로 처리합니다. 서버가 알림을 푸시하지 않는 경우 이 방법으로 리스너를 비활성화하세요. 다음 패턴은 POST 요청에서 인증 토큰을 기다리고 이를 나가는 헤더에 연결한 다음 합성 405로 GET 리스너를 단락시킵니다. ```typescript async function waitForToken(timeoutMs = 5000): Promise { // Replace with your token lookup. Return null if no token is available. return getAuthToken({ timeoutMs }) } const mcpClient = new MCPClient({ servers: { apiServer: { url: new URL('https://api.example.com/mcp'), fetch: async (url, init) => { const method = (init?.method || 'GET').toUpperCase() // The SDK opens a background GET stream for server-pushed notifications. // If your server does not use it, short-circuit with 405 to stop reconnect attempts. if (method === 'GET') { return new Response(null, { status: 405, statusText: 'Method Not Allowed' }) } // POST: wait for the token, then forward the request with an Authorization header. const token = await waitForToken() if (!token) { // Forward the request without a token and let the server reject it. // The SDK surfaces non-2xx POST responses as errors to the caller of // tools/list, tools/call, etc., which is the desired behavior here. return fetch(url, init) } const headers = new Headers(init?.headers) headers.set('authorization', `Bearer ${token}`) return fetch(url, { ...init, headers }) }, }, }, }) ``` 서버가 클라이언트로 알림을 푸시하지 않는 경우에만 GET 리스너에 `405`를 반환하세요. 서버가 독립형 GET 스트림을 사용한다면 `GET` 요청에도 인증 토큰을 첨부하고 요청이 통과하도록 하세요. ## SSE 요청 헤더 사용 레거시 SSE MCP 전송을 사용하는 경우 MCP SDK의 버그로 인해 `requestInit`과 `eventSourceInit`을 모두 구성해야 합니다. 또는 POST 요청과 SSE 연결에 모두 자동으로 사용되는 사용자 정의 `fetch` 함수를 사용할 수 있습니다. ```ts // Option 1: Using requestInit and eventSourceInit (required for SSE) const sseClient = new MCPClient({ servers: { exampleServer: { url: new URL('https://your-mcp-server.com/sse'), // Note: requestInit alone isn't enough for SSE requestInit: { headers: { Authorization: 'Bearer your-token', }, }, // This is also required for SSE connections with custom headers eventSourceInit: { fetch(input: Request | URL | string, init?: RequestInit) { const headers = new Headers(init?.headers || {}) headers.set('Authorization', 'Bearer your-token') return fetch(input, { ...init, headers, }) }, }, }, }, }) // Option 2: Using custom fetch (simpler, works for both Streamable HTTP and SSE) const sseClientWithFetch = new MCPClient({ servers: { exampleServer: { url: new URL('https://your-mcp-server.com/sse'), fetch: async (url, init) => { const headers = new Headers(init?.headers || {}) headers.set('Authorization', 'Bearer your-token') return fetch(url, { ...init, headers, }) }, }, }, }) ``` ## 관련 정보 - MCP 서버를 생성하려면 다음을 참조하세요.[MCPServer documentation](https://mastra.zisheng.pro/ko/reference/tools/mcp-server). - Model 컨텍스트 프로토콜에 대한 자세한 내용은 다음을 참조하세요.[@modelcontextprotocol/sdk documentation](https://github.com/modelcontextprotocol/typescript-sdk).