MCP클라이언트
그만큼MCPClient클래스는 Mastra 애플리케이션에서 여러 MCP 서버 연결과 해당 Tool을 관리하는 방법을 제공합니다. 연결 수명주기, Tool 네임스페이스 지정을 처리하고 구성된 모든 서버의 Tool에 대한 액세스를 제공합니다.
건설자건설자에 대한 직접 링크
MCPClient 클래스의 새 인스턴스를 만듭니다.
constructor({
id?: string;
servers: Record<string, MastraMCPServerDefinition>;
timeout?: number;
}: MCPClientOptions)
MCPClient옵션MCPClient옵션에 대한 직접 링크
id?:
servers:
timeout?:
MastraMCPServerDefinitionmastramcpserverdefinition에 대한 직접 링크
servers 맵의 각 서버는 MastraMCPServerDefinition 타입을 사용하여 구성합니다. transport 타입은 제공된 매개변수에 따라 감지됩니다.
command가 제공되면 Stdio transport를 사용합니다.url이 제공되면 먼저 Streamable HTTP transport 사용을 시도하고, 초기 연결에 실패하면 레거시 SSE transport로 대체합니다.
command?:
args?:
env?:
inheritDefaultEnv?:
false로 설정하면 env에 명시적으로 나열된 변수만 하위 프로세스에 전달됩니다. PATH가 없는 하위 프로세스에서는 절대 경로가 아닌 명령을 생성하지 못할 수 있습니다.url?:
requestInit?:
eventSourceInit?:
fetch?:
requestContext 매개변수를 받습니다. 제공하면 이 함수가 모든 HTTP 요청에 사용되므로 동적 인증 헤더를 추가하고, 요청 범위 자격 증명을 MCP 서버에 전달하고, 요청별 동작을 사용자 지정하거나, 요청/응답을 가로채 수정할 수 있습니다. fetch를 제공하면 사용자 지정 fetch 함수 내에서 이러한 사항을 처리할 수 있으므로 requestInit, eventSourceInit, authProvider는 선택 사항이 됩니다.allowedHosts?:
"api.example.com" 또는 "localhost:8080". 호스트 이름은 대소문자를 구분하지 않고 정확히 일치해야 하며 와일드카드는 지원되지 않고 URL 스킴은 검사하지 않습니다. 빈 배열은 모든 요청을 거부합니다. 설정하지 않으면 제한이 적용되지 않습니다. 적용에 관한 자세한 내용은 아래 보안 섹션을 참조하세요.logger?:
timeout?:
capabilities?:
authProvider?:
enableServerLogs?:
forwardInstructions?:
instructionsMaxLength?:
requireToolApproval?:
true로 설정하면 모든 Tool에 승인이 필요합니다. 함수로 설정하면 Tool 이름, 인수, 요청 컨텍스트 및 서버가 알린 Tool 주석과 함께 함수가 호출되어 승인 필요 여부를 동적으로 결정합니다.Tool 승인Tool 승인에 대한 직접 링크
해당 서버의 Tool을 실행하기 전에 사람의 승인을 받도록 하려면 서버 정의에서 requireToolApproval을 사용하세요. 이 옵션은 기존 Human-in-the-Loop 승인 흐름과 함께 작동합니다.
모든 Tool에 승인 요구하기모든 Tool에 승인 요구하기에 대한 직접 링크
서버의 모든 Tool에 승인을 요구하려면 requireToolApproval을 true로 설정하세요.
const mcp = new MCPClient({
servers: {
github: {
url: new URL('http://localhost:3000/mcp'),
requireToolApproval: true,
},
},
})
기능을 사용한 동적 승인기능을 사용한 동적 승인에 대한 직접 링크
승인이 필요한지 여부를 호출별로 결정하는 함수를 전달합니다. 함수는 Tool 이름, Model이 전달한 인수, 들어오는 요청의 요청 컨텍스트, 그리고 서버에서 알린 경우 Tool의 MCP annotations를 받습니다.
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 주석 사용신뢰할 수 있는 서버의 Tool 주석 사용에 대한 직접 링크
MCP 서버를 신뢰한다면 해당 서버의 Tool 주석(readOnlyHint, destructiveHint, idempotentHint, openWorldHint, title)을 승인 결정에 활용할 수 있습니다.
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.
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()를 통해 확인할 수 있지만, 어떤 Agent의 시스템 Prompt에도 추가되지 않습니다.
보안 참고 사항:서버 지침은 Agent의 시스템 Prompt에 그대로 전달되며 길이 제한에 따른 잘림만 적용됩니다. 악의적이거나 손상된 MCP 서버는 이를 이용해 Agent가 신뢰할 수 있는 시스템 지침으로 처리할 내용을 삽입할 수 있습니다. 신뢰하는 서버에만
forwardInstructions를 활성화하고, 타사 서버의 지침을 전달하기 전에getServerInstructions()로 검토하는 것이 좋습니다.
보안보안에 대한 직접 링크
Stdio 서버용 하위 프로세스 환경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를 설정하세요.
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에 배치한 변수는 그대로 전달되므로 신뢰할 수 없는 출처에서 가져온 서버 구성(예: 사용자가 제공한 구성 파일)은 신뢰할 수 없는 입력으로 취급하세요.
아웃바운드 호스트 제한allowedHostsrestricting-outbound-hosts-with-allowedhosts에 대한 직접 링크
HTTP 서버 URL이 신뢰할 수 없는 구성에서 온 경우 공격자가 제어하는 URL이 클라이언트를 내부 서비스로 유도할 수 있습니다(서버 측 요청 위조). 이러한 서버에는 allowedHosts를 설정하여 클라이언트가 연결할 수 있는 호스트를 제한하세요.
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 응답을 신뢰할 수 없는 입력으로 처리Tool 응답을 신뢰할 수 없는 입력으로 처리에 대한 직접 링크
MCP 서버가 반환한 Tool 결과는 Model 입력으로 Agent의 컨텍스트에 전달됩니다. 악의적이거나 손상된 서버는 Tool 출력을 Prompt 삽입에 사용할 수 있습니다. transport 클라이언트는 Tool 응답을 정제하지 않습니다. 정제 정책은 Agent 계층에 속하며, 여기에서 Mastra의 입력 및 출력 프로세서를 사용해 콘텐츠가 Model에 도달하기 전후에 검사, 변환 또는 차단할 수 있습니다. 타사 서버를 사용할 때는 이를 requireToolApproval 및 위의 forwardInstructions 보안 참고 사항과 함께 적용하세요.
행동 양식행동 양식에 대한 직접 링크
listTools()listtools에 대한 직접 링크
충돌을 방지하기 위해 서버 이름으로 네임스페이스가 지정된 Tool 이름(serverName_toolName 형식)을 사용하여 구성된 모든 서버에서 모든 Tool을 검색합니다.
Agent 정의에 전달하기 위한 용도입니다.
new Agent({ id: 'agent', tools: await mcp.listTools() })
listToolsWithErrors()listtoolswitherrors에 대한 직접 링크
서버 이름으로 네임스페이스가 지정된 Tool 이름을 사용하여 구성된 모든 서버에서 모든 Tool을 검색합니다. 또한 Tool 연결 또는 나열에 실패한 서버에 대한 서버별 오류도 반환합니다.
const { tools, errors } = await mcp.listToolsWithErrors()
new Agent({ id: 'agent', tools })
console.log(errors)
listToolsets()listtoolsets에 대한 직접 링크
네임스페이스가 지정된 Tool 이름(serverName.toolName 형식)을 Tool 구현에 매핑한 객체를 반환합니다.
generate 또는 stream 메서드를 호출할 때 런타임에 전달하기 위한 용도입니다.
const res = await agent.stream(prompt, {
toolsets: await mcp.listToolsets(),
})
getServerInstructions()getserverinstructions에 대한 직접 링크
구성된 각 MCP 서버에 대해 현재 알려진 지침을 반환합니다. 아직 연결되지 않았거나 지침을 광고하지 않는 서버는 반환됩니다.undefined.
getServerInstructions(): Record<string, string | undefined>
예:
await mcp.listTools()
const instructionsByServer = mcp.getServerInstructions()
console.log(instructionsByServer.db)
authenticate()authenticate에 대한 직접 링크
리디렉션 URL이 loopback 주소를 가리키는 MCPOAuthClientProvider로 구성된 서버에 대해 대화형 OAuth 인증 코드 흐름을 실행합니다. 로컬 콜백 서버를 시작하고 Provider의 onRedirectToAuthorization 콜백을 통해 인증 URL을 전달한 다음, 브라우저에서 인증 코드가 반환되기를 기다렸다가 코드를 토큰으로 교환하고 다시 연결합니다. 대화형 브라우저 인증을 참조하세요.
선택 사항인 timeoutMs는 브라우저가 인증 코드를 반환할 때까지 흐름이 기다리는 시간을 제한하며 기본값은 5분입니다.
async authenticate(serverName: string, options?: { timeoutMs?: number }): Promise<void>
getServerAuthState()getserverauthstate에 대한 직접 링크
구성된 서버의 OAuth 인증 상태를 반환합니다. 연결 시도가 인증 오류로 거부된 후에는 'needs-auth', 서버가 Provider의 자격 증명을 수락한 후에는 'authorized', authProvider가 없거나 아직 연결을 시도하지 않은 서버에는 undefined를 반환합니다.
getServerAuthState(serverName: string): 'needs-auth' | 'authorized' | undefined
cancelAuthentication()cancelauthentication에 대한 직접 링크
서버에서 진행 중인 authenticate() 흐름을 취소하여 중단된 브라우저 인증으로 인해 클라이언트가 무기한 대기하지 않도록 합니다. 콜백 서버가 바인딩되기 전의 설정 단계를 포함해 흐름을 중단하고, 수신 대기 중인 로컬 콜백 서버가 있으면 닫으며, 대기 중인 authenticate() 호출은 거부됩니다. 흐름이 취소되면 true, 진행 중인 흐름이 없으면 false를 반환합니다.
getServerAuthState()의 결과는 흐름이 얼마나 진행되었는지에 따라 달라집니다. 401 거부 후 취소된 흐름은 'needs-auth' 상태를 유지하며 즉시 다시 시도할 수 있습니다. 연결을 시도하지 않은 상태에서 설정 중 취소하면 상태는 변경되지 않습니다(일반적으로 undefined).
async cancelAuthentication(serverName: string): Promise<boolean>
disconnect()disconnect에 대한 직접 링크
모든 MCP 서버와의 연결을 끊고 리소스를 정리합니다.
async disconnect(): Promise<void>
toMCPServerProxies()tomcpserverproxies에 대한 직접 링크
구성된 서버마다 하나씩 MCPClientServerProxy 인스턴스의 맵을 반환합니다. 각 프록시는 기본 클라이언트 연결을 MCPServerBase 인스턴스로 래핑하므로 외부(Mastra 이외의) MCP 서버를 mcpServers에 등록하고 Studio에 표시할 수 있습니다.
async toMCPServerProxies(): Promise<Record<string, MCPClientServerProxy>>
결과를 Mastra의 mcpServers 구성에 전달합니다.
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재산resources-property에 대한 직접 링크
MCPClient 인스턴스에는 리소스 관련 작업에 액세스할 수 있는 resources 속성이 있습니다.
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()resourceslist에 대한 직접 링크
연결된 모든 MCP 서버에서 사용 가능한 모든 리소스를 서버 이름별로 그룹화하여 검색합니다.
async list(): Promise<Record<string, Resource[]>>
예:
const resourcesByServer = await mcpClient.resources.list()
for (const serverName in resourcesByServer) {
console.log(`Resources from ${serverName}:`, resourcesByServer[serverName])
}
resources.templates()resourcestemplates에 대한 직접 링크
연결된 모든 MCP 서버에서 사용 가능한 모든 리소스 템플릿을 서버 이름별로 그룹화하여 검색합니다.
async templates(): Promise<Record<string, ResourceTemplate[]>>
예:
const templatesByServer = await mcpClient.resources.templates()
for (const serverName in templatesByServer) {
console.log(`Templates from ${serverName}:`, templatesByServer[serverName])
}
resources.read(serverName: string, uri: string)resourcesreadservername-string-uri-string에 대한 직접 링크
서버에서 특정 리소스의 내용을 읽습니다.
async read(serverName: string, uri: string): Promise<ReadResourceResult>
serverName: 서버의 식별자(서버에 사용되는 키)serversconstructor option).uri: 읽을 리소스의 URI입니다.
예:
const content = await mcpClient.resources.read('myWeatherServer', 'weather://current')
console.log('Current weather:', content.contents[0].text)
resources.subscribe(serverName: string, uri: string)resourcessubscribeservername-string-uri-string에 대한 직접 링크
서버의 특정 리소스에 대한 업데이트를 구독합니다.
async subscribe(serverName: string, uri: string): Promise<object>
예:
await mcpClient.resources.subscribe('myWeatherServer', 'weather://current')
resources.unsubscribe(serverName: string, uri: string)resourcesunsubscribeservername-string-uri-string에 대한 직접 링크
서버의 특정 리소스에 대한 업데이트 구독을 취소합니다.
async unsubscribe(serverName: string, uri: string): Promise<object>
예:
await mcpClient.resources.unsubscribe('myWeatherServer', 'weather://current')
resources.onUpdated(serverName: string, handler: (params: { uri: string }) => void)resourcesonupdatedservername-string-handler-params--uri-string---void에 대한 직접 링크
특정 서버의 구독 리소스가 업데이트될 때 호출될 알림 핸들러를 설정합니다.
async onUpdated(serverName: string, handler: (params: { uri: string }) => void): Promise<void>
예:
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)resourcesonlistchangedservername-string-handler---void에 대한 직접 링크
특정 서버에서 사용 가능한 리소스 목록이 변경될 때 호출될 알림 핸들러를 설정합니다.
async onListChanged(serverName: string, handler: () => void): Promise<void>
예:
mcpClient.resources.onListChanged('myWeatherServer', () => {
console.log('Resource list changed on myWeatherServer.')
// You should re-fetch the list of resources
// await mcpClient.resources.list();
})
elicitation재산elicitation-property에 대한 직접 링크
MCPClient 인스턴스에는 elicitation 관련 작업에 액세스할 수 있는 elicitation 속성이 있습니다. elicitation을 사용하면 MCP 서버가 사용자에게 구조화된 정보를 요청할 수 있습니다.
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)elicitationonrequestservername-string-handler-elicitationhandler에 대한 직접 링크
연결된 MCP 서버가 추출 요청을 보낼 때 호출될 핸들러 기능을 설정합니다. 핸들러는 요청을 수신하고 응답을 반환해야 합니다.
ElicitationHandler기능elicitationhandler-function에 대한 직접 링크
핸들러 함수는 다음을 포함하는 요청 객체를 받습니다.
message: 어떤 정보가 필요한지 설명하는 사람이 읽을 수 있는 메시지입니다.requestedSchema: 예상 응답의 구조를 정의하는 JSON 스키마
핸들러는 다음 항목이 포함된 ElicitResult를 반환해야 합니다.
action: 다음 중 하나'accept','decline', or'cancel'content: 사용자의 데이터(액션이'accept')
예:
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' }
})
완전한 대화형 예:
import { MCPClient } from '@mastra/mcp'
import { createInterface } from 'readline'
const readline = createInterface({
input: process.stdin,
output: process.stdout,
})
function askQuestion(question: string): Promise<string> {
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<string, any> = {}
// 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재산prompts-property에 대한 직접 링크
MCPClient 인스턴스에는 Prompt 관련 작업에 액세스할 수 있는 prompts 속성이 있습니다.
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()promptslist에 대한 직접 링크
연결된 모든 MCP 서버에서 사용 가능한 모든 Prompt를 서버 이름별로 그룹화하여 검색합니다.
async list(): Promise<Record<string, Prompt[]>>
예:
const promptsByServer = await mcpClient.prompts.list()
for (const serverName in promptsByServer) {
console.log(`Prompts from ${serverName}:`, promptsByServer[serverName])
}
prompts.get({ serverName, name, args?, version? })promptsget-servername-name-args-version-에 대한 직접 링크
서버에서 특정 Prompt와 해당 메시지를 검색합니다.
async get({
serverName,
name,
args?,
version?,
}: {
serverName: string;
name: string;
args?: Record<string, any>;
version?: string;
}): Promise<{ prompt: Prompt; messages: PromptMessage[] }>
예:
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)promptsonlistchangedservername-string-handler---void에 대한 직접 링크
특정 서버에서 사용 가능한 Prompt 목록이 변경될 때 호출될 알림 핸들러를 설정합니다.
async onListChanged(serverName: string, handler: () => void): Promise<void>
예:
mcpClient.prompts.onListChanged('myWeatherServer', () => {
console.log('Prompt list changed on myWeatherServer.')
// You should re-fetch the list of prompts
// await mcpClient.prompts.list();
})
tools재산tools-property에 대한 직접 링크
MCPClient 인스턴스에는 Tool 목록 변경 알림을 구독하기 위한 tools 속성이 있습니다. Tool을 가져오려면 listTools() 또는 listToolsets()를 사용하세요.
tools.onListChanged(serverName: string, handler: () => void)toolsonlistchangedservername-string-handler---void에 대한 직접 링크
특정 서버에서 사용 가능한 Tool 목록이 변경될 때(예: 서버가 런타임에 Tool을 추가하거나 제거할 때) 호출될 알림 핸들러를 설정합니다.
async onListChanged(serverName: string, handler: () => void): Promise<void>
예:
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재산progress-property에 대한 직접 링크
MCPClient 인스턴스에는 Tool 실행 중 MCP 서버가 내보내는 진행 상황 알림을 구독하기 위한 progress 속성이 있습니다.
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)progressonupdateservername-string-handler에 대한 직접 링크
지정된 서버에서 진행 업데이트를 수신하기 위한 핸들러 함수를 등록합니다.
async onUpdate(
serverName: string,
handler: (params: {
progressToken: string;
progress: number;
total?: number;
message?: string;
}) => void,
): Promise<void>
참고:
enableProgressTracking이 true(기본값)이면 Tool 호출에progressToken이 포함되어 업데이트를 특정 실행과 연결할 수 있습니다.- Tool을 실행할 때
runId를 제공하면 이 값이progressToken으로 사용됩니다. 서버의 진행 상황 추적을 비활성화하려면 다음을 수행하십시오.
const mcpClient = new MCPClient({
servers: {
myServer: {
url: new URL('http://localhost:4111/api/mcp/myServer/mcp'),
enableProgressTracking: false,
},
},
})
이끌어 냄이끌어 냄에 대한 직접 링크
추출은 MCP 서버가 사용자에게 구조화된 정보를 요청할 수 있도록 하는 기능입니다. 서버에 추가 데이터가 필요한 경우 사용자에게 메시지를 표시하여 클라이언트가 처리하는 추출 요청을 보낼 수 있습니다. 일반적인 예는 Tool 호출 중입니다.
추출 작동 방식추출 작동 방식에 대한 직접 링크
- 서버 요청: MCP 서버 Tool이 메시지와 스키마를 사용해
server.elicitation.sendRequest()를 호출합니다. - 클라이언트 핸들러: 요청과 함께 elicitation 핸들러 함수가 호출됩니다.
- 사용자 상호 작용: 핸들러가 UI, CLI 등을 통해 사용자 입력을 수집합니다.
- 응답: 핸들러가 사용자의 응답(수락/거부/취소)을 반환합니다.
- Tool 계속 실행: 서버 Tool이 응답을 받고 실행을 계속합니다.
유도 설정유도 설정에 대한 직접 링크
도출을 사용하는 Tool이 호출되기 전에 도출 처리기를 설정해야 합니다.
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,
}
})
응답 유형응답 유형에 대한 직접 링크
추출 핸들러는 다음 세 가지 응답 유형 중 하나를 반환해야 합니다.
-
수용하다: 사용자 제공 데이터 및 제출 확인
return {action: 'accept',content: { name: 'John Doe', email: 'john@example.com' },} -
감소: 사용자가 정보 제공을 명시적으로 거부했습니다.
return { action: 'decline' } -
취소: 사용자가 요청을 거부하거나 취소했습니다.
return { action: 'cancel' }
스키마 기반 입력 컬렉션스키마 기반 입력 컬렉션에 대한 직접 링크
requestedSchema는 서버에 필요한 데이터의 구조를 제공합니다.
await mcpClient.elicitation.onRequest('interactiveServer', async request => {
const { properties, required = [] } = request.requestedSchema
const content: Record<string, any> = {}
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 인증OAuth 인증에 대한 직접 링크
MCP 인증 사양에 따라 OAuth 인증이 필요한 MCP 서버에 연결하려면 MCPOAuthClientProvider를 사용하세요.
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을 수신하므로 애플리케이션에서 사용자의 브라우저로 열 수 있습니다. 브라우저가 인증 코드를 반환하면 토큰 교환이 완료됩니다.
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를 직접 구동해야 합니다.
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()
}
빠른 토큰 제공자빠른 토큰 제공자에 대한 직접 링크
테스트용이거나 이미 유효한 액세스 토큰이 있는 경우:
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:
import { MCPOAuthClientProvider, OAuthStorage } from '@mastra/mcp'
class DatabaseOAuthStorage implements OAuthStorage {
constructor(
private db: Database,
private userId: string,
) {}
async set(key: string, value: string): Promise<void> {
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<string | undefined> {
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<void> {
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 구성정적 Tool 구성에 대한 직접 링크
전체 앱에서 MCP 서버로의 단일 연결을 사용하는 Tool에는 listTools()를 사용하고 해당 Tool을 Agent에 전달하세요.
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 세트동적 Tool 세트에 대한 직접 링크
사용자마다 새로운 MCP 연결이 필요하면 listToolsets()를 사용하고 stream 또는 generate를 호출할 때 Tool을 추가하세요.
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 클래스에는 여러 인스턴스를 관리할 때 메모리 누수를 방지하는 기능이 내장되어 있습니다.
- 별도의
id없이 동일한 구성으로 여러 인스턴스를 생성하면 메모리 누수를 방지하기 위해 오류가 발생합니다. - 동일한 구성의 인스턴스가 여러 개 필요하면 각 인스턴스에 고유한
id를 제공하세요. - 동일한 구성으로 인스턴스를 다시 생성하기 전에
await configuration.disconnect()를 호출하세요. - 인스턴스가 하나만 필요하면 다시 생성되지 않도록 구성을 더 상위 범위로 옮기는 것이 좋습니다.
예를 들어,
id:
// 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는 서버 연결을 정상적으로 처리합니다.
- 여러 서버에 대한 자동 연결 관리
- 개발 중 오류 메시지를 방지하기 위한 정상적인 서버 종료
- 연결을 끊을 때 리소스를 적절하게 정리합니다.
런타임 정의 인증을 위해 사용자 정의 가져오기 사용런타임 정의 인증을 위해 사용자 정의 가져오기 사용에 대한 직접 링크
HTTP 서버의 경우 런타임에 정의된 인증이나 요청 가로채기를 처리하도록 사용자 정의 fetch 함수를 제공할 수 있습니다. 이 함수로 다른 사용자 정의 동작도 처리할 수 있습니다. 요청마다 토큰을 갱신하거나 들어오는 요청의 사용자 자격 증명을 MCP 서버로 전달해야 할 때 특히 유용합니다.
사용자 정의 fetch 함수는 선택적 세 번째 매개변수인 requestContext를 받습니다. 이 매개변수를 통해 미들웨어에서 설정했거나 Agent/Tool 실행 중 전달한 요청 범위 데이터(예: 인증 쿠키, 전달자 토큰)에 액세스할 수 있습니다. 초기 연결 핸드셰이크 중에는 requestContext가 null입니다.
fetch를 제공하면 사용자 정의 fetch 함수 내에서 관련 사항을 처리할 수 있으므로 requestInit, eventSourceInit, authProvider는 선택 사항이 됩니다.
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 명세에서는 서버가 GET SSE 스트림을 제공하지 않을 때 반환하는 신호로 405 Method Not Allowed를 정의하며, SDK는 이를 리스너를 정상적으로 중지하는 최종 상태로 처리합니다. 서버가 알림을 푸시하지 않는 경우 이 방법으로 리스너를 비활성화하세요.
다음 패턴은 POST 요청에서 인증 토큰을 기다리고 이를 나가는 헤더에 연결한 다음 합성 405로 GET 리스너를 단락시킵니다.
async function waitForToken(timeoutMs = 5000): Promise<string | null> {
// 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 요청 헤더 사용에 대한 직접 링크
레거시 SSE MCP 전송을 사용하는 경우 MCP SDK의 버그로 인해 requestInit과 eventSourceInit을 모두 구성해야 합니다. 또는 POST 요청과 SSE 연결에 모두 자동으로 사용되는 사용자 정의 fetch 함수를 사용할 수 있습니다.
// 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.
- Model 컨텍스트 프로토콜에 대한 자세한 내용은 다음을 참조하세요.@modelcontextprotocol/sdk documentation.