DEV Community

Cover image for Construindo um MCP Server do Cartola FC com Golang
Arthur Isvi S. Santos
Arthur Isvi S. Santos

Posted on

Construindo um MCP Server do Cartola FC com Golang

Introdução

Por definições oficiais, o MCP (Model-Context Protocol) é um protocolo aberto e universal que padroniza a forma como aplicações de IA interagem com dados e serviços externos, a fim de alimentar os LLMs com contexto adicional.

Em resumo, é um protocolo que permite que aplicações ampliem o "conhecimento" de uma IA, por meio da disponibilização de ferramentas e dados adicionais em tempo real. Imagine poder perguntar a uma IA qualquer: "Quais são as rotas que mais gasto no Uber?" ou "Qual foi o valor total das minhas corridas no último mês?". Possivelmente esses dados são disponibilizados na própria API do Uber, e para tal seria necessário um desenvolvedor para extrair "manualmente" as informações integrando a API. A ideia do MCP é possibilitar que o próprio agente de IA adquira a capacidade de consumir a API e responder a essas perguntas.

"Pense no MCP como uma porta USB-C para aplicativos de IA. Assim como o USB-C fornece uma maneira padronizada de conectar seus dispositivos a diversos periféricos e acessórios. O MCP fornece uma maneira padronizada de conectar modelos de IA a diferentes fontes e ferramentas de dados". (Anthropic, 2024)

Antes de começarmos, é importante conhecer alguns dos conceitos básicos do protocolo:

Image description
Fonte Imagem

  • Server: é o componente responsável por expor um contexto adicional, por exemplo, acesso a fonte de dados ou funcionalidade externa. Ele é responsável por executar as solicitações recebidas do agente de IA (clientes).
  • Client: aplicações ou agentes de IA que utiliza o MCP Server para acessar ferramentas e recursos adicionais, expandindo suas capacidades.
  • Tools: funcionalidades específicas fornecidas pelo MCP Server à IA.

Para mais informações sobre o protocolo, recomendo visitar a documentação oficial.

Neste artigo, vamos demonstrar como criar um MCP Server em Golang que utiliza a API pública do Cartola FC. Com ele, um agente de IA será capaz de responder perguntas sobre o Fantasy Game, como as pontuações de jogadores em rodadas atuais ou específicas.

O que a aplicação vai fazer?

Nosso objetivo é criar um MCP Server que consuma a API oficial do Cartola FC para permitir que um agente de IA responda a seguinte pergunta e suas variações:

"Qual a pontuação do jogador Fulano na rodada atual"?

A aplicação servidora vai expor Tools específicas que possibilitam a IA responder a esta pergunta, entretanto mais Tools podem ser construídas a fim de responder outras questões. Caso queira desenvolver outras tools, segue referência da api utilizada.

Implementação do MCP Server

MCP com Golang

Embora o MCP seja um protocolo genérico, até o momento não existe uma SDK oficial para Golang. No entanto, bibliotecas como metoro-io/mcp-golang e mark3labs/mcp-go oferecem implementações práticas. Para este artigo, escolhemos a mcp-golang por questões didáticas, mas qualquer uma das duas poderia ser utilizada.

Dito isso, vou mostrar como foi construído o Server para o Cartola FC:

package main

import (
    "encoding/json"
    "io"
    "log"
    "net/http"
    "strconv"

    mcp_golang "github.com/metoro-io/mcp-golang"
    "github.com/metoro-io/mcp-golang/transport/stdio"
)

const (
    API_URL = "https://api.cartolafc.globo.com"
)

type PlayerScoreDefaultInput struct{}

type DefaultResponse struct {
    Content string `json:"content"`
}

type PlayerScoreInputByRound struct {
    Round int `json:"round"`
}

func main() {
    done := make(chan struct{})
     // Criação da instância do Server
    server := mcp_golang.NewServer(stdio.NewStdioServerTransport())

    // Criação das tools específicas
    err := server.RegisterTool("pontuacoes_jogadores",
        "Retorna a pontuação parcial de todos os jogadores na rodada atual", handlePontuacoesJogadores)

    if err != nil {
        log.Fatalf("Erro ao registrar tool: %v", err)
    }

    err = server.RegisterTool("pontuacoes_jogadores_por_rodada",
        "Retorna a pontuação de todos os jogadores na rodada informada", handlePontuacoesJogadoresPorRodada)

    if err != nil {
        log.Fatalf("Erro ao registrar tool: %v", err)
    }

    // Sobe o servidor
    err = server.Serve()
    if err != nil {
        log.Fatalf("Erro ao iniciar servidor: %v", err)
    }

    <-done
}
Enter fullscreen mode Exit fullscreen mode

Perceba que a função main é responsável por criar a instância do servidor MCP, registrar as Tools e executar o servidor.

Tools

pontuacoes_jogadores: Retorna a pontuação parcial de todos os jogadores na rodada atual.

