모델 컨텍스트 프로토콜
모델 컨텍스트 프로토콜 (Model Context Protocol, MCP)
langchain-mcp-adapters 패키지에 대한 레거시 MCP 문서입니다.
Model Context Protocol (MCP)는 애플리케이션이 LLM에 도구와 컨텍스트를 제공하는 방식을 표준화하는 개방형 프로토콜입니다. LangChain 에이전트는 @langchain/mcp-adapters 라이브러리를 사용해 MCP 서버에 정의된 도구를 사용할 수 있습니다.
퀵스타트 (Quickstart)
@langchain/mcp-adapters 라이브러리를 설치하세요:
pnpm add @langchain/mcp-adapters
yarn add @langchain/mcp-adapters
bun add @langchain/mcp-adapters
@langchain/mcp-adapters는 에이전트가 하나 이상의 MCP 서버에 정의된 도구를 사용할 수 있게 해줍니다.
import { MultiServerMCPClient } from "@langchain/mcp-adapters"; // [!code highlight]
import { ChatAnthropic } from "@langchain/anthropic";
import { createAgent } from "langchain";
const client = new MultiServerMCPClient({ // [!code highlight]
math: {
transport: "stdio", // Local subprocess communication
command: "node",
// Replace with absolute path to your math_server.js file
args: ["/path/to/math_server.js"],
},
weather: {
transport: "http", // HTTP-based remote server
// Ensure you start your weather server on port 8000
url: "http://localhost:8000/mcp",
},
});
const tools = await client.getTools(); // [!code highlight]
const agent = createAgent({
model: "claude-sonnet-5",
tools, // [!code highlight]
});
const mathResponse = await agent.invoke({
messages: [{ role: "user", content: "what's (3 + 5) x 12?" }],
});
const weatherResponse = await agent.invoke({
messages: [{ role: "user", content: "what is the weather in nyc?" }],
});
import { MultiServerMCPClient } from "@langchain/mcp-adapters"; // [!code highlight]
import { createAgent } from "langchain";
const client = new MultiServerMCPClient({
docs: {
transport: "http",
url: "https://docs.langchain.com/mcp", // [!code highlight]
},
});
const tools = await client.getTools();
const agent = createAgent({
model: "claude-sonnet-5",
tools,
});
const response = await agent.invoke({
messages: [
{
role: "user",
content: "How do I add short-term memory to a LangChain agent?",
},
],
});
서버가 노출하는 도구는 다음과 같습니다:
| 도구 | 설명 |
|---|---|
search_docs_by_lang_chain |
관련 가이드, how-to, 예시를 문서에서 검색합니다. |
query_docs_filesystem_docs_by_lang_chain |
가상 파일 시스템(rg, head, cat 및 관련 명령)을 통해 문서를 읽거나 검색합니다. |
submit_feedback |
문서 페이지에 문제를 보고합니다. |
커스텀 서버 (Custom servers)
자체 MCP 서버를 만들려면 @modelcontextprotocol/sdk 라이브러리를 사용할 수 있습니다. 이 라이브러리는 도구를 정의하고 서버로 실행하는 간단한 방법을 제공합니다.
pnpm add @modelcontextprotocol/sdk
yarn add @modelcontextprotocol/sdk
bun add @modelcontextprotocol/sdk
MCP 도구 서버로 에이전트를 테스트하려면 다음 예시를 사용하세요:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{
name: "math-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "add",
description: "Add two numbers",
inputSchema: {
type: "object",
properties: {
a: {
type: "number",
description: "First number",
},
b: {
type: "number",
description: "Second number",
},
},
required: ["a", "b"],
},
},
{
name: "multiply",
description: "Multiply two numbers",
inputSchema: {
type: "object",
properties: {
a: {
type: "number",
description: "First number",
},
b: {
type: "number",
description: "Second number",
},
},
required: ["a", "b"],
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case "add": {
const { a, b } = request.params.arguments as { a: number; b: number };
return {
content: [
{
type: "text",
text: String(a + b),
},
],
};
}
case "multiply": {
const { a, b } = request.params.arguments as { a: number; b: number };
return {
content: [
{
type: "text",
text: String(a * b),
},
],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Math MCP server running on stdio");
}
main();
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import express from "express";
const app = express();
app.use(express.json());
const server = new Server(
{
name: "weather-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_weather",
description: "Get weather for location",
inputSchema: {
type: "object",
properties: {
location: {
type: "string",
description: "Location to get weather for",
},
},
required: ["location"],
},
},
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case "get_weather": {
const { location } = request.params.arguments as { location: string };
return {
content: [
{
type: "text",
text: `It's always sunny in ${location}`,
},
],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
});
app.post("/mcp", async (req, res) => {
const transport = new SSEServerTransport("/mcp", res);
await server.connect(transport);
});
const PORT = process.env.PORT || 8000;
app.listen(PORT, () => {
console.log(`Weather MCP server running on port ${PORT}`);
});
전송 (Transports)
MCP는 클라이언트-서버 통신을 위한 여러 전송 메커니즘을 지원합니다.
HTTP
http 전송(또는 streamable-http라고도 함)은 클라이언트-서버 통신에 HTTP 요청을 사용합니다. 자세한 내용은 MCP HTTP 전송 사양을 참조하세요.
직접 실행하는 서버에는 로컬 URL을, 공개이고 API 키가 필요 없는 LangChain docs MCP 서버(https://docs.langchain.com/mcp) 같은 호스팅 URL을 사용하세요.
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
import { createAgent } from "langchain";
const client = new MultiServerMCPClient({
mcp: {
transport: "http",
// url: "http://localhost:8000/mcp", // Local server
url: "https://docs.langchain.com/mcp", // Hosted server
},
});
const tools = await client.getTools();
const agent = createAgent({ model: "openai:gpt-5.4", tools });
const response = await agent.invoke({
messages: [
{
role: "user",
content: "How do I connect LangChain to an MCP server over HTTP?",
},
],
});
헤더 전달 (Passing headers)
HTTP로 MCP 서버에 연결할 때 연결 구성의 headers 필드를 사용해 커스텀 헤더(예: 인증 또는 추적용)를 포함할 수 있습니다. 이 예시는 LangChain docs MCP 서버를 사용합니다. 인증이 필요한 서버에는 헤더 값을 바꾸세요:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
import { createAgent } from "langchain";
const client = new MultiServerMCPClient({
mcp: {
transport: "http",
url: "https://docs.langchain.com/mcp",
headers: { // [!code highlight]
Authorization: "Bearer YOUR_TOKEN", // [!code highlight]
"X-Custom-Header": "custom-value", // [!code highlight]
}, // [!code highlight]
},
});
const tools = await client.getTools();
const agent = createAgent({ model: "openai:gpt-5.4", tools });
const response = await agent.invoke({
messages: [
{
role: "user",
content: "How do I connect LangChain to an MCP server over HTTP?",
},
],
});
인증 (Authentication)
@langchain/mcp-adapters 라이브러리는 내부적으로 공식 MCP TypeScript SDK를 사용하며, OAuthClientProvider 인터페이스를 구현해 커스텀 인증 메커니즘을 제공할 수 있습니다.
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
const client = new MultiServerMCPClient({
weather: {
transport: "http",
url: "http://localhost:8000/mcp",
authProvider: authProvider, // [!code highlight]
},
});
stdio
클라이언트가 서버를 하위 프로세스로 시작하고 표준 입력/출력으로 통신합니다. 로컬 도구와 단순한 구성에 가장 적합합니다.
const client = new MultiServerMCPClient({
math: {
transport: "stdio",
command: "node",
args: ["/path/to/math_server.js"],
},
});
핵심 기능 (Core features)
도구 (Tools)
도구는 MCP 서버가 LLM이 작업을 수행하기 위해 호출할 수 있는 실행 가능한 함수를 노출하게 해줍니다. 예를 들어 데이터베이스 쿼리, API 호출, 외부 시스템과의 상호작용 등이 있습니다. LangChain은 MCP 도구를 LangChain 도구로 변환해 어떤 LangChain 에이전트나 워크플로에서도 바로 사용할 수 있게 합니다.
도구 로딩 (Loading tools)
client.getTools()를 사용해 MCP 서버에서 도구를 가져와 에이전트에 전달하세요:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
import { createAgent } from "langchain";
const client = new MultiServerMCPClient({...});
const tools = await client.getTools(); // [!code highlight]
const agent = createAgent({ model: "claude-sonnet-5", tools });
MCP 도구 실행이 실패하면(isError: true인 CallToolResult) @langchain/mcp-adapters는 ToolException을 발생시킵니다. 이 오류를 처리하려면 도구 호출을 try/catch로 감싸세요. Python 어댑터와 달리 TypeScript 어댑터는 오류를 실패한 도구 메시지로 모델에 반환하지 않습니다.
구조화된 콘텐츠 (Structured content)
MCP 도구는 사람이 읽을 수 있는 텍스트 응답과 함께 구조화된 콘텐츠를 반환할 수 있습니다. 이는 도구가 모델에 표시되는 텍스트 외에도 기계가 파싱할 수 있는 데이터(JSON 같은)를 반환해야 할 때 유용합니다.
MCP 도구가 structuredContent를 반환하면 어댑터는 도구 메시지 artifact 배열에 mcp_structured_content 항목을 추가합니다. 도구 인터셉터를 사용해 구조화된 콘텐츠를 자동으로 처리하거나 변환할 수도 있습니다.
artifact에서 구조화된 콘텐츠 추출
에이전트를 호출한 뒤 응답의 도구 메시지에서 구조화된 콘텐츠에 접근할 수 있습니다:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
import { createAgent } from "langchain";
const client = new MultiServerMCPClient({...});
const tools = await client.getTools();
const agent = createAgent({ model: "claude-sonnet-5", tools });
const result = await agent.invoke({
messages: [{ role: "user", content: "Get data from the server" }],
});
for (const message of result.messages) {
if (message.type !== "tool" || !message.artifact) continue;
for (const item of message.artifact) {
if (item?.type === "mcp_structured_content") {
console.log(item.data);
}
}
}
멀티모달 도구 콘텐츠 (Multimodal tool content)
MCP 도구는 응답에서 멀티모달 콘텐츠(이미지, 텍스트 등)를 반환할 수 있습니다. MCP 서버가 여러 파트(예: 텍스트와 이미지)가 있는 콘텐츠를 반환하면 어댑터는 이를 LangChain의 표준 콘텐츠 블록으로 변환합니다. ToolMessage의 contentBlocks 속성으로 표준화된 표현에 접근할 수 있습니다:
async function accessMultimodalToolContent(): Promise
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent } from "langchain";
async function accessMultimodalToolContent(): Promise<void> {
const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
const client = new MultiServerMCPClient({});
const tools = await client.getTools();
const agent = createAgent({ model: "openai:gpt-5.5", tools });
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
import { createAgent } from "langchain";
async function accessMultimodalToolContent(): Promise<void> {
const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
const client = new MultiServerMCPClient({});
const tools = await client.getTools();
const agent = createAgent({ model: "anthropic:claude-sonnet-5", tools });
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
import { createAgent } from "langchain";
async function accessMultimodalToolContent(): Promise<void> {
const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
const client = new MultiServerMCPClient({});
const tools = await client.getTools();
const agent = createAgent({ model: "openrouter:z-ai/glm-5.2", tools });
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
import { createAgent } from "langchain";
async function accessMultimodalToolContent(): Promise<void> {
const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
const client = new MultiServerMCPClient({});
const tools = await client.getTools();
const agent = createAgent({ model: "fireworks:accounts/fireworks/models/glm-5p2", tools });
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
import { createAgent } from "langchain";
async function accessMultimodalToolContent(): Promise<void> {
const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
const client = new MultiServerMCPClient({});
const tools = await client.getTools();
const agent = createAgent({ model: "baseten:zai-org/GLM-5.2", tools });
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
import { createAgent } from "langchain";
async function accessMultimodalToolContent(): Promise<void> {
const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
const client = new MultiServerMCPClient({});
const tools = await client.getTools();
const agent = createAgent({ model: "ollama:north-mini-code-1.0", tools });
const result = await agent.invoke({
messages: [
{ role: "user", content: "Take a screenshot of the current page" },
],
});
// Access multimodal content from tool messages
for (const message of result.messages) {
if (message.type === "tool") {
// Raw content in provider-native format
console.log(`Raw content: ${message.content}`);
// Standardized content blocks // [!code highlight]
for (const block of message.contentBlocks) {
// [!code highlight]
if (block.type === "text") {
// [!code highlight]
console.log(`Text: ${block.text}`); // [!code highlight]
} else if (block.type === "image") {
// [!code highlight]
console.log(`Image URL: ${block.url}`); // [!code highlight]
console.log(`Image base64: ${block.base64?.slice(0, 50)}...`); // [!code highlight]
}
}
}
}
}
이렇게 하면 기본 MCP 서버가 콘텐츠를 포맷하는 방식과 무관하게 프로바이더에 구애받지 않는 방식으로 멀티모달 도구 응답을 처리할 수 있습니다.
리소스 (Resources)
리소스는 MCP 서버가 파일, 데이터베이스 레코드, API 응답 같은 데이터를 노출하게 해주며 클라이언트가 읽을 수 있습니다.
리소스 로딩 (Loading resources)
client.listResources()로 리소스를 발견하고 client.readResource()로 내용을 읽으세요:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
const client = new MultiServerMCPClient({...});
// List resources from a server
const resourcesByServer = await client.listResources("server_name"); // [!code highlight]
for (const resource of resourcesByServer["server_name"] ?? []) {
console.log(`URI: ${resource.uri}, MIME type: ${resource.mimeType}`);
}
// Read a specific resource by URI
const contents = await client.readResource( // [!code highlight]
"server_name",
"file:///path/to/file.txt",
);
for (const content of contents) {
console.log(`URI: ${content.uri}, MIME type: ${content.mimeType}`);
if (content.text) console.log(content.text);
}
고급 기능 (Advanced features)
도구 인터셉터 (Tool interceptors)
MCP 서버는 별도의 프로세스로 실행됩니다. store, context, 에이전트 상태 같은 LangGraph 런타임 정보에는 접근할 수 없습니다. MultiServerMCPClient의 beforeToolCall과 afterToolCall 훅을 사용해 도구 인자, 헤더, 또는 결과를 수정하세요:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
const client = new MultiServerMCPClient({
mcpServers: {
math: {
transport: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-math"],
},
},
beforeToolCall: ({ serverName, name, args }) => { // [!code highlight]
const nextArgs = { ...(args as Record<string, unknown>), injected: true };
return {
args: nextArgs,
headers: { "X-Request-ID": crypto.randomUUID() },
};
},
afterToolCall: (res) => { // [!code highlight]
if (res.name === "someTool") {
return { result: ["modified-output", []] };
}
return { result: res.result };
},
});
const tools = await client.getTools();
- beforeToolCall:
{ args?, headers? }를 반환할 수 있습니다. 헤더는 HTTP와 SSE 전송에서 지원됩니다. Stdio 연결은 커스텀 헤더를 지원하지 않습니다. - afterToolCall:
result가[content, artifact]튜플,ToolMessage, LangGraphCommand, 또는 원래 결과인{ result }를 반환할 수 있습니다.
진행 알림 (Progress notifications)
onProgress로 오래 걸리는 도구 실행의 진행 업데이트를 구독하세요:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
const client = new MultiServerMCPClient({
mcpServers: {
everything: {
transport: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-everything"],
},
},
onProgress: (progress, source) => { // [!code highlight]
const pct =
progress.percentage ??
(progress.progress != null && progress.total
? Math.round((progress.progress / progress.total) * 100)
: undefined);
if (pct == null) return;
const origin =
source.type === "tool" ? `${source.server}/${source.name}` : "unknown";
console.log(`[progress:${origin}] ${pct}%`);
},
});
const tools = await client.getTools();
로깅 (Logging)
MCP 프로토콜은 서버의 로깅 알림을 지원합니다. onMessage로 구독하세요:
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
const client = new MultiServerMCPClient({
mcpServers: {
everything: {
transport: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-everything"],
},
},
onMessage: (log, source) => { // [!code highlight]
console.log(`[${source.server}] ${log.level}: ${log.data}`);
},
});
const tools = await client.getTools();
client.setLoggingLevel("debug") 또는 client.setLoggingLevel("server_name", "debug")로 서버 로깅 레벨을 설정할 수도 있습니다.
추가 리소스 (Additional resources)
더 알아보기
- 이 문서를 MCP로 연결하면 Claude, VSCode 등에서 실시간 답변을 받을 수 있어요.
- GitHub에서 이 페이지 편집하기 또는 이슈 제출하기.