← Ver a árvore completa

ROTA · 6 CLASSES · 1 GRUPOS

Servidor MCP em Go

Implemente protocolo, capacidades, ferramentas, recursos e testes de um servidor MCP.

Pré-requisitos orientam, mas nunca bloqueiam. Cinco classes fundamentais e o bônus Go 1.27 são grátis com conta.

O ARCO DA ROTA

01IA

ORDEM RECOMENDADA

6 classes, do primeiro passo ao fechamento.

Cada classe leva cerca de 15–25 minutos no novo formato de slides. Você pode sair da ordem quando outro nó fizer mais sentido.

  1. 01
    Servidor MCP em GoO protocolo: mensagens JSON-RPC pela entrada padrão

    JSON-RPC 2.0, IDs, notificações e o enquadramento por linha.

  2. 02
    Servidor MCP em GoHandshake e capabilities: negocie antes de servir

    initialize, versão negociada e anúncio honesto de capabilities.

  3. 03
    Servidor MCP em GoExpondo tools: descreva, valide e execute ações

    tools/list, tools/call e resultados estruturados sem efeitos escondidos.

  4. 04
    Servidor MCP em GoExpondo resources: ofereça contexto por URI

    resources/list, resources/read, MIME type e URI estável.

  5. 05
    Servidor MCP em GoTestando seu servidor MCP em memória

    sessões determinísticas com strings.Reader, bytes.Buffer e falhas observáveis.

  6. 06
    Servidor MCP em GoCapstone: servidor MCP completo para demonstrar

    um servidor de catálogo com handshake, tool, resource e transporte em memória.

PRÉVIA DA PRIMEIRA CLASSE

O protocolo: mensagens JSON-RPC pela entrada padrão

scanner := bufio.NewScanner(input) // uma linha = uma mensagem
encoder := json.NewEncoder(output)
for scanner.Scan() {
	var request struct {
		ID json.RawMessage `json:"id"`
	}
	json.Unmarshal(scanner.Bytes(), &request)
	if len(request.ID) == 0 { continue } // sem id: notificação silenciosa
	encoder.Encode(map[string]any{"jsonrpc": "2.0", "id": request.ID})
}

Pense em duas pessoas passando fichas por uma abertura estreita. Cada ficha precisa dizer qual regra segue, qual trabalho pede e, quando houver resposta, qual número permite associar pergunta e retorno. Se uma ficha vier cortada ao meio ou duas forem coladas, o receptor não sabe onde uma termina. Um servidor MCP por entrada e saída padrão enfrenta exatamente essa fronteira: recebe mensagens JSON-RPC 2.0 completas, uma por linha, e escreve mensagens completas sem misturar diagnóstico.

JSON-RPC é o envelope, não o catálogo MCP. O campo jsonrpc fixa a versão "2.0"; method nomeia a operação; params carrega argumentos; id correlaciona uma requisição com sua resposta. MCP define métodos e formatos dentro desse envelope. Essa separação ajuda a diagnosticar: JSON quebrado é erro de parse, método desconhecido é erro de JSON-RPC e argumentos inválidos pertencem à operação.

Uma mensagem com id é requisição e exige resposta. Uma mensagem sem id é notificação e não recebe resposta, inclusive quando o processamento falha. O valor zero é um ID válido; testar id == 0 para reconhecer notificação perde requisições reais. Em Go, json.RawMessage preserva número ou string e permite distinguir ausência pelo comprimento.

No transporte stdio do MCP, cada mensagem ocupa uma linha e não pode conter quebra de linha embutida. json.Encoder.Encode acrescenta o delimitador; bufio.Scanner encontra cada entrada. O stdout pertence ao protocolo. Um fmt.Println de depuração nesse writer vira uma falsa mensagem e derruba o cliente; diagnósticos devem ir a stderr em um binário real.

Antes de executar, preveja quantas linhas aparecem na saída e quais IDs voltam. Depois troque o ID numérico por "req-a" e confirme que ele permanece string. Faça mais duas mudanças: acrescente uma notificação sem id e injete uma linha JSON inválida entre duas requisições. Explique por que a notificação não cria resposta e por que o erro de parse usa null.

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

func main() {
	input := strings.NewReader("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"ping\"}\n")
	scanner := bufio.NewScanner(input)
	var output bytes.Buffer
	encoder := json.NewEncoder(&output)
	for scanner.Scan() {
		var message map[string]any
		if json.Unmarshal(scanner.Bytes(), &message) == nil {
			encoder.Encode(map[string]any{"jsonrpc": "2.0", "id": message["id"], "result": map[string]any{}})
		}
	}
	fmt.Print(output.String())
}

Este programa reduz o transporte ao seu núcleo observável. Os tipos preservam o ID bruto e impedem que a camada de domínio precise conhecer scanner ou delimitador.

Os imports cobrem enquadramento, JSON e uma demonstração em memória.

A introdução termina aqui

Entre para continuar nos slides, rodar o código e enfrentar os desafios.

Começar pela rota →

ESTA ROTA E TODAS AS OUTRAS

Qualquer plano abre os 192 nós.

Mensal R$19,90 · anual R$149 · lifetime R$499. Todos têm garantia de 7 dias.

Comparar os 3 planos →Começar pelas classes grátis