도구 호출, 리소스 읽기, 프롬프트 가져오기
도구 호출, 리소스 읽기, 프롬프트 가져오기 (Call tools, read resources, get prompts)
이 페이지의 모든 코드 블록은 연결된 Client 위에서 실행돼요. 연결을 어떻게 맺는지는 서버에 연결하기 에서 다루고, 여기서는 메모리 안에서 도구 세 개, 리소스 하나, 프롬프트 하나를 등록한 orders 서버와 짝을 지어 확인해 볼게요.
도구 목록을 보고 하나 호출하기
listTools 는 서버가 광고하는 도구를 반환하고, callTool 은 평범한 arguments 객체로 이름이 있는 도구 하나를 호출합니다.
const { tools } = await client.listTools();
console.log(tools.map(tool => tool.name));
const result = await client.callTool({ name: 'lookup-order', arguments: { id: 'A-1041' } });
console.log(result.content);
result.content 는 도구 핸들러가 돌려준 content 배열을 그대로 담고 있어요.
[ 'lookup-order', 'order-total', 'export-orders' ]
[ { type: 'text', text: 'A-1041: 3 items, shipped' } ]
::: tip
실패한 도구 호출도 여전히 결과예요. content 를 믿기 전에 isError 를 확인하세요. 입력 스키마가 거부한 인자도 같은 방식으로 돌아옵니다. 오직 프로토콜 수준의 실패 — 알 수 없는 도구, 타임아웃 — 만이 throw 돼요.
:::
SDK가 페이지를 다 돌게 맡기세요
그 listTools() 는 이미 모든 페이지를 다 돌았어요. 서버가 목록을 나눠서 주면 SDK가 nextCursor 를 페이지별로 따라가며, nextCursor 가 없는 하나의 합친 목록을 돌려줍니다. listPrompts(), listResources(), listResourceTemplates() 도 같은 방식으로 합쳐요.
cursor — 여러분 애플리케이션이 잡아 둔 어떤 페이지의 nextCursor — 를 넘기면 listTools 는 정확히 그 페이지만 날것 그대로 돌려줍니다.
const page = await client.listTools({ cursor: heldCursor });
console.log(
page.tools.map(tool => tool.name),
page.nextCursor
);
orders 서버는 도구 세 개를 페이지당 두 개씩 나눠 주고, heldCursor 가 두 번째 페이지를 가리켜요 — 도구 하나, 뒤따라갈 것 없음.
[ 'export-orders' ] undefined
::: warning
ClientOptions.listMaxPages (기본 64)가 합치기 순회의 상한이에요. 페이지네이션이 끝나지 않는 서버는 코드가 LIST_PAGINATION_EXCEEDED 인 SdkError 로 호출을 거부합니다. listMaxPages: 0 으로 상한을 없앨 수 있고, 명시적인 cursor 호출은 절대 상한에 걸리지 않아요.
:::
구조화된 출력 읽기
outputSchema 를 선언한 도구는 content 옆에 structuredContent 를 돌려줍니다. 이것의 타입은 unknown 이라, 존재하는지 확인하고 쓰기 전에 좁혀야 해요.
const details = await client.callTool({ name: 'order-total', arguments: { id: 'A-1041' } });
const total: unknown = details.structuredContent;
if (typeof total === 'object' && total !== null && 'currency' in total) {
console.log(total);
}
order-total 은 { id, total, currency } 를 선언하고, 그 값이 그대로 돌아옵니다.
{ id: 'A-1041', total: 61.5, currency: 'EUR' }
이전 listTools() 가 클라이언트에게 도구의 outputSchema 를 주었다면, callTool 은 그에 맞춰 structuredContent 를 검증하고 어긋나는 결과를 거부합니다.
구조화 결과의 와이어 인코딩은 프로토콜 시대에 따라 달라요 — 프로토콜 버전 을 보세요.
리소스 읽기
listResources 는 서버가 노출하는 것을 이름으로 보여 주고, readResource 는 URI 하나를 가져옵니다.
const { resources } = await client.listResources();
console.log(resources.map(resource => resource.uri));
const { contents } = await client.readResource({ uri: 'orders://recent' });
console.log(contents[0]);
contents 의 각 항목은 uri 와 mimeType, 그리고 text 또는 base64 blob 을 담아요.
[ 'orders://recent' ]
{
uri: 'orders://recent',
mimeType: 'application/json',
text: '["A-1041","A-1042"]'
}
파라미터가 있는 URI라면 listResourceTemplates() 가 서버의 URI 템플릿을 돌려줘요. 그중 하나를 펼쳐서 나온 URI를 readResource 에 넘기면 됩니다. 리소스가 바뀌었을 때 타이머로 다시 읽는 대신 반응하고 싶다면 구독 을 보세요.
프롬프트 가져오기
listPrompts 는 각 프롬프트를 인자와 함께 광고하고, getPrompt 는 인자를 채워 넣어 모델에 보낼 준비가 된 messages 를 돌려줍니다.
const { prompts } = await client.listPrompts();
console.log(prompts.map(prompt => prompt.name));
const prompt = await client.getPrompt({ name: 'summarize-order', arguments: { id: 'A-1041', tone: 'terse' } });
console.log(prompt.messages);
서버의 템플릿이 두 인자가 모두 치환된 채로 돌아옵니다.
[ 'summarize-order' ]
[
{
role: 'user',
content: {
type: 'text',
text: 'Write a terse status update for order A-1041.'
}
}
]
인자 자동완성
complete 는 사용자가 인자를 입력하는 동안 서버에 제안을 요청해요. ref 는 프롬프트(또는 리소스 템플릿)를 가리키고, argument 는 부분 입력값을 담습니다.
const { completion } = await client.complete({
ref: { type: 'ref/prompt', name: 'summarize-order' },
argument: { name: 'tone', value: 'f' }
});
console.log(completion.values);
서버가 tone 에 대해 받아들이는 값들과 f 를 대조합니다.
[ 'formal', 'friendly' ]
긴 호출의 진행 상황 추적하기
모든 동작은 두 번째 인자로 요청 옵션을 받아요. onprogress 는 서버가 이 호출에 대해 내보내는 각 notifications/progress 를 받고, resetTimeoutOnProgress 는 업데이트가 올 때마다 요청 타임아웃을 다시 시작하며, maxTotalTimeout 은 절대 상한입니다.
const exported = await client.callTool(
{ name: 'export-orders', arguments: { format: 'csv' } },
{
onprogress: update => console.log(update),
resetTimeoutOnProgress: true,
maxTotalTimeout: 600_000
}
);
console.log(exported.content);
호출이 진행 중인 동안에도 업데이트가 흘러들어 오고, 반환 타입은 바뀌지 않아요.
{ progress: 1, total: 2, message: 'exported A-1041' }
{ progress: 2, total: 2, message: 'exported A-1042' }
[ { type: 'text', text: '2 orders exported as csv' } ]
연결 확인하기
ping 은 ping 요청을 보내고 서버가 돌려주는 빈 결과로 resolve 돼요. SDK는 양쪽 모두에서 ping 에 자동으로 응답하므로, 어느 쪽도 핸들러를 등록하지 않습니다.
const pong = await client.ping({ timeout: 5000 });
console.log(pong);
orders 서버는 즉시 응답합니다.
{}
응답을 멈춘 서버는 timeout 이 지나면 코드가 REQUEST_TIMEOUT 인 SdkError 로 호출을 거부해요.
ping 은 2025 시대 메서드예요 — 프로토콜 버전 을 보세요.
요약
listTools,listResources,listResourceTemplates,listPrompts는 모든 페이지를 합치고,{ cursor }로 단일 원본 페이지를 가져오며,listMaxPages가 순회를 제한해요.callTool은 모델용content를, 도구가outputSchema를 선언했다면 애플리케이션용structuredContent를 돌려줍니다.readResource({ uri })와getPrompt({ name, arguments })는 도구와 같은 목록-후-가져오기 모양을 따릅니다.complete()는 프롬프트나 리소스 템플릿 인자의 서버 제안을 돌려줘요.- 요청 옵션의
onprogress가 호출의 반환 타입을 바꾸지 않고 진행 업데이트를 흘려보냅니다. ping()은 서버가 여전히 응답하는지 확인하고, 양쪽 모두 ping에 자동으로 답해요.