---
id: a1fad13de24c90924cfa9e25e9f986cb
author: snowmerak
title: Compreendendo um pouco o MCP host
description: Este texto tem como objetivo auxiliar a compreensão do protocolo MCP da Anthropic e do host MCP (mcphost) implementado em Go.
language: pt
date: 2025-04-08T00:59:42.416582136Z
path: /blog/posts/a-little-understanding-of-mcp-host-zb952aae0
category: ai
---

## O que é o MCP

[MCP](https://modelcontextprotocol.io/introduction) é um protocolo desenvolvido pela Anthropic para o Claude. MCP é a abreviação de Model Context Protocol, sendo um protocolo que permite ao LLM solicitar ativamente ações ou recursos externos. Como o MCP é, literalmente, apenas um protocolo que oferece requisições e respostas, o processo e a execução devem ser implementados pelo desenvolvedor.

### Sobre o funcionamento interno

Antes de explicar o funcionamento interno, gostaria de abordar o [Gemini Function Calling](https://ai.google.dev/gemini-api/docs/function-calling). O Gemini Function Calling, assim como o MCP, permite que o LLM invoque ativamente ações externas. Talvez surja a dúvida sobre por que trouxemos o Function Calling. O motivo é que o Function Calling surgiu antes do MCP e, por utilizar o mesmo esquema OpenAPI, há compatibilidade entre eles, o que nos levou a supor que seus comportamentos seriam semelhantes. Dessa forma, como a explicação do Gemini Function Calling é comparativamente mais detalhada, considerou-se que ela seria útil e, por isso, foi incluída.

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

O fluxo geral é o seguinte.

1. Define-se a função.
2. Envia-se a definição da função ao Gemini juntamente com o 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. Caso necessário, o Gemini solicita uma chamada de função.
   1. Caso o Gemini julgue necessário, o chamador recebe o nome e os parâmetros para a chamada da função.
   2. O chamador pode decidir se executará ou não a chamada.
      1. Executar a chamada e retornar um valor legítimo
      2. Não executar a chamada, mas retornar dados como se a tivesse executado
      3. Simplesmente ignorar
4. O Gemini pode, nesse processo, solicitar a chamada de várias funções de uma só vez, ou, após analisar o resultado de uma função, realizar novas chamadas, executando e requisitando tais operações.
5. Quando se obtém, por fim, uma resposta organizada, o processo é encerrado.

Esse fluxo é, de modo geral, consistente com o do MCP. Isso também é descrito de maneira semelhante no [tutorial do MCP](https://modelcontextprotocol.io/tutorials/building-mcp-with-llms). O mesmo vale para o ollama tools.

E, felizmente, esses três mecanismos, ollama tools, MCP e Gemini Function Calling, compartilham praticamente a mesma estrutura de esquema, de modo que é possível utilizar os três implementando apenas o MCP.

Ah, e há uma desvantagem comum a todos. Como, em última análise, é o modelo que executa as chamadas, se o modelo que você utiliza estiver em mau estado, ele pode deixar de chamar funções, chamá-las de forma incorreta, ou até mesmo enviar um DOS ao servidor MCP, entre outros comportamentos indevidos.

## Host MCP em Go

### mcphost da mark3lab

Em Go, existe o [mcphost](https://github.com/mark3labs/mcphost), desenvolvido pela organização mark3lab.

O uso é bastante simples.

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

Após a instalação, crie o arquivo `$HOME/.mcp.json` e escreva o seguinte conteúdo:

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

Em seguida, execute com um modelo do ollama da seguinte forma.  
Claro que, antes disso, se necessário, baixe o modelo com `ollama pull mistral-small`.

> Em geral, recomendo o claude ou o qwen2.5, mas, no momento, eu recomendo o mistral-small.

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

Contudo, executando dessa forma, só é possível utilizá-lo no formato de perguntas e respostas no ambiente CLI.  
Por isso, vamos modificar o código deste `mcphost` para que ele possa operar de maneira mais programável.

### Fork do mcphost

Como já verificamos, o `mcphost` inclui a funcionalidade de extrair metadados e invocar funções por meio do MCP. Portanto, são necessárias partes responsáveis por chamar o LLM, por lidar com o servidor MCP e por gerenciar o histórico de mensagens.

O trecho correspondente a isso foi extraído para o `Runner` do pacote a seguir.

```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
}
```

Não analisaremos separadamente as declarações internas dessa parte. Elas, contudo, correspondem quase exatamente aos seus nomes.

```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,
				}},
			},
		},
	}
}
```

Para os `mcpClients` e `tools` utilizados aqui, consulte [este arquivo](https://github.com/snowmerak/mcphost/blob/main/internal/runner/from.go).  
Como utilizaremos o provider do ollama, consulte [este arquivo](https://github.com/snowmerak/mcphost/blob/main/pkg/llm/ollama/provider.go).

O prato principal é o método `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
}
```

O código em si foi montado a partir de trechos do [arquivo](https://github.com/snowmerak/mcphost/blob/main/cmd/mcp.go).

Em linhas gerais, o funcionamento é o seguinte.

1. Envia-se a lista de ferramentas junto com o prompt, solicitando a decisão de execução ou a geração de uma resposta.
2. Quando a resposta é gerada, a recursão é interrompida e ela é retornada.
3. Se o LLM solicitar a execução de uma ferramenta, o host chama o servidor MCP.
4. Adiciona-se o resultado ao histórico e retorna-se ao passo 1.

## Considerações finais

> Já acabou?

Na verdade, não há muito mais a dizer. Este texto foi escrito para ajudar a compreender, de forma geral, como o servidor MCP funciona. Espero que este conteúdo tenha contribuído, ainda que modestamente, para a compreensão do funcionamento do host MCP.
