---
id: a1fad13de24c90924cfa9e25e9f986cb
author: snowmerak
title: Memahami Sedikit tentang MCP Host
description: Artikel ini bertujuan membantu pemahaman tentang protokol MCP dari Anthropic dan MCP host (mcphost) yang diimplementasikan dengan Go.
language: id
date: 2025-04-08T00:59:42.416582136Z
path: /blog/posts/a-little-understanding-of-mcp-host-zb952aae0
category: ai
---

## Apa itu MCP

[MCP](https://modelcontextprotocol.io/introduction) adalah protokol yang dikembangkan oleh Anthropic untuk Claude. MCP merupakan singkatan dari Model Context Protocol, yaitu protokol yang memungkinkan LLM secara aktif meminta tindakan atau sumber daya dari pihak eksternal. Karena MCP secara harfiah hanyalah protokol yang menyampaikan permintaan dan respons, proses serta eksekusinya harus diimplementasikan oleh pengembang.

### Tentang mekanisme internal

Sebelum menjelaskan mekanisme internal, izinkan saya membahas terlebih dahulu [Gemini Function Calling](https://ai.google.dev/gemini-api/docs/function-calling). Gemini Function Calling juga memungkinkan LLM secara proaktif memanggil tindakan eksternal, sama seperti MCP. Tentu akan timbul pertanyaan mengapa Function Calling perlu dibahas. Alasannya, Function Calling lebih dahulu dirilis dibandingkan MCP dan keduanya sama-sama menggunakan skema OpenAPI, sehingga kompatibel satu sama lain; oleh karena itu, saya menduga cara kerjanya serupa. Karena penjelasan Gemini Function Calling relatif lebih rinci, saya memasukkannya di sini karena diperkirakan akan membantu.

![FunctionCalling](/assets/images/mcp_with_csharp/function-calling-overview.png)

Alur keseluruhannya adalah sebagai berikut.

1. Mendefinisikan fungsi.
2. Mengirimkan definisi fungsi ke Gemini bersama dengan prompt.
   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 meminta pemanggilan fungsi jika diperlukan.
   1. Jika Gemini memerlukannya, pemanggil (caller) menerima nama dan parameter untuk pemanggilan fungsi.
   2. Pemanggil dapat memutuskan apakah akan menjalankan atau tidak.
      1. Menjalankan pemanggilan dan mengembalikan nilai yang sah
      2. Tidak menjalankan pemanggilan, tetapi mengembalikan data seolah-olah pemanggilan telah dilakukan
      3. Mengabaikannya begitu saja
4. Gemini dapat memanggil beberapa fungsi sekaligus dalam satu proses, atau memanggil fungsi lagi setelah meninjau hasil pemanggilan sebelumnya, dan meminta eksekusi atas tindakan tersebut.
5. Proses berakhir ketika dihasilkan jawaban yang tersusun dengan baik.

Alur ini secara umum sejalan dengan MCP. Hal serupa juga dijelaskan dalam [tutorial MCP](https://modelcontextprotocol.io/tutorials/building-mcp-with-llms). Alur ini juga mirip dengan ollama tools.

Dan yang sangat melegakan, ketiga perangkat ini, yaitu ollama tools, MCP, dan Gemini Function Calling, memiliki struktur skema yang nyaris sama, sehingga cukup dengan mengimplementasikan MCP, Anda dapat menggunakannya di ketiga platform tersebut.

Namun ada satu kelemahan yang dimiliki bersama. Karena eksekusi sepenuhnya dilakukan oleh model, apabila model yang Anda gunakan kurang baik, model dapat tidak memanggil fungsi, memanggilnya secara keliru, atau bahkan melakukan tindakan yang tidak semestinya seperti membanjiri server MCP dengan permintaan (DoS).

## Host MCP berbasis Go

### mcphost milik mark3lab

Di Go terdapat [mcphost](https://github.com/mark3labs/mcphost) yang sedang dikembangkan oleh organisasi bernama mark3lab.

Cara penggunaannya sangat sederhana.

```sh
go install github.com/mark3labs/mcphost@latest
```

Setelah instalasi, buat berkas `$HOME/.mcp.json` dan tulis seperti berikut.

```json
{
  "mcpServers": {
    "sqlite": {
      "command": "uvx",
      "args": [
        "mcp-server-sqlite",
        "--db-path",
        "/tmp/foo.db"
      ]
    },
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/tmp"
      ]
    }
  }
}
```

Lalu jalankan dengan model ollama seperti berikut.  
Tentunya, jika diperlukan, unduh model terlebih dahulu dengan `ollama pull mistral-small`.

> Secara umum Claude atau qwen2.5 direkomendasikan, tetapi untuk saat ini saya merekomendasikan mistral-small.

```sh
mcphost -m ollama:mistral-small
```

Namun, dengan cara ini, penggunaannya terbatas pada tanya jawab di lingkungan CLI.  
Oleh karena itu, kita akan memodifikasi kode `mcphost` ini agar dapat beroperasi secara lebih programmable.

### Fork mcphost

Seperti yang telah kita lihat, `mcphost` sudah memiliki fitur untuk mengekstraksi metadata dan memanggil fungsi melalui MCP. Oleh karena itu, diperlukan bagian yang memanggil LLM, bagian yang menangani server MCP, dan bagian yang mengelola riwayat pesan.

Bagian-bagian tersebut dikumpulkan dalam `Runner` pada paket berikut.

```go
package runner

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	mcpclient "github.com/mark3labs/mcp-go/client"
	"github.com/mark3labs/mcp-go/mcp"

	"github.com/mark3labs/mcphost/pkg/history"
	"github.com/mark3labs/mcphost/pkg/llm"
)

type Runner struct {
	provider   llm.Provider
	mcpClients map[string]*mcpclient.StdioMCPClient
	tools      []llm.Tool

	messages []history.HistoryMessage
}
```

Deklarasi internal dari bagian tersebut tidak akan dibahas secara terpisah. Namun, namanya hampir sepenuhnya mencerminkan fungsinya.

```go
func NewRunner(systemPrompt string, provider llm.Provider, mcpClients map[string]*mcpclient.StdioMCPClient, tools []llm.Tool) *Runner {
	return &Runner{
		provider:   provider,
		mcpClients: mcpClients,
		tools:      tools,
		messages: []history.HistoryMessage{
			{
				Role: "system",
				Content: []history.ContentBlock{{
					Type: "text",
					Text: systemPrompt,
				}},
			},
		},
	}
}
```

Untuk `mcpClients` dan `tools` yang digunakan di sini, silakan periksa [berkas terkait](https://github.com/snowmerak/mcphost/blob/main/internal/runner/from.go).  
Untuk `provider`, kita akan menggunakan milik ollama, jadi silakan periksa [berkas terkait](https://github.com/snowmerak/mcphost/blob/main/pkg/llm/ollama/provider.go).

Bagian utamanya adalah metode `Run`.

```go
func (r *Runner) Run(ctx context.Context, prompt string) (string, error) {
	if len(prompt) != 0 {
		r.messages = append(r.messages, history.HistoryMessage{
			Role: "user",
			Content: []history.ContentBlock{{
				Type: "text",
				Text: prompt,
			}},
		})
	}

	llmMessages := make([]llm.Message, len(r.messages))
	for i := range r.messages {
		llmMessages[i] = &r.messages[i]
	}

	const initialBackoff = 1 * time.Second
	const maxRetries int = 5
	const maxBackoff = 30 * time.Second

	var message llm.Message
	var err error
	backoff := initialBackoff
	retries := 0
	for {
		message, err = r.provider.CreateMessage(
			context.Background(),
			prompt,
			llmMessages,
			r.tools,
		)
		if err != nil {
			if strings.Contains(err.Error(), "overloaded_error") {
				if retries >= maxRetries {
					return "", fmt.Errorf(
						"claude is currently overloaded. please wait a few minutes and try again",
					)
				}

				time.Sleep(backoff)
				backoff *= 2
				if backoff > maxBackoff {
					backoff = maxBackoff
				}
				retries++
				continue
			}

			return "", err
		}

		break
	}

	var messageContent []history.ContentBlock

	var toolResults []history.ContentBlock
	messageContent = []history.ContentBlock{}

	if message.GetContent() != "" {
		messageContent = append(messageContent, history.ContentBlock{
			Type: "text",
			Text: message.GetContent(),
		})
	}

	for _, toolCall := range message.GetToolCalls() {
		input, _ := json.Marshal(toolCall.GetArguments())
		messageContent = append(messageContent, history.ContentBlock{
			Type:  "tool_use",
			ID:    toolCall.GetID(),
			Name:  toolCall.GetName(),
			Input: input,
		})

		parts := strings.Split(toolCall.GetName(), "__")

		serverName, toolName := parts[0], parts[1]
		mcpClient, ok := r.mcpClients[serverName]
		if !ok {
			continue
		}

		var toolArgs map[string]interface{}
		if err := json.Unmarshal(input, &toolArgs); err != nil {
			continue
		}

		var toolResultPtr *mcp.CallToolResult
		req := mcp.CallToolRequest{}
		req.Params.Name = toolName
		req.Params.Arguments = toolArgs
		toolResultPtr, err = mcpClient.CallTool(
			context.Background(),
			req,
		)

		if err != nil {
			errMsg := fmt.Sprintf(
				"Error calling tool %s: %v",
				toolName,
				err,
			)
			log.Printf("Error calling tool %s: %v", toolName, err)

			toolResults = append(toolResults, history.ContentBlock{
				Type:      "tool_result",
				ToolUseID: toolCall.GetID(),
				Content: []history.ContentBlock{{
					Type: "text",
					Text: errMsg,
				}},
			})

			continue
		}

		toolResult := *toolResultPtr

		if toolResult.Content != nil {
			resultBlock := history.ContentBlock{
				Type:      "tool_result",
				ToolUseID: toolCall.GetID(),
				Content:   toolResult.Content,
			}

			var resultText string
			for _, item := range toolResult.Content {
				if contentMap, ok := item.(map[string]interface{}); ok {
					if text, ok := contentMap["text"]; ok {
						resultText += fmt.Sprintf("%v ", text)
					}
				}
			}

			resultBlock.Text = strings.TrimSpace(resultText)

			toolResults = append(toolResults, resultBlock)
		}
	}

	r.messages = append(r.messages, history.HistoryMessage{
		Role:    message.GetRole(),
		Content: messageContent,
	})

	if len(toolResults) > 0 {
		r.messages = append(r.messages, history.HistoryMessage{
			Role:    "user",
			Content: toolResults,
		})

		return r.Run(ctx, "")
	}

	return message.GetContent(), nil
}
```

Kode ini merupakan gabungan dari sebagian kode pada [berkas terkait](https://github.com/snowmerak/mcphost/blob/main/cmd/mcp.go).

Secara garis besar, alurnya adalah sebagai berikut.

1. Mengirimkan daftar tool bersama prompt untuk meminta apakah perlu dieksekusi atau untuk menghasilkan respons.
2. Jika respons telah dihasilkan, rekursi dihentikan dan respons dikembalikan.
3. Jika LLM meminta eksekusi tool, host memanggil server MCP.
4. Hasilnya ditambahkan ke riwayat, lalu kembali ke langkah 1.

## Penutup

> Sudah selesai?

Sebenarnya, tidak banyak yang perlu disampaikan. Tulisan ini dibuat untuk membantu pemahaman tentang cara kerja server MCP secara garis besar. Semoga tulisan ini sedikit membantu Anda dalam memahami cara kerja host MCP.
