MCP Sampling AI 프로바이더

MCP Sampling AI 프로바이더

MCP Sampling AI 프로바이더 는 기존 클라이언트 구독(예: VS Code Copilot)을 활용해 MCP 서버가 AI SDK를 통해 AI 모델을 사용할 수 있게 해줘요. 이 프로바이더는 별도의 API 키나 구독 없이도 MCP 서버를 추론하고 의사결정할 수 있는 에이전틱 도구로 바꿔줘요.

이 프로바이더는 MCP의 sampling 기능을 통해 요청을 MCP 클라이언트로 전달하면서 LanguageModelV4를 구현해, 고유한 장점을 제공해요:

  • 서버 측 AI 통합 (Server-Side AI Integration): MCP 서버가 AI SDK의 표준 인터페이스를 통해 직접 언어 모델을 호출할 수 있게 해줘요
  • 직접 모델 관리 불필요 (No Direct Model Management): AI 요청을 MCP 클라이언트로 전달해 여러 API 키의 필요성을 없애요
  • 모델 유연성 (Model Flexibility): MCP 클라이언트가 선호도(비용, 속도, 지능)에 따라 어떤 모델을 사용할지 결정하게 해줘요
  • 원활한 AI SDK 호환성 (Seamless AI SDK Compatibility): generateText, streamText, Output을 통한 구조화 출력, 실험적 도구 호출 지원
  • 클라이언트 샘플링 지원 (Client Sampling Support): 내장 헬퍼로 어떤 MCP 클라이언트에든 샘플링 기능 추가
  • 에이전틱 도구 (Agentic Tools): 단순한 MCP 도구를 추론하고 의사결정할 수 있는 지능형 에이전트로 변환

MCP Sampling에 대해 더 알아보려면 MCP 스펙을 참고하세요.

출처: 문서

본문

사전 요구 사항 (Prerequisites)

경고: 이 프로바이더는 특정 요구 사항이 있어요:

  1. MCP 서버 안에서 실행해야 함 - 독립형 AI SDK 프로바이더가 아니에요. MCP 클라이언트로 요청을 전달하는 방식으로 동작해요.
  2. 클라이언트가 MCP Sampling을 지원해야 함 - 연결된 MCP 클라이언트가 샘플링 기능을 구현해야 하며, 직접 구현할 수도 있어요(아래 클라이언트 샘플링 참고).

샘플링 지원 클라이언트 (Clients with Sampling Support)

  • VS Code (GitHub Copilot 포함) - 지원됨
  • Claude Desktop - 추적 중 (이슈 #1785)
  • Cursor - 추적 중 (이슈 #3023)

더 많은 옵션은 MCP 클라이언트 전체 목록을 참고하세요.

대안: setupClientSampling()을 사용해 어떤 MCP 클라이언트에든 샘플링을 추가할 수 있어요(아래 예시 참고).

설정 (Setup)

MCP Sampling AI 프로바이더는 @mcpc-tech/mcp-sampling-ai-provider 모듈에서 사용할 수 있어요. 다음과 같이 설치할 수 있어요:

# pnpm
pnpm add @mcpc-tech/mcp-sampling-ai-provider

# npm
npm install @mcpc-tech/mcp-sampling-ai-provider

# yarn
yarn add @mcpc-tech/mcp-sampling-ai-provider

# bun
bun add @mcpc-tech/mcp-sampling-ai-provider

# deno
deno add jsr:@mcpc/mcp-sampling-ai-provider

프로바이더 인스턴스 (Provider Instance)

MCP Sampling 프로바이더 인스턴스를 만들려면 MCP 서버 인스턴스와 함께 createMCPSamplingProvider 함수를 사용하세요:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { createMCPSamplingProvider } from '@mcpc-tech/mcp-sampling-ai-provider';

// 샘플링 기능을 가진 MCP 서버 생성
const server = new Server(
  { name: 'my-agent', version: '1.0.0' },
  { capabilities: { sampling: {}, tools: {} } },
);

const provider = createMCPSamplingProvider({ server });

구성 (Configuration)

프로바이더는 다음 구성을 받아요:

  • server MCP Server instance

    샘플링 기능이 활성화된 MCP Server 인스턴스예요.

언어 모델 (Language Models)

languageModel() 메서드로 언어 모델 인스턴스를 만들어요:

const model = provider.languageModel({
  modelPreferences: {
    hints: [{ name: 'gpt-5-mini' }],
    costPriority: 0.5,
    speedPriority: 0.8,
    intelligencePriority: 0.9,
  },
});

모델 선호도 (Model Preferences)

languageModel() 메서드는 선택적 모델 선호도를 받아요:

  • hints Array\<\{ name: string \}\>

    모델 이름 힌트 배열 (예: [{ name: "gpt-5-mini" }]). MCP 클라이언트에 선호 모델을 제안해요.

  • costPriority number (0-1)

    값이 높을수록 더 저렴한 모델을 선호해요. 기본값은 0이에요.

  • speedPriority number (0-1)

    값이 높을수록 더 빠른 모델을 선호해요. 기본값은 0이에요.

  • intelligencePriority number (0-1)

    값이 높을수록 더 강력한 모델을 선호해요. 기본값은 0이에요.

자세한 내용은 MCP 모델 선호도를 참고하세요.

예시 (Examples)

generateText

MCP 서버 도구에서 MCP Sampling 프로바이더로 텍스트를 생성해요:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { createMCPSamplingProvider } from '@mcpc-tech/mcp-sampling-ai-provider';
import { generateText } from 'ai';

// 샘플링 기능을 가진 MCP 서버 생성
const server = new Server(
  { name: 'translator', version: '1.0.0' },
  { capabilities: { sampling: {}, tools: {} } },
);

// 사용 가능한 도구 목록
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'translate',
        description: 'Translate text to a target language using AI',
        inputSchema: {
          type: 'object',
          properties: {
            text: {
              type: 'string',
              description: 'The text to translate',
            },
            target_lang: {
              type: 'string',
              description: 'The target language (e.g., "Spanish", "French")',
            },
          },
          required: ['text', 'target_lang'],
        },
      },
    ],
  };
});

// AI를 사용하는 번역 도구 등록
server.setRequestHandler(CallToolRequestSchema, async request => {
  if (request.params.name === 'translate') {
    // 서버에서 프로바이더 생성
    const provider = createMCPSamplingProvider({ server });

    // AI SDK로 텍스트 번역
    const { text } = await generateText({
      model: provider.languageModel({
        modelPreferences: { hints: [{ name: 'gpt-5-mini' }] },
      }),
      prompt: `Translate to ${request.params.arguments?.target_lang}: ${request.params.arguments?.text}`,
    });

    return { content: [{ type: 'text', text }] };
  }
});

// 연결 및 시작
const transport = new StdioServerTransport();
await server.connect(transport);

streamText

MCP Sampling 프로바이더로 텍스트 응답을 스트리밍해요:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { createMCPSamplingProvider } from '@mcpc-tech/mcp-sampling-ai-provider';
import { streamText } from 'ai';

const server = new Server(
  { name: 'ai-assistant', version: '1.0.0' },
  { capabilities: { sampling: {}, tools: {} } },
);

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'generate-story',
        description: 'Generate a story or poem using AI',
        inputSchema: {
          type: 'object',
          properties: {},
        },
      },
    ],
  };
});

server.setRequestHandler(CallToolRequestSchema, async request => {
  if (request.params.name === 'generate-story') {
    const provider = createMCPSamplingProvider({ server });

    const result = streamText({
      model: provider.languageModel({
        modelPreferences: {
          hints: [{ name: 'gpt-5-mini' }],
          speedPriority: 0.9,
        },
      }),
      prompt: 'Write a short poem about coding.',
    });

    const text = await result.text;

    return { content: [{ type: 'text', text }] };
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

구조화 출력 (Structured Output)

MCP Sampling 프로바이더로 구조화 객체를 생성해요:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { createMCPSamplingProvider } from '@mcpc-tech/mcp-sampling-ai-provider';
import { generateText, Output } from 'ai';
import { z } from 'zod';

const server = new Server(
  { name: 'recipe-generator', version: '1.0.0' },
  { capabilities: { sampling: {}, tools: {} } },
);

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'generate-recipe',
        description: 'Generate a recipe using AI',
        inputSchema: {
          type: 'object',
          properties: {},
        },
      },
    ],
  };
});