pontuacoes_jogadores_por_rodada: Retorna a pontuação de todos os jogadores em uma rodada específica informada.

Esse é o ponto central do nosso MCP, é o trecho de código capaz de dar contexto adicional a IA e fazer com que seja possível responder as perguntas específicas sobre o Cartola FC. Perceba que esse contexto é definido explicitamente por nós. Caso quiséssemos que a IA fosse capaz de responder sobre outras questões, como por exemplo, a pontuação de um time, deveríamos criar uma tool específica para tal.

Por sua vez, cada tool precisa executar uma função, neste caso estamos o fazendo em handlePontuacoesJogadores e handlePontuacoesJogadoresPorRodada:

func handlePontuacoesJogadores(params *PlayerScoreDefaultInput) (*mcp_golang.ToolResponse, error) {
    response, err := getPlayersScores()
    if err != nil {
        return nil, err
    }
    return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(response)), nil
}

func handlePontuacoesJogadoresPorRodada(params *PlayerScoreInputByRound) (*mcp_golang.ToolResponse, error) {
    response, err := getPlayersScoresByRound(params.Round)
    if err != nil {
        return nil, err
    }
    return mcp_golang.NewToolResponse(mcp_golang.NewTextContent(response)), nil
}

Enter fullscreen mode Exit fullscreen mode

Basicamente para ambas as tools é realizado uma consulta na API do Cartola FC e retornado as informações, um deles utilizando o parâmetro da rodada e o outro não.

É válido mencionar que não é realizado nenhum tratamento em código para o retorno do endpoint, o que acaba implicando que o cliente tenha acesso apenas aos dados de maneira "crua", assim como retornado pela API oficial.

Se fosse o caso, poderíamos ter feito tratamentos no nosso código para por exemplo buscar pelo nome do jogador e assim "otimizar" o trabalho realizado pelo LLM, já retornando a informação.

func getPlayersScores() (string, error) {
    url := API_URL + "/atletas/pontuados"
    resp, err := http.Get(url)
    if err != nil {
        log.Printf("Erro ao fazer requisição para API: %v", err)
        return "", err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Printf("Erro ao ler resposta da API: %v", err)
        return "", err
    }

    response := &DefaultResponse{
        Content: string(body),
    }

    jsonResponse, err := json.Marshal(response)
    if err != nil {
        log.Printf("Erro ao converter resposta para JSON: %v", err)
        return "", err
    }

    return string(jsonResponse), nil
}

func getPlayersScoresByRound(round int) (string, error) {
    url := API_URL + "/atletas/pontuados/" + strconv.Itoa(round)
    resp, err := http.Get(url)
    if err != nil {
        log.Printf("Erro ao fazer requisição para API: %v", err)
        return "", err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        log.Printf("Erro ao ler resposta da API: %v", err)
        return "", err
    }

    response := &DefaultResponse{
        Content: string(body),
    }

    jsonResponse, err := json.Marshal(response)
    if err != nil {
        log.Printf("Erro ao converter resposta para JSON: %v", err)
        return "", err
    }

    return string(jsonResponse), nil
}

Enter fullscreen mode Exit fullscreen mode

Resultados

Agora é a hora da verdade. Farei uma comparação da interação do antes e depois com um agente de IA (para tal utilizei o Cursor AI) para verificar se o MCP Server construído foi efetivo ou não.

Antes

Image description

Depois

Image description

Percebe-se que sem a conexão com o MCP Server, a IA procura por contextos externos (acesso a internet) para responder a pergunta solicitada, entretanto mesmo sugerindo a requisição a API, acaba não sendo efetiva na sua resposta.

Por sua vez, com o MCP Server disponibilizando o contexto correto, a IA desenvolve a capacidade de executar as tools específicas e assim responder as perguntas mencionadas, com velocidade e assertividade.

Conclusão

Apesar de ser um exemplo didático, a aplicação serviu como uma boa prova de conceito para demonstrar o potencial que se espera de uma IA utilizando o protocolo MCP. Pense nisso no âmbito da integração de grandes sistemas, no consumo de grandes fontes de dados e consequentemente na capacidade do próprio agente de IA consumir informações em tempo real para adquirir mais contexto da situação. Em suma, o MCP vem com o objetivo fundamental de simplificar como os agentes de IA interagem com dados externos.

Código produzido também no Github

Referências

Model Context Protocol - Anthropic
Model Context Protocol (MCP) Para Sistemas de IA Generativa – Conceito, Aplicações e Desafios
Criando um MCP Server usando Go - Elton Minetto
Código Fonte TV

Top comments (3)

Collapse
 
yagohf profile image
Yago Ferreira

Que artigo legal! Parabéns pela ideia.

Collapse
 
arthur_isvi profile image
Arthur Isvi S. Santos

Muito obrigado! Feliz que tenha gostado :)

Collapse
 
manito_maciel_7dcd2653752 profile image
Manito Maciel

Ótimo artigo Arthur 👏👏👏