에이전트 템플릿과 코드 구성
에이전트 템플릿은 OSDK, Ontology MCP와 Palantir MCP의 공통 연결을 제공하고, 업무 로직을 작성할 코드 저장소를 생성합니다. 사용하는 프레임워크에 따라 모델 실행 루프와 사용자 지정 도구의 정의 방식이 달라집니다.
지원 프레임워크
| 템플릿 | 주요 API와 특징 |
|---|---|
| Claude Agent SDK | query로 실행하고 MCP 서버로 도구 제공 |
| OpenAI Agents SDK | Agent, run으로 실행하며 연결한 MCP 서버와 사용자 도구 사용 |
| Google ADK | Agent, InMemoryRunner로 실행. 템플릿의 FoundryGemini가 Gemini 요청을 Foundry 언어 모델 프록시로 전달하므로 별도의 Google API 키가 필요하지 않음 |
템플릿들은 저장소 구조, 인수 스키마, 기본 MCP 구성과 게시 절차를 공유합니다. 아래 코드에서는 선택한 프레임워크에 맞는 구현을 사용하고, 생성된 Foundry 템플릿의 인증과 모델 라우팅 구성을 유지합니다.
저장소 구조
agent/
├── index.ts
├── systemPrompt.ts
└── customTools.ts
utils/
scripts/
| 경로 | 역할 |
|---|---|
agent/index.ts | AgentArguments 입력 스키마와 runAgent 진입점 |
agent/systemPrompt.ts | 모델의 동작을 정의하는 시스템 프롬프트 |
agent/customTools.ts | 에이전트가 호출할 사용자 지정 도구 |
utils/ | OSDK 클라이언트와 게시 명령 등 공통 플랫폼 도구 |
scripts/ | 게시할 에이전트를 패키징하는 빌드 스크립트 |
사용자 지정 MCP 설정은 agent/mcps/custom.ts처럼 agent/ 아래의 별도 파일에 둡니다. 이렇게 사용자 파일에 설정을 두면 저장소 업그레이드 중에 템플릿과 충돌할 가능성을 줄일 수 있습니다.
시스템 프롬프트와 입력
agent/systemPrompt.ts에서 에이전트가 따를 지침을 수정합니다. 실행마다 전달할 값은 agent/index.ts의 AgentArguments에서 정의합니다.
import { defineInputs, t } from "@palantir/agent-templates-bundle";
export const AgentArguments = defineInputs({
additionalAgentContext: t.string().optional(),
});
스키마에 정의한 인수는 실행 시 받아들여지고 게시할 때 함수의 입력 타입으로 등록됩니다. 실습 실행 루프는 고정된 질문을 사용하며 additionalAgentContext를 질문에 추가하지 않습니다. 업무에 사용할 문맥은 생성된 템플릿의 인수 처리 방식에 맞춰 구성합니다.
OSDK와 OMCP 범위
먼저 에이전트에 Ontology SDK를 추가하고 필요한 객체 유형, 액션 유형과 질의 함수를 선택하여 SDK를 생성합니다.
import { getOntologySdkClient } from "@palantir/agent-templates-bundle";
import { $ontologyRid } from "@ontology/sdk";
const client = await getOntologySdkClient($ontologyRid);
OMCP도 같은 SDK 리소스를 바탕으로 생성됩니다. SDK의 객체 유형, 액션 유형과 질의 함수가 모델이 호출할 수 있는 도구가 됩니다. 도구의 노출 범위와 실제 클라이언트의 접근 제한을 함께 확인합니다.
MCP 설정 함수
| 함수 | 제공하는 연결 |
|---|---|
getOntologyMcpConfiguration | SDK에 포함된 온톨로지 리소스의 MCP 도구. 먼저 SDK를 추가해야 함 |
getPalantirMcpConfiguration | 플랫폼 리소스를 다루는 Palantir MCP 도구 |
getPythonTransformsMcpConfiguration | 변환 탐색과 미리보기 등 Python Transforms MCP 도구 |
각 함수는 비동기이며 실행 시 URL, 실행 명령과 인증정보를 해석합니다. 반환값을 사용하기 전에 반드시 await합니다.
| 프레임워크 | 가져오기 경로 | 반환 연결 타입 |
|---|---|---|
| Claude Agent SDK | @palantir/agent-templates-bundle/mcp/anthropic | McpHttpServerConfig 또는 McpStdioServerConfig |
| OpenAI Agents SDK | @palantir/agent-templates-bundle/mcp/openai | MCPServerStreamableHttp 또는 MCPServerStdio |
| Google ADK | @palantir/agent-templates-bundle/mcp/google | MCPToolset |
여러 연결은 Promise.all로 함께 해석할 수 있습니다. Claude에서는 반환값을 mcpServers 맵에, OpenAI에서는 Agent의 mcpServers 배열에, Google에서는 Agent의 tools 배열에 넣습니다.
Palantir MCP는 기본적으로 도구 검색을 사용하여 필요한 도구를 찾아오므로 초기 토큰 사용량을 줄일 수 있습니다. 허용된 전체 도구 목록을 처음부터 불러오려면 에이전트의 해당 MCP 구성에서 도구 검색을 해제합니다.
인스턴스 주소 해석
Palantir MCP와 Python Transforms MCP에는 Foundry 외부 접속 호스트인 FOUNDRY_EXTERNAL_HOST가 필요합니다. 에이전트 라이브러리는 자기 인증정보로 Control Panel에서 이 호스트를 실행 시 해석합니다. Marketplace로 다른 인스턴스에 설치해도 현재 환경의 호스트를 사용할 수 있습니다.
호스트를 해석하지 못하면 저장소를 생성할 때 기록된 호스트를 사용합니다. 이 대체 호스트는 getPalantirMcpConfiguration 또는 getPythonTransformsMcpConfiguration의 fallbackHost 옵션으로 지정할 수 있습니다.
실습 도구의 동작
가상의 포장 작업을 계산하는 도구입니다. 품목 수와 상자당 수량을 받아 필요한 상자 수를 계산합니다.
품목 수는 0 이상의 정수, 상자당 수량은 1 이상의 정수로 제한합니다. 계산은 ceil(품목 수 / 상자당 수량)입니다. 모든 예시에서 비민감 실습 리소스와 읽기 전용 OMCP 범위를 사용합니다.
Claude Agent SDK
agent/customTools.ts
tool은 Zod 객체 전체가 아니라 필드별 스키마를 가진 객체를 입력 스키마로 받습니다. 결과는 MCP 콘텐츠 형식으로 반환하고, 같은 프로세스의 MCP 서버에 등록합니다.
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
const calculateBoxes = tool(
"calculate_boxes",
"품목 수와 상자당 수량으로 필요한 상자 수를 계산합니다.",
{
itemCount: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
itemsPerBox: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
},
async ({ itemCount, itemsPerBox }) => ({
content: [{
type: "text",
text: JSON.stringify({ boxes: Math.ceil(itemCount / itemsPerBox) }),
}],
}),
);
export const packingServer = createSdkMcpServer({
name: "packing",
version: "1.0.0",
tools: [calculateBoxes],
});
runAgent 실행 루프
다음 구현은 OMCP 구성과 사용자 도구 서버를 연결하고 결과 스트림을 끝까지 소비합니다. 내장 도구는 비활성화하고, 명시한 MCP 도구만 사전 허용합니다.
mcp__ontology_mcp__*는 그 서버의 모든 도구를 허용하므로 읽기 전용 실습 범위로 제한한 서버에만 이 예시 설정을 적용합니다. 운영용 쓰기 도구가 있는 서버에 같은 범위를 그대로 적용하면 안 됩니다.
import { query } from "@anthropic-ai/claude-agent-sdk";
import { getOntologyMcpConfiguration } from "@palantir/agent-templates-bundle/mcp/anthropic";
import { packingServer } from "./customTools";
export async function runAgent(): Promise<void> {
const ontologyMcp = await getOntologyMcpConfiguration();
const messages = query({
prompt: "사용 가능한 온톨로지 조회 도구의 역할을 설명하고, calculate_boxes로 품목 12개를 상자당 5개씩 담을 때 필요한 상자 수를 계산하세요. 데이터는 변경하지 마세요.",
options: {
tools: [],
mcpServers: {
ontology_mcp: ontologyMcp,
packing: packingServer,
},
allowedTools: [
"mcp__ontology_mcp__*",
"mcp__packing__calculate_boxes",
],
},
});
for await (const message of messages) {
if (message.type === "result") {
if (message.subtype !== "success") {
throw new Error(`에이전트 실행 실패: ${message.subtype}`);
}
console.log(message.result);
}
}
}
OpenAI Agents SDK
agent/customTools.ts
OpenAI 도구는 parameters로 입력 스키마를 받고 execute에서 계산을 수행합니다.
import { tool } from "@openai/agents";
import { z } from "zod";
export const calculateBoxes = tool({
name: "calculate_boxes",
description: "품목 수와 상자당 수량으로 필요한 상자 수를 계산합니다.",
parameters: z.object({
itemCount: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
itemsPerBox: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
}),
execute: async ({ itemCount, itemsPerBox }) => ({
boxes: Math.ceil(itemCount / itemsPerBox),
}),
});
runAgent 실행 루프
MCP 서버를 연결한 뒤 Agent와 run을 사용합니다. 텍스트 스트림을 소비한 다음 completed까지 기다리고, 종료 시 연결을 닫습니다. 연결 과정에서 실패해도 정리 단계가 실행되도록 연결을 try 안에 둡니다.
import { Agent, run } from "@openai/agents";
import { getOntologyMcpConfiguration } from "@palantir/agent-templates-bundle/mcp/openai";
import { calculateBoxes } from "./customTools";
export async function runAgent(): Promise<void> {
const ontologyMcp = await getOntologyMcpConfiguration();
try {
await ontologyMcp.connect();
const agent = new Agent({
name: "PackingExample",
instructions: "비민감 실습 정보만 다루고 데이터를 변경하지 않습니다.",
model: "gpt-5.4",
mcpServers: [ontologyMcp],
tools: [calculateBoxes],
});
const result = await run(
agent,
"사용 가능한 온톨로지 조회 도구의 역할을 설명하고, 품목 12개를 상자당 5개씩 담을 때 필요한 상자 수를 도구로 계산하세요.",
{ stream: true },
);
for await (const text of result.toTextStream({ compatibleWithNodeStreams: true })) {
process.stdout.write(text);
}
await result.completed;
if (result.finalOutput === undefined) {
throw new Error("에이전트가 최종 결과를 완료하지 못했습니다.");
}
} finally {
await ontologyMcp.close();
}
}
Google ADK
agent/customTools.ts
Google ADK는 FunctionTool로 도구를 정의합니다. 실행 결과는 JSON으로 표현할 수 있는 객체로 반환합니다.
import { FunctionTool } from "@google/adk";
import { z } from "zod";
export const calculateBoxes = new FunctionTool({
name: "calculate_boxes",
description: "품목 수와 상자당 수량으로 필요한 상자 수를 계산합니다.",
parameters: z.object({
itemCount: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
itemsPerBox: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
}),
execute: async ({ itemCount, itemsPerBox }) => ({
boxes: Math.ceil(itemCount / itemsPerBox),
}),
});
runAgent 실행 루프
템플릿이 관리하는 foundryGemini.ts의 FoundryGemini를 사용하면 별도의 Google API 키 없이 Foundry 모델 프록시로 요청합니다.
import { Agent, InMemoryRunner, isFinalResponse, stringifyContent } from "@google/adk";
import { getOntologyMcpConfiguration } from "@palantir/agent-templates-bundle/mcp/google";
import { FoundryGemini } from "./foundryGemini";
import { calculateBoxes } from "./customTools";
export async function runAgent(): Promise<void> {
const ontologyMcp = await getOntologyMcpConfiguration();
try {
const agent = new Agent({
name: "PackingExample",
instruction: "비민감 실습 정보만 다루고 데이터를 변경하지 않습니다.",
model: new FoundryGemini({ model: "gemini-2.5-flash" }),
tools: [ontologyMcp, calculateBoxes],
});
const runner = new InMemoryRunner({ agent });
let completed = false;
for await (const event of runner.runEphemeral({
userId: "example-user",
newMessage: {
role: "user",
parts: [{ text: "사용 가능한 온톨로지 조회 도구의 역할을 설명하고, 품목 12개를 상자당 5개씩 담을 때 필요한 상자 수를 도구로 계산하세요." }],
},
})) {
if (event.errorMessage) {
throw new Error("에이전트의 모델 실행 중 오류가 발생했습니다.");
}
if (isFinalResponse(event) && event.content) {
console.log(stringifyContent(event));
completed = true;
}
}
if (!completed) throw new Error("에이전트가 최종 응답을 완료하지 못했습니다.");
} finally {
await ontologyMcp.close();
}
}
InMemoryRunner는 세션과 메모리를 프로세스 안에 보관하며, 실행 사이에 영속 저장하지 않습니다.
예시의 userId는 ADK 내부 세션을 구분하는 값이며, Foundry의 실행 사용자나 플랫폼 접근 권한을 설정하지는 않습니다.
운영 코드에 반영할 때
예시 모델 이름은 해당 인스턴스에서 활성화된 모델과 맞아야 합니다. 모델의 가용성과 지역 조건을 확인합니다.
실습 코드는 비민감 계산 결과를 출력합니다. 실제 업무에서는 모델 답변과 도구 결과에 민감한 정보가 포함될 수 있으므로 전체 응답을 무조건 로그에 남기지 않습니다. 호출자가 사용할 결과는 필요한 접근 통제가 적용된 온톨로지 객체에 기록하도록 설계합니다.
SDK는 모델 응답 스트림을 내부에서 처리하지만, 게시된 Foundry 함수는 Void를 반환하며 스트리밍이나 배치 호출을 지원하지 않습니다. 결과는 게시와 호출에 설명된 방식으로 전달합니다.