server.setRequestHandler(CallToolRequestSchema, async request => {
  if (request.params.name === 'generate-recipe') {
    const provider = createMCPSamplingProvider({ server });

    const recipeSchema = z.object({
      recipe: z.object({
        name: z.string(),
        cuisine: z.string(),
        ingredients: z.array(z.string()),
        steps: z.array(z.string()),
      }),
    });

    const { output } = await generateText({
      model: provider.languageModel({
        modelPreferences: { hints: [{ name: 'gpt-5-mini' }] },
      }),
      output: Output.object({ schema: recipeSchema }),
      prompt: 'Generate a delicious lasagna recipe.',
    });

    return {
      content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
    };
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

도구 호출 (Tool Calling, 실험적)

MCP Sampling 프로바이더와 함께 도구를 사용해요. 참고: 이 기능은 시스템 프롬프트로 구현되어 네이티브 도구 지원만큼 안정적이지 않을 수 있어요:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { createMCPSamplingProvider } from '@mcpc-tech/mcp-sampling-ai-provider';
import { generateText, isStepCount } from 'ai';
import { z } from 'zod';

const server = new Server(
  { name: 'weather-agent', version: '1.0.0' },
  { capabilities: { sampling: {}, tools: {} } },
);

server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'ask-weather',
        description: 'Ask a weather-related question',
        inputSchema: {
          type: 'object',
          properties: {
            question: {
              type: 'string',
              description: 'The weather question to ask',
            },
          },
        },
      },
    ],
  };
});

server.setRequestHandler(CallToolRequestSchema, async request => {
  if (request.params.name === 'ask-weather') {
    const provider = createMCPSamplingProvider({ server });

    const result = await generateText({
      model: provider.languageModel({
        modelPreferences: { hints: [{ name: 'gpt-5-mini' }] },
      }),
      tools: {
        getWeather: {
          description: 'Get the weather for a location',
          inputSchema: z.object({
            city: z.string().describe('The city name'),
          }),
          execute: async ({ city }) => {
            return `The weather in ${city} is sunny and 72°F`;
          },
        },
      },
      prompt:
        request.params.arguments?.question ||
        'What is the weather in San Francisco?',
      stopWhen: isStepCount(5),
    });

    return { content: [{ type: 'text', text: result.text }] };
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

추가 예시 (Additional Examples)

더 완전한 작동 예시는 examples 디렉토리를 참고하세요:

클라이언트 샘플링 (Client Sampling, 네이티브 미지원 클라이언트용)

MCP 클라이언트가 네이티브로 샘플링을 지원하지 않으면 setupClientSampling을 모델 선호도와 함께 사용해 샘플링 기능을 추가할 수 있어요:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import {
  convertAISDKFinishReasonToMCP,
  selectModelFromPreferences,
  setupClientSampling,
} from '@mcpc-tech/mcp-sampling-ai-provider';
import { generateText } from 'ai';

const client = new Client(
  { name: 'my-client', version: '1.0.0' },
  { capabilities: { sampling: {} } },
);

setupClientSampling(client, {
  handler: async params => {
    const modelId = selectModelFromPreferences(params.modelPreferences, {
      hints: {
        'gpt-5': 'openai/gpt-5-mini',
        'gpt-mini': 'openai/gpt-5-mini',
      },
      priorities: {
        speed: 'openai/gpt-5-mini',
        intelligence: 'openai/gpt-5-mini',
      },
      default: 'openai/gpt-5-mini',
    });

    const result = await generateText({
      model: modelId,
      messages: params.messages,
    });

    return {
      model: modelId,
      role: 'assistant',
      content: { type: 'text', text: result.text },
      stopReason: convertAISDKFinishReasonToMCP(result.finishReason),
    };
  },
});

const transport = new StdioClientTransport({
  command: 'npx',
  args: ['-y', 'example_mcp_server.ts'],
});

await client.connect(transport);

자세한 내용은 전체 예시를 참고하세요.

동작 원리 (How It Works)

요청 흐름은 간단해요:

  1. AI SDK가 언어 모델을 호출
  2. 프로바이더가 MCP sampling/createMessage 형식으로 변환
  3. MCP 클라이언트가 샘플링 요청 처리
  4. 프로바이더가 응답을 AI SDK 형식으로 다시 변환

MCP 클라이언트(예: VS Code, Claude Desktop)는 제공된 modelPreferences를 바탕으로 실제 사용할 모델을 결정해요.

제한 사항 (Limitations)

  • 토큰 카운팅 없음: MCP는 토큰 사용 정보를 제공하지 않아요 (0 반환)
  • 네이티브 스트리밍 없음: MCP 샘플링은 스트리밍을 지원하지 않아요 - 프로바이더가 먼저 doGenerate를 호출한 다음 완전한 응답을 스트림 이벤트로 발행해요
  • 실험적 도구/JSON 지원: MCP 샘플링이 네이티브로 지원하지 않으므로 systemPrompt로 구현돼요

더 알아보기 (Learn more)

전체 사이트맵