GoSuda

Comprendere meglio l'host MCP

By snowmerak
views ...

MCP๊ฐ€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ

MCP๋ž€ Anthropic์—์„œ claude๋ฅผ ์œ„ํ•ด ๊ฐœ๋ฐœ๋œ ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. MCP๋Š” Model Context Protocol์˜ ์ค„์ž„๋ง๋กœ์จ LLM์ด ๋Šฅ๋™์ ์œผ๋กœ ์™ธ๋ถ€์— ๋™์ž‘์ด๋‚˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. MCP๋Š” ์ง„์งœ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ์— ๋ถˆ๊ณผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ๊ณผ์ •๊ณผ ์‹คํ–‰์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๋ถ€ ๋™์ž‘์— ๋Œ€ํ•ด์„œ

๋‚ด๋ถ€ ๋™์ž‘์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ธฐ ์•ž์„œ, Gemini Function Calling์— ๋Œ€ํ•ด ์งš๊ณ  ๋„˜์–ด ๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. Gemini Function Calling๋„ MCP์™€ ๋™์ผํ•˜๊ฒŒ LLM์ด ์ฃผ๋„์ ์œผ๋กœ ์™ธ๋ถ€ ๋™์ž‘์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์™œ Function Calling์„ ๊ตณ์ด ๊ฐ€์ ธ์™”๋Š”๊ฐ€ ์˜๋ฌธ์ด ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ตณ์ด ๊ฐ€์ ธ์˜จ ์ด์œ ๋Š” Function Calling์ด MCP๋ณด๋‹ค ๋จผ์ € ๋‚˜์˜ค๊ธฐ๋„ ํ–ˆ๊ณ , ๋™์ผํ•˜๊ฒŒ OpenAPI ์Šคํ‚ค๋งˆ๋ฅผ ์ด์šฉํ•œ๋‹ค๋Š” ์ ์—์„œ ํ˜ธํ™˜์ด ๋˜์–ด, ์ƒํ˜ธ ๊ฐ„์˜ ๋™์ž‘์ด ์œ ์‚ฌํ•  ๊ฒƒ์œผ๋กœ ์ถ”์ธกํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ณด๋‹ˆ ๋น„๊ต์  Gemini Function Calling์˜ ์„ค๋ช…์ด ๋”์šฑ ์ƒ์„ธํ•˜๊ธฐ์— ๋„์›€์ด ๋  ๊ฒƒ์œผ๋กœ ๋ณด์—ฌ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

FunctionCalling

์ „์ฒด์ ์ธ ํ๋ฆ„์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

  1. ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  2. ํ”„๋กฌํ”„ํŠธ์™€ ํ•จ๊ป˜ Gemini์— ํ•จ์ˆ˜ ์ •์˜๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
    1. "Send user prompt along with the function declaration(s) to the model. It analyzes the request and determines if a function call would be helpful. If so, it responds with a structured JSON object."
  3. Gemini๊ฐ€ ํ•„์š”ํ•˜๋ฉด ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
    1. Gemini๊ฐ€ ํ•„์š”ํ•˜๋ฉด ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ์œ„ํ•œ ์ด๋ฆ„๊ณผ ํŒจ๋Ÿฌ๋ฏธํ„ฐ๋ฅผ ํ˜ธ์ถœ์ž๊ฐ€ ์ „๋‹ฌ๋ฐ›์Šต๋‹ˆ๋‹ค.
    2. ํ˜ธ์ถœ์ž๋Š” ์‹คํ–‰์„ ํ• ์ง€, ๋ง์ง€ ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      1. ํ˜ธ์ถœํ•ด์„œ ์ •๋‹นํ•œ ๊ฐ’์„ ๋Œ๋ ค์ค„ ๊ฒƒ์ธ์ง€
      2. ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ํ˜ธ์ถœํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ• ์ง€
      3. ๊ทธ๋ƒฅ ๋ฌด์‹œํ• ์ง€
  4. Gemini๋Š” ์œ„ ๊ณผ์ •์—์„œ ํ•œ๋ฒˆ์— ์—ฌ๋Ÿฌ๊ฐœ์˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜, ํ•จ์ˆ˜ ํ˜ธ์ถœ ํ›„ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ  ๋˜ ํ˜ธ์ถœํ•˜๋Š” ๋“ฑ์˜ ๋™์ž‘์„ ์ˆ˜ํ–‰ ๋ฐ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  5. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ •๋ˆ๋œ ๋Œ€๋‹ต์ด ๋‚˜์˜ค๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

์ด ํ๋ฆ„์€ ์ผ๋ฐ˜์ ์œผ๋กœ MCP์™€ ์ผ๋งฅ์ƒํ†ตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” MCP์˜ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋„ ๋น„์Šทํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ollama tools๋„ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ •๋ง ๋‹คํ–‰์ด๊ฒŒ๋„ ์ด 3๊ฐ€์ง€ ๋„๊ตฌ, ollama tools, MCP, Gemini Function Calling์€ ์Šคํ‚ค๋งˆ ๊ตฌ์กฐ๊ฐ€ ๊ณต์œ ๋˜๋‹ค์‹œํ”ผ ํ•ด์„œ MCP ํ•˜๋‚˜๋งŒ ๊ตฌํ˜„ํ•จ์œผ๋กœ 3๊ณณ์— ๋‹ค ์“ธ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•„ ๊ทธ๋ฆฌ๊ณ  ๋ชจ๋‘๊ฐ€ ๊ณต์œ ํ•˜๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๋ชจ๋ธ์ด ์‹คํ–‰์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ๋ถ„์ด ์“ฐ๋Š” ๋ชจ๋ธ์ด ์ƒํƒœ๊ฐ€ ์•ˆ ์ข‹๋‹ค๋ฉด, ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ์ด์ƒํ•˜๊ฒŒ ํ˜ธ์ถœํ•œ๋‹ค๊ฑฐ๋‚˜, MCP ์„œ๋ฒ„์— DOS๋ฅผ ๋‚ ๋ฆฌ๋Š” ๋“ฑ์˜ ์˜ค๋™์ž‘์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Go๋กœ ๋œ MCP ํ˜ธ์ŠคํŠธ

mark3lab's mcphost

Go์—๋Š” mark3lab์ด๋ž€ ์กฐ์ง์—์„œ ๊ฐœ๋ฐœ ์ค‘์ธ mcphost๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

1go install github.com/mark3labs/mcphost@latest

์„ค์น˜ ํ›„, $HOME/.mcp.json ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 1{
 2  "mcpServers": {
 3    "sqlite": {
 4      "command": "uvx",
 5      "args": [
 6        "mcp-server-sqlite",
 7        "--db-path",
 8        "/tmp/foo.db"
 9      ]
10    },
11    "filesystem": {
12      "command": "npx",
13      "args": [
14        "-y",
15        "@modelcontextprotocol/server-filesystem",
16        "/tmp"
17      ]
18    }
19  }
20}

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ollama ๋ชจ๋ธ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
๋ฌผ๋ก  ๊ทธ ์ „์— ํ•„์š”ํ•˜๋ฉด ollama pull mistral-small๋กœ ๋ชจ๋ธ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ claude๋‚˜ qwen2.5๋ฅผ ์ถ”์ฒœํ•˜์ง€๋งŒ, ์ €๋Š” ํ˜„์žฌ๋กœ์ฌ mistral-small์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

1mcphost -m ollama:mistral-small

๋‹ค๋งŒ ์ด๋ ‡๊ฒŒ ์‹คํ–‰ํ•˜๋ฉด, CLI ํ™˜๊ฒฝ์—์„œ ์งˆ์˜์‘๋‹ต ์‹์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ ‡๊ธฐ์— ์ €ํฌ๋Š” ์ด mcphost์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์„œ ์ข€ ๋” ํ”„๋กœ๊ทธ๋ž˜๋จธ๋ธ” ํ•˜๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ˆ˜์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

mcphost ํฌํฌ

์ด๋ฏธ ํ™•์ธํ–ˆ๋‹ค์‹œํ”ผ mcphost์—๋Š” MCP๋ฅผ ํ™œ์šฉํ•ด์„œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๊ณ , ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ llm์„ ํ˜ธ์ถœํ•˜๋Š” ๋ถ€๋ถ„, mcp ์„œ๋ฒ„๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ถ€๋ถ„, ๋ฉ”์‹œ์ง€ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„์„ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ด ๋‹ค์Œ ํŒจํ‚ค์ง€์˜ Runner์ž…๋‹ˆ๋‹ค.

 1package runner
 2
 3import (
 4	"context"
 5	"encoding/json"
 6	"fmt"
 7	"log"
 8	"strings"
 9	"time"
10
11	mcpclient "github.com/mark3labs/mcp-go/client"
12	"github.com/mark3labs/mcp-go/mcp"
13
14	"github.com/mark3labs/mcphost/pkg/history"
15	"github.com/mark3labs/mcphost/pkg/llm"
16)
17
18type Runner struct {
19	provider   llm.Provider
20	mcpClients map[string]*mcpclient.StdioMCPClient
21	tools      []llm.Tool
22
23	messages []history.HistoryMessage
24}

ํ•ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„์˜ ๋‚ด๋ถ€ ์„ ์–ธ์€ ๋”ฐ๋กœ ๋ณด์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊ฑฐ์˜ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.

 1func NewRunner(systemPrompt string, provider llm.Provider, mcpClients map[string]*mcpclient.StdioMCPClient, tools []llm.Tool) *Runner {
 2	return &Runner{
 3		provider:   provider,
 4		mcpClients: mcpClients,
 5		tools:      tools,
 6		messages: []history.HistoryMessage{
 7			{
 8				Role: "system",
 9				Content: []history.ContentBlock{{
10					Type: "text",
11					Text: systemPrompt,
12				}},
13			},
14		},
15	}
16}

์—ฌ๊ธฐ์— ์“ฐ์ผ mcpClients์™€ tools์— ๋Œ€ํ•ด์„œ๋Š” ํ•ด๋‹น ํŒŒ์ผ์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.
provider๋Š” ollama์˜ ๊ฒƒ์„ ์“ธ ํ…Œ๋‹ˆ ํ•ด๋‹น ํŒŒ์ผ์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.

๋ฉ”์ธ ์š”๋ฆฌ๋Š” Run ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

  1func (r *Runner) Run(ctx context.Context, prompt string) (string, error) {
  2	if len(prompt) != 0 {
  3		r.messages = append(r.messages, history.HistoryMessage{
  4			Role: "user",
  5			Content: []history.ContentBlock{{
  6				Type: "text",
  7				Text: prompt,
  8			}},
  9		})
 10	}
 11
 12	llmMessages := make([]llm.Message, len(r.messages))
 13	for i := range r.messages {
 14		llmMessages[i] = &r.messages[i]
 15	}
 16
 17	const initialBackoff = 1 * time.Second
 18	const maxRetries int = 5
 19	const maxBackoff = 30 * time.Second
 20
 21	var message llm.Message
 22	var err error
 23	backoff := initialBackoff
 24	retries := 0
 25	for {
 26		message, err = r.provider.CreateMessage(
 27			context.Background(),
 28			prompt,
 29			llmMessages,
 30			r.tools,
 31		)
 32		if err != nil {
 33			if strings.Contains(err.Error(), "overloaded_error") {
 34				if retries >= maxRetries {
 35					return "", fmt.Errorf(
 36						"claude is currently overloaded. please wait a few minutes and try again",
 37					)
 38				}
 39
 40				time.Sleep(backoff)
 41				backoff *= 2
 42				if backoff > maxBackoff {
 43					backoff = maxBackoff
 44				}
 45				retries++
 46				continue
 47			}
 48
 49			return "", err
 50		}
 51
 52		break
 53	}
 54
 55	var messageContent []history.ContentBlock
 56
 57	var toolResults []history.ContentBlock
 58	messageContent = []history.ContentBlock{}
 59
 60	if message.GetContent() != "" {
 61		messageContent = append(messageContent, history.ContentBlock{
 62			Type: "text",
 63			Text: message.GetContent(),
 64		})
 65	}
 66
 67	for _, toolCall := range message.GetToolCalls() {
 68		input, _ := json.Marshal(toolCall.GetArguments())
 69		messageContent = append(messageContent, history.ContentBlock{
 70			Type:  "tool_use",
 71			ID:    toolCall.GetID(),
 72			Name:  toolCall.GetName(),
 73			Input: input,
 74		})
 75
 76		parts := strings.Split(toolCall.GetName(), "__")
 77
 78		serverName, toolName := parts[0], parts[1]
 79		mcpClient, ok := r.mcpClients[serverName]
 80		if !ok {
 81			continue
 82		}
 83
 84		var toolArgs map[string]interface{}
 85		if err := json.Unmarshal(input, &toolArgs); err != nil {
 86			continue
 87		}
 88
 89		var toolResultPtr *mcp.CallToolResult
 90		req := mcp.CallToolRequest{}
 91		req.Params.Name = toolName
 92		req.Params.Arguments = toolArgs
 93		toolResultPtr, err = mcpClient.CallTool(
 94			context.Background(),
 95			req,
 96		)
 97
 98		if err != nil {
 99			errMsg := fmt.Sprintf(
100				"Error calling tool %s: %v",
101				toolName,
102				err,
103			)
104			log.Printf("Error calling tool %s: %v", toolName, err)
105
106			toolResults = append(toolResults, history.ContentBlock{
107				Type:      "tool_result",
108				ToolUseID: toolCall.GetID(),
109				Content: []history.ContentBlock{{
110					Type: "text",
111					Text: errMsg,
112				}},
113			})
114
115			continue
116		}
117
118		toolResult := *toolResultPtr
119
120		if toolResult.Content != nil {
121			resultBlock := history.ContentBlock{
122				Type:      "tool_result",
123				ToolUseID: toolCall.GetID(),
124				Content:   toolResult.Content,
125			}
126
127			var resultText string
128			for _, item := range toolResult.Content {
129				if contentMap, ok := item.(map[string]interface{}); ok {
130					if text, ok := contentMap["text"]; ok {
131						resultText += fmt.Sprintf("%v ", text)
132					}
133				}
134			}
135
136			resultBlock.Text = strings.TrimSpace(resultText)
137
138			toolResults = append(toolResults, resultBlock)
139		}
140	}
141
142	r.messages = append(r.messages, history.HistoryMessage{
143		Role:    message.GetRole(),
144		Content: messageContent,
145	})
146
147	if len(toolResults) > 0 {
148		r.messages = append(r.messages, history.HistoryMessage{
149			Role:    "user",
150			Content: toolResults,
151		})
152
153		return r.Run(ctx, "")
154	}
155
156	return message.GetContent(), nil
157}

์ฝ”๋“œ ์ž์ฒด๋Š” ํ•ด๋‹น ํŒŒ์ผ์˜ ์ผ๋ถ€ ์ฝ”๋“œ๋ฅผ ์งœ์ง‘๊ธฐ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋‚ด์šฉ์€ ๋Œ€๋žต ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ํ”„๋กฌํ”„ํŠธ์™€ ํ•จ๊ป˜ ํˆด ๋ชฉ๋ก์„ ์ „์†กํ•˜์—ฌ ์‹คํ–‰ ์—ฌ๋ถ€, ํ˜น์€ ์‘๋‹ต ์ƒ์„ฑ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  2. ์‘๋‹ต์ด ์ƒ์„ฑ๋˜๋ฉด ์žฌ๊ท€๋ฅผ ๋ฉˆ์ถ”๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  3. LLM์ด ํˆด ์‹คํ–‰ ์š”์ฒญ์„ ๋‚จ๊ธด๋‹ค๋ฉด, ํ˜ธ์ŠคํŠธ์—์„œ๋Š” MCP Server๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  4. ์‘๋‹ต์„ ํžˆ์Šคํ† ๋ฆฌ์— ์ถ”๊ฐ€ํ•ด์„œ ๋‹ค์‹œ 1๋ฒˆ์œผ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.

๋์œผ๋กœ

๋ฒŒ์จ ๋?

์‚ฌ์‹ค ํ•  ๋ง์ด ๊ทธ๋ ‡๊ฒŒ ๋งŽ์ง„ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€๋žต์ ์œผ๋กœ MCP Server๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘๋˜๋Š” ์ง€์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋„์™€ ๋“œ๋ฆฌ๊ธฐ ์œ„ํ•ด ์ž‘์„ฑ๋œ ๊ธ€์ž…๋‹ˆ๋‹ค. ์ด ๊ธ€์ด ์—ฌ๋Ÿฌ๋ถ„๋“ค์—๊ฒŒ ์ž๊ทธ๋งฃ๊ฒŒ๋‚˜๋งˆ MCP host์˜ ๋™์ž‘์„ ์ดํ•ดํ•จ์— ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.