<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Higor Morais</title>
    <description>The latest articles on DEV Community by Higor Morais (@higorae).</description>
    <link>https://dev.to/higorae</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F369030%2F276e7c7f-c8ba-4cf6-b03e-82d9439e9d7e.jpeg</url>
      <title>DEV Community: Higor Morais</title>
      <link>https://dev.to/higorae</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/higorae"/>
    <language>en</language>
    <item>
      <title>MCP na prática: Tools, Resources e quando usar cada um</title>
      <dc:creator>Higor Morais</dc:creator>
      <pubDate>Wed, 17 Jun 2026 18:03:32 +0000</pubDate>
      <link>https://dev.to/higorae/mcp-na-pratica-tools-resources-e-quando-usar-cada-um-13aa</link>
      <guid>https://dev.to/higorae/mcp-na-pratica-tools-resources-e-quando-usar-cada-um-13aa</guid>
      <description>&lt;h1&gt;
  
  
  MCP na prática: Tools, Resources e quando usar cada um
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Aprendizados de construir um servidor MCP de catálogo de cursos — da POC ao remote MCP.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  O que é o Model Context Protocol (MCP)?
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; é um padrão aberto que permite que aplicações de IA (como o Cursor, Claude Desktop ou outros hosts) se conectem a &lt;strong&gt;fontes de dados e ferramentas externas&lt;/strong&gt; de forma padronizada.&lt;/p&gt;

&lt;p&gt;Pense no MCP como uma &lt;strong&gt;tomada universal&lt;/strong&gt;: em vez de cada editor inventar sua própria integração com bancos, APIs e scripts, todos falam o mesmo protocolo — &lt;strong&gt;JSON-RPC 2.0&lt;/strong&gt; — sobre um transporte (stdio ou HTTP).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐     JSON-RPC      ┌─────────────┐     in-process    ┌─────────────┐
│   Cursor    │ ◄──────────────► │ MCP Server  │ ◄───────────────► │  Domínio    │
│  (cliente)  │   tools/call     │             │   CoursesService│  SQLite     │
│             │   resources/read │             │                 │             │
└─────────────┘                   └─────────────┘                 └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O MCP &lt;strong&gt;não substitui&lt;/strong&gt; sua lógica de negócio. Ele é a &lt;strong&gt;camada de adaptação&lt;/strong&gt; entre o modelo de linguagem e o mundo real — mas na V4 aprendemos que essa camada pode (e deve) ser fina, compartilhando o domínio com o backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  As três primitivas do MCP
&lt;/h2&gt;

&lt;p&gt;O protocolo expõe três tipos de capacidade. Entender a diferença entre elas é o ponto central deste artigo.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitiva&lt;/th&gt;
&lt;th&gt;Metáfora&lt;/th&gt;
&lt;th&gt;Quem controla&lt;/th&gt;
&lt;th&gt;Protocolo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verbo — &lt;em&gt;fazer algo&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Modelo (com supervisão humana)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tools/call&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Substantivo — &lt;em&gt;ler algo&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Aplicação / usuário&lt;/td&gt;
&lt;td&gt;&lt;code&gt;resources/read&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Template — &lt;em&gt;como fazer&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Usuário&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prompts/get&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Tools — ações invocáveis
&lt;/h3&gt;

&lt;p&gt;Tools são &lt;strong&gt;funções que o modelo pode chamar&lt;/strong&gt;. Cada tool tem nome, descrição, schema de entrada (JSON Schema via Zod) e retorna um resultado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"criar_curso"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cria um novo curso no catálogo."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cargaHoraria"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cargaHoraria"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Quando o modelo usa:&lt;/strong&gt; o usuário pede uma ação — &lt;em&gt;"crie um curso de NestJS com 12 horas"&lt;/em&gt; — e o modelo decide invocar &lt;code&gt;criar_curso&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Características:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pode ter &lt;strong&gt;efeito colateral&lt;/strong&gt; (criar, atualizar, deletar, enviar email)&lt;/li&gt;
&lt;li&gt;Retorna erro estruturado com &lt;code&gt;isError: true&lt;/code&gt; para o modelo se corrigir&lt;/li&gt;
&lt;li&gt;O humano deve estar no loop (aprovação, logs, confirmação)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources — dados passivos legíveis
&lt;/h3&gt;

&lt;p&gt;Resources são &lt;strong&gt;dados identificados por URI&lt;/strong&gt; que o host ou o modelo podem &lt;strong&gt;ler&lt;/strong&gt; para obter contexto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cursos://catalogo          → catálogo completo (JSON)
cursos://f47ac10b-58cc-... → detalhes de um curso
file:///docs/guia.md       → documento estático
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Quando o modelo usa:&lt;/strong&gt; o usuário quer &lt;strong&gt;consultar&lt;/strong&gt; informação — &lt;em&gt;"quais cursos existem?"&lt;/em&gt; — ou o host anexa o resource automaticamente ao contexto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Características:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Somente leitura&lt;/strong&gt; (por design)&lt;/li&gt;
&lt;li&gt;Identificados por &lt;strong&gt;URI&lt;/strong&gt; (esquemas customizados são permitidos)&lt;/li&gt;
&lt;li&gt;Podem ser &lt;strong&gt;fixos&lt;/strong&gt; (&lt;code&gt;cursos://catalogo&lt;/code&gt;) ou &lt;strong&gt;templates&lt;/strong&gt; (&lt;code&gt;cursos://{uuid}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Application-driven: o host decide como exibir e quando incluir no contexto&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Como usar um resource na prática
&lt;/h4&gt;

&lt;p&gt;Resources não são “executados” como tools. O fluxo é:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cliente lista resources disponíveis (&lt;code&gt;resources/list&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Cliente lê o conteúdo (&lt;code&gt;resources/read&lt;/code&gt;) passando a URI&lt;/li&gt;
&lt;li&gt;O conteúdo entra no contexto do modelo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No &lt;strong&gt;Cursor&lt;/strong&gt;, basta pedir em linguagem natural:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Leia o catálogo de cursos e me diga quantos existem."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O Cursor chama &lt;code&gt;resources/read&lt;/code&gt; com &lt;code&gt;cursos://catalogo&lt;/code&gt; internamente. Você não monta JSON-RPC manualmente.&lt;/p&gt;

&lt;p&gt;Para debug, use o &lt;strong&gt;MCP Inspector&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @modelcontextprotocol/inspector
&lt;span class="c"&gt;# Conecte em http://localhost:3000/mcp com Authorization: Bearer &amp;lt;API_KEY&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;# Aba Resources → read cursos://catalogo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Erro comum:&lt;/strong&gt; o modelo tentar chamar &lt;code&gt;listar_cursos&lt;/code&gt; — tool que existia na V2 e foi removida na V3. Se isso acontecer, reconecte o MCP no Cursor e peça explicitamente para ler o resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompts — templates reutilizáveis
&lt;/h3&gt;

&lt;p&gt;Prompts são &lt;strong&gt;modelos de instrução&lt;/strong&gt; parametrizados que guiam o modelo por um fluxo conhecido. Diferente de tools (o modelo executa) e resources (o modelo lê), prompts são &lt;strong&gt;invocados pelo usuário&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;criar-curso  titulo="Arquitetura de Software"  cargaHoraria=12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na V3.1 adicionamos o prompt &lt;code&gt;criar-curso&lt;/code&gt;, que monta um roteiro:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ler &lt;code&gt;cursos://catalogo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verificar duplicatas&lt;/li&gt;
&lt;li&gt;Validar dados&lt;/li&gt;
&lt;li&gt;Chamar &lt;code&gt;criar_curso&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Confirmar resultado&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O prompt &lt;strong&gt;não cria o curso&lt;/strong&gt; — orienta o modelo a usar resources e tools corretamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt MCP vs Cursor Skill:&lt;/strong&gt; prompts vivem no server MCP e funcionam em qualquer client compatível; skills vivem em &lt;code&gt;.cursor/skills/&lt;/code&gt; e são específicas do Cursor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quando usar:&lt;/strong&gt; fluxos repetíveis onde você quer &lt;strong&gt;consistência&lt;/strong&gt; — onboarding, checklists, workflows de revisão.&lt;/p&gt;




&lt;h2&gt;
  
  
  A regra de ouro: Tool vs Resource
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pergunta&lt;/th&gt;
&lt;th&gt;Se a resposta for sim →&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A operação &lt;strong&gt;altera&lt;/strong&gt; algo no sistema?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tool&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A operação &lt;strong&gt;só lê&lt;/strong&gt; dados existentes?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O modelo precisa &lt;strong&gt;decidir agir&lt;/strong&gt; com parâmetros?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tool&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O dado serve como &lt;strong&gt;contexto de referência&lt;/strong&gt;?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Há &lt;strong&gt;validação complexa&lt;/strong&gt; ou efeito colateral?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Tool&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O host pode &lt;strong&gt;anexar automaticamente&lt;/strong&gt; ao chat?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exemplo concreto: catálogo de cursos
&lt;/h3&gt;

&lt;p&gt;Na &lt;strong&gt;V2&lt;/strong&gt; do nosso projeto, tínhamos três tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listar_cursos   → leitura (mas exposta como Tool)  ✗
buscar_curso    → leitura (mas exposta como Tool)  ✗
criar_curso     → escrita ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na &lt;strong&gt;V3&lt;/strong&gt;, separamos corretamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;cursos://catalogo     → leitura do catálogo&lt;/span&gt;
  &lt;span class="s"&gt;cursos://{uuid}       → leitura de um curso&lt;/span&gt;

&lt;span class="na"&gt;Tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;criar_curso           → escrita&lt;/span&gt;
  &lt;span class="s"&gt;atualizar_curso       → escrita&lt;/span&gt;
  &lt;span class="s"&gt;arquivar_curso        → escrita&lt;/span&gt;

&lt;span class="na"&gt;Prompts (V3.1)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;criar-curso           → roteiro guiado de criação&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Por que mudamos?&lt;/strong&gt; Porque &lt;code&gt;listar_cursos&lt;/code&gt; e &lt;code&gt;buscar_curso&lt;/code&gt; não tinham efeito colateral — eram consultas disfarçadas de ações. Isso gerava redundância: o modelo podia escolher entre duas formas de fazer a mesma coisa, sem critério claro. Na prática, vimos o erro &lt;code&gt;-32602: Tool listar_cursos not found&lt;/code&gt; quando conversas antigas ou regras desatualizadas ainda referenciavam as tools removidas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quando usar Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Use Tool quando...
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cenário&lt;/th&gt;
&lt;th&gt;Exemplo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Criar dados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;criar_curso({ titulo, cargaHoraria })&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Atualizar dados&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;atualizar_curso({ id, cargaHoraria: 10 })&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ações de domínio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;arquivar_curso({ id })&lt;/code&gt; — não é DELETE, é ação de negócio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operações com validação&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rejeitar curso arquivado, validar campos obrigatórios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integrações externas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enviar email, chamar webhook, processar pagamento&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cálculos ou transformações&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gerar relatório, converter formato&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ❌ Não use Tool quando...
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cenário&lt;/th&gt;
&lt;th&gt;Use em vez disso&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Listar dados sem efeito colateral&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resource (&lt;code&gt;cursos://catalogo&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consultar um registro por ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resource (&lt;code&gt;cursos://{uuid}&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expor documentação estática&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resource (&lt;code&gt;docs://api-reference&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Anexar contexto ao chat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resource (application-driven)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Quando usar Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Use Resource quando...
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cenário&lt;/th&gt;
&lt;th&gt;Exemplo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Catálogo ou lista de referência&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cursos://catalogo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Detalhe de entidade por URI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cursos://{uuid}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentação, schemas, configs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;file:///README.md&lt;/code&gt;, &lt;code&gt;schema://database&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dados que mudam pouco e servem de contexto&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Políticas, glossários, FAQs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;O host precisa descobrir o que existe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;resources/list&lt;/code&gt; + &lt;code&gt;resources/templates/list&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ❌ Não use Resource quando...
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cenário&lt;/th&gt;
&lt;th&gt;Use em vez disso&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operação que altera estado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Busca com filtros complexos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool (ex.: &lt;code&gt;buscar_cursos_por_categoria&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ação que exige confirmação humana&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processamento ou cálculo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Resource fixo vs template
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo&lt;/th&gt;
&lt;th&gt;URI&lt;/th&gt;
&lt;th&gt;Quando usar&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fixo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cursos://catalogo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dado único, endereço conhecido&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Template&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cursos://{uuid}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parametrizado, N instâncias&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Quando usar Prompts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Use Prompt quando...
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Existe um &lt;strong&gt;fluxo repetível&lt;/strong&gt; com parâmetros conhecidos&lt;/li&gt;
&lt;li&gt;Você quer &lt;strong&gt;consistência&lt;/strong&gt; na forma como o modelo aborda uma tarefa&lt;/li&gt;
&lt;li&gt;O usuário invoca explicitamente (slash command, palette)&lt;/li&gt;
&lt;li&gt;Quer portabilidade entre clients MCP (Cursor, Claude Desktop, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Não use Prompt quando...
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A tarefa é &lt;strong&gt;ad hoc&lt;/strong&gt; e imprevisível → deixe o modelo usar tools/resources livremente&lt;/li&gt;
&lt;li&gt;O fluxo depende de &lt;strong&gt;muitas variáveis dinâmicas&lt;/strong&gt; → tools são mais flexíveis&lt;/li&gt;
&lt;li&gt;Precisa de comportamento contínuo do agente → use Cursor Skill&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Transporte: stdio vs Streamable HTTP
&lt;/h2&gt;

&lt;p&gt;O MCP define &lt;strong&gt;como&lt;/strong&gt; cliente e server se comunicam. Isso é independente de Tools/Resources.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transporte&lt;/th&gt;
&lt;th&gt;Como funciona&lt;/th&gt;
&lt;th&gt;Quando usar&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;stdio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cursor spawna processo; JSON-RPC via stdin/stdout&lt;/td&gt;
&lt;td&gt;Dev local offline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Streamable HTTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server HTTP remoto; POST com JSON-RPC&lt;/td&gt;
&lt;td&gt;Produção, múltiplos clientes, zero install&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  stdio — dev local (V4)
&lt;/h3&gt;

&lt;p&gt;Mantido na V4 para desenvolvimento offline. O processo MCP acessa SQLite diretamente — sem backend HTTP intermediário.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcp-cursos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}/apps/mcp/dist/mcp.js"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"DATABASE_PATH"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}/data/cursos.db"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vantagens:&lt;/strong&gt; funciona offline, debug rápido, sem auth.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Limitação:&lt;/strong&gt; processo local — cada dev precisa do repo e build.&lt;/p&gt;
&lt;h3&gt;
  
  
  Streamable HTTP — remote MCP (V4)
&lt;/h3&gt;

&lt;p&gt;Na V4, o backend expõe MCP diretamente em &lt;code&gt;POST /mcp&lt;/code&gt;. O Cursor conecta por URL — sem spawnar processo, sem &lt;code&gt;npx&lt;/code&gt;, sem build local.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcp-cursos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer dev-api-key"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vantagens:&lt;/strong&gt; distribuição trivial (URL + API key), um deploy serve N clientes, Docker-ready.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Modo V4:&lt;/strong&gt; stateless — cada request cria server + transport novos. Sessões stateful ficam para V5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auth:&lt;/strong&gt; &lt;code&gt;Authorization: Bearer &amp;lt;API_KEY&amp;gt;&lt;/code&gt; no header. Mesma chave do backend/Docker.&lt;/p&gt;




&lt;h2&gt;
  
  
  Arquitetura: a evolução do adaptador
&lt;/h2&gt;

&lt;h3&gt;
  
  
  V2 — MCP fino + REST intermediária
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cursor → apps/mcp (stdio) → HTTP REST → apps/backend → SQLite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Funcionou para aprender, mas tinha &lt;strong&gt;dois contratos&lt;/strong&gt; (REST + MCP) e latência desnecessária.&lt;/p&gt;

&lt;h3&gt;
  
  
  V4 — domínio compartilhado, MCP como interface única
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;libs/courses-domain    CoursesService + TypeORM + migrations
        │
libs/mcp-server        createMcpServer(courses)
        │
        ├─ apps/mcp      stdio + SQLite (dev)
        └─ apps/backend  Streamable HTTP /mcp + SQLite (produção)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Mudanças-chave na V4:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST &lt;code&gt;/mcp/v1/cursos&lt;/code&gt; &lt;strong&gt;removida&lt;/strong&gt; — MCP é a única interface pública&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;libs/courses-domain&lt;/code&gt; — regras de negócio compartilhadas&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;libs/mcp-server&lt;/code&gt; — registro de resources, tools e prompts em um lugar&lt;/li&gt;
&lt;li&gt;stdio e remote têm &lt;strong&gt;paridade garantida&lt;/strong&gt; — mesma lib, mesmo comportamento&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Por quê removemos a REST?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dois contratos = dupla manutenção&lt;/li&gt;
&lt;li&gt;O adaptador MCP fazia HTTP para chamar o próprio backend — hop desnecessário&lt;/li&gt;
&lt;li&gt;Distribuição simplifica: usuário final precisa só de URL + API key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;strong&gt;spec MCP não prescreve&lt;/strong&gt; como o server fala com seus dados. REST, gRPC ou in-process — escolha de implementação. Na V4, escolhemos &lt;strong&gt;in-process&lt;/strong&gt; com lib compartilhada.&lt;/p&gt;




&lt;h2&gt;
  
  
  Distribuir seu MCP
&lt;/h2&gt;

&lt;p&gt;Com Streamable HTTP, distribuir fica simples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Público&lt;/th&gt;
&lt;th&gt;O que distribuir&lt;/th&gt;
&lt;th&gt;Config do usuário&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Devs / open source&lt;/td&gt;
&lt;td&gt;Backend Docker + docs&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;url&lt;/code&gt; + &lt;code&gt;headers&lt;/code&gt; no mcp.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Times internos&lt;/td&gt;
&lt;td&gt;Imagem GHCR + API key por time&lt;/td&gt;
&lt;td&gt;docker run + mcp.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usuários finais&lt;/td&gt;
&lt;td&gt;Backend hosted por você&lt;/td&gt;
&lt;td&gt;Só URL + chave&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Próximo passo (não implementado):&lt;/strong&gt; publicar no npm com &lt;code&gt;npx -y @org/mcp-cursos&lt;/code&gt; para stdio, e registrar no &lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP Registry&lt;/a&gt; para descoberta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;stdio vs remote para distribuição:&lt;/strong&gt; remote vence — o usuário cola 10 linhas de JSON e funciona. stdio exige Node, clone e build.&lt;/p&gt;




&lt;h2&gt;
  
  
  Evolução do nosso projeto: V1 → V4
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Versão&lt;/th&gt;
&lt;th&gt;Transporte&lt;/th&gt;
&lt;th&gt;Leitura&lt;/th&gt;
&lt;th&gt;Escrita&lt;/th&gt;
&lt;th&gt;Prompts&lt;/th&gt;
&lt;th&gt;Persistência&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;V1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stdio&lt;/td&gt;
&lt;td&gt;Tools (&lt;code&gt;listar&lt;/code&gt;, &lt;code&gt;buscar&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Tool (&lt;code&gt;criar&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Mock in-memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;V2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stdio → REST&lt;/td&gt;
&lt;td&gt;Tools (&lt;code&gt;listar&lt;/code&gt;, &lt;code&gt;buscar&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Tool (&lt;code&gt;criar&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;SQLite + backend HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;V3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stdio → REST&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Resources&lt;/strong&gt; (&lt;code&gt;cursos://…&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Tools (&lt;code&gt;criar&lt;/code&gt;, &lt;code&gt;atualizar&lt;/code&gt;, &lt;code&gt;arquivar&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;SQLite + soft delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;V3.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stdio → REST&lt;/td&gt;
&lt;td&gt;Resources&lt;/td&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;criar-curso&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;V4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Streamable HTTP&lt;/strong&gt; + stdio dev&lt;/td&gt;
&lt;td&gt;Resources&lt;/td&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;&lt;code&gt;criar-curso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Domínio compartilhado, REST removida&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cada versão ensinou algo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;V1→V2:&lt;/strong&gt; persistência real exige backend separado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V2→V3:&lt;/strong&gt; leitura ≠ tool; resources tornam o server previsível&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V3→V3.1:&lt;/strong&gt; prompts guiam fluxos repetíveis sem duplicar tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V3→V4:&lt;/strong&gt; REST intermediária era overhead; remote MCP elimina install local&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Decisões de domínio que impactam o MCP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Arquivamento (soft delete)
&lt;/h3&gt;

&lt;p&gt;Em vez de deletar cursos, &lt;strong&gt;arquivamos&lt;/strong&gt; — &lt;code&gt;arquivado: true&lt;/code&gt;. O curso some do catálogo ativo, mas continua consultável por URI.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Onde&lt;/th&gt;
&lt;th&gt;Comportamento&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cursos://catalogo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lista só cursos &lt;strong&gt;ativos&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;cursos://{uuid}&lt;/code&gt; arquivado&lt;/td&gt;
&lt;td&gt;Retorna curso com &lt;code&gt;arquivado: true&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;atualizar_curso&lt;/code&gt; em arquivado&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bloqueado&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;arquivar_curso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tool — ação explícita de domínio&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Atualização parcial
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;atualizar_curso&lt;/code&gt; aceita &lt;code&gt;titulo&lt;/code&gt; e/ou &lt;code&gt;cargaHoraria&lt;/code&gt; — pelo menos um obrigatório. Evita reenviar dados desnecessários.&lt;/p&gt;




&lt;h2&gt;
  
  
  Notificações: subscribe e listChanged
&lt;/h2&gt;

&lt;p&gt;A spec MCP permite que o server &lt;strong&gt;notifique&lt;/strong&gt; o cliente quando resources mudam (&lt;code&gt;resources/subscribe&lt;/code&gt;, &lt;code&gt;resources/listChanged&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Com subscribe:&lt;/strong&gt; após &lt;code&gt;criar_curso&lt;/code&gt;, o Cursor poderia atualizar &lt;code&gt;cursos://catalogo&lt;/code&gt; automaticamente no contexto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sem subscribe (nossa V4):&lt;/strong&gt; o modelo relê o resource quando o usuário pede — &lt;em&gt;"mostra o catálogo atualizado"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Para a maioria dos casos, &lt;strong&gt;relê quando necessário&lt;/strong&gt; é suficiente. Subscribe + sessões stateful (V5) fazem sentido quando o catálogo muda constantemente e o host precisa de refresh automático.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checklist rápido para seu próximo MCP
&lt;/h2&gt;

&lt;p&gt;Antes de implementar, pergunte:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;O que é leitura?&lt;/strong&gt; → Resource com URI clara&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O que é ação?&lt;/strong&gt; → Tool com schema validado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existe fluxo repetível?&lt;/strong&gt; → Prompt parametrizado&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O MCP fala direto com o banco?&lt;/strong&gt; → Prefira lib de domínio compartilhada; evite REST intermediária se MCP é a única interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stdio ou HTTP?&lt;/strong&gt; → stdio para dev offline; Streamable HTTP para distribuição&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preciso de notificações?&lt;/strong&gt; → Comece sem; adicione com sessões stateful se necessário&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools de leitura coexistem com Resources?&lt;/strong&gt; → Evite redundância — causa confusão no modelo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Como distribuir?&lt;/strong&gt; → Remote MCP (URL + auth) &amp;gt; npm + stdio &amp;gt; clone + build&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol — Documentação oficial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/docs/learn/server-concepts" rel="noopener noreferrer"&gt;Understanding MCP servers — Tools, Resources, Prompts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/server/tools" rel="noopener noreferrer"&gt;Specification: Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/server/resources" rel="noopener noreferrer"&gt;Specification: Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP Registry&lt;/a&gt; — catálogo oficial de servers&lt;/li&gt;
&lt;li&gt;Repositório de referência: projeto &lt;strong&gt;mcp-cursos&lt;/strong&gt; — tags &lt;code&gt;V1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;, &lt;code&gt;v3&lt;/code&gt;, &lt;code&gt;v3.1&lt;/code&gt;, &lt;code&gt;v4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ADRs: &lt;code&gt;docs/adr/&lt;/code&gt; — decisões de arquitetura documentadas&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;MCP não é magia — é &lt;strong&gt;protocolo&lt;/strong&gt;. Tools são verbos, Resources são substantivos, Prompts são roteiros. Separar leitura de escrita torna seu server previsível para o modelo e mais fácil de manter para você.&lt;/p&gt;

&lt;p&gt;A jornada do &lt;strong&gt;mcp-cursos&lt;/strong&gt; resumida:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Comece simples&lt;/strong&gt; — stdio, poucas tools, mock ou API mínima&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separe leitura de escrita&lt;/strong&gt; — resources para consulta, tools para ação&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adicione prompts&lt;/strong&gt; — roteiros para fluxos repetíveis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vá para remote&lt;/strong&gt; — Streamable HTTP elimina fricção de distribuição&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compartilhe o domínio&lt;/strong&gt; — uma lib, paridade entre stdio e remote&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Guia de Configuração: Zsh, Oh My Zsh e Atuin</title>
      <dc:creator>Higor Morais</dc:creator>
      <pubDate>Tue, 21 Apr 2026 12:25:38 +0000</pubDate>
      <link>https://dev.to/higorae/guia-de-configuracao-zsh-oh-my-zsh-e-atuin-3eeb</link>
      <guid>https://dev.to/higorae/guia-de-configuracao-zsh-oh-my-zsh-e-atuin-3eeb</guid>
      <description>&lt;p&gt;Aqui está um tutorial completo sobre como instalar o Oh My Zsh (junto com os plugins mais populares) e configurar o Atuin para o histórico do shell.&lt;/p&gt;

&lt;p&gt;No final deste guia, você encontrará um &lt;code&gt;Makefile&lt;/code&gt; que automatiza essas instalações para que você possa replicar facilmente essa configuração em qualquer sistema baseado em Unix (Linux ou macOS).&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Pré-requisitos
&lt;/h2&gt;

&lt;p&gt;Certifique-se de ter o &lt;code&gt;zsh&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt; e &lt;code&gt;git&lt;/code&gt; instalados no seu sistema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Ubuntu/Debian:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;zsh curl git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No macOS (via Homebrew):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;zsh curl git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defina o Zsh como seu shell padrão:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chsh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;which zsh&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Instalação do Oh My Zsh
&lt;/h2&gt;

&lt;p&gt;Execute o script oficial de instalação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Instalando Plugins do Zsh
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Nota: Como não consegui ler automaticamente seus plugins específicos do &lt;code&gt;.zshrc&lt;/code&gt;, incluí os dois plugins da comunidade mais essenciais e populares. Você pode adicionar outros facilmente à lista!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. zsh-autosuggestions&lt;/strong&gt; (Sugere comandos enquanto você digita com base no histórico)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/zsh-users/zsh-autosuggestions &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/plugins/zsh-autosuggestions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. zsh-syntax-highlighting&lt;/strong&gt; (Destaca a sintaxe dos comandos enquanto são digitados)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/zsh-users/zsh-syntax-highlighting.git &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/plugins/zsh-syntax-highlighting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configuração:&lt;/strong&gt;&lt;br&gt;
Abra seu arquivo &lt;code&gt;~/.zshrc&lt;/code&gt; e atualize o array &lt;code&gt;plugins&lt;/code&gt; para incluí-los (junto com o &lt;code&gt;git&lt;/code&gt;), assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    git
    zsh-autosuggestions
    zsh-syntax-highlighting
    &lt;span class="c"&gt;# Adicione seus outros plugins aqui!&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida, recarregue seu shell: &lt;code&gt;source ~/.zshrc&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Instalação e Configuração do Atuin
&lt;/h2&gt;

&lt;p&gt;O &lt;a href="https://github.com/atuinsh/atuin" rel="noopener noreferrer"&gt;Atuin&lt;/a&gt; substitui seu histórico de shell existente por um banco de dados SQLite, permitindo sincronização criptografada e recursos incríveis de pesquisa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instalação:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://setup.atuin.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configuração:&lt;/strong&gt;&lt;br&gt;
O Atuin deve se adicionar automaticamente ao seu &lt;code&gt;~/.zshrc&lt;/code&gt;. Se não o fizer, adicione esta linha no FINAL do seu &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;atuin init zsh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sincronização e Configuração de Conta (Opcional, mas recomendado):&lt;/strong&gt;&lt;br&gt;
Se você quiser fazer backup e sincronizar seu histórico entre computadores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Registre uma conta&lt;/span&gt;
atuin register &lt;span class="nt"&gt;-u&lt;/span&gt; &amp;lt;USUARIO&amp;gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &amp;lt;EMAIL&amp;gt;

&lt;span class="c"&gt;# Ou se você já tem uma conta, faça login:&lt;/span&gt;
atuin login &lt;span class="nt"&gt;-u&lt;/span&gt; &amp;lt;USUARIO&amp;gt;

&lt;span class="c"&gt;# Importe seu histórico existente&lt;/span&gt;
atuin import auto

&lt;span class="c"&gt;# Sincronize&lt;/span&gt;
atuin &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Configuração Automatizada via Makefile
&lt;/h2&gt;

&lt;p&gt;Para facilitar as coisas em qualquer sistema baseado em Unix, crie um arquivo chamado &lt;code&gt;Makefile&lt;/code&gt; e cole o conteúdo a seguir.&lt;/p&gt;

&lt;p&gt;Este Makefile verifica as dependências, instala o Oh My Zsh, clona os plugins, instala o Atuin e fornece um comando para atualizar automaticamente o seu &lt;code&gt;.zshrc&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Makefile para configuração do Zsh, Oh My Zsh, Plugins e Atuin
&lt;/span&gt;
&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all check-deps install-zsh install-oh-my-zsh install-plugins install-atuin configure-zshrc&lt;/span&gt;

&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;check-deps install-oh-my-zsh install-plugins install-atuin configure-zshrc&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"======================================================="&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Instalação concluída! Por favor, reinicie seu terminal ou execute:"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"exec zsh"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"======================================================="&lt;/span&gt;

&lt;span class="nl"&gt;check-deps&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verificando dependências..."&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; git &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2 &lt;span class="s2"&gt;"git é necessário, mas não está instalado. Abortando."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; curl &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2 &lt;span class="s2"&gt;"curl é necessário, mas não está instalado. Abortando."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; zsh &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2 &lt;span class="s2"&gt;"zsh é necessário. Por favor, instale o Zsh primeiro."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nl"&gt;install-oh-my-zsh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Instalando Oh My Zsh..."&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;HOME/.oh-my-zsh"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;RUNZSH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$$(&lt;/span&gt;&lt;span class="s2"&gt;curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Oh My Zsh já está instalado."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nl"&gt;install-plugins&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Instalando Plugins do Zsh..."&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$${&lt;/span&gt;&lt;span class="s2"&gt;ZSH_CUSTOM:-&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;HOME/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/zsh-autosuggestions"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        git clone https://github.com/zsh-users/zsh-autosuggestions &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$${&lt;/span&gt;&lt;span class="s2"&gt;ZSH_CUSTOM:-&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;HOME/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/zsh-autosuggestions"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"zsh-autosuggestions já está instalado."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$${&lt;/span&gt;&lt;span class="s2"&gt;ZSH_CUSTOM:-&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;HOME/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/zsh-syntax-highlighting"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        git clone https://github.com/zsh-users/zsh-syntax-highlighting.git &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$${&lt;/span&gt;&lt;span class="s2"&gt;ZSH_CUSTOM:-&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;HOME/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/plugins/zsh-syntax-highlighting"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"zsh-syntax-highlighting já está instalado."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nl"&gt;install-atuin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Instalando o Atuin..."&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; atuin &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://setup.atuin.sh | sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Atuin já está instalado."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nl"&gt;configure-zshrc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configurando ~/.zshrc..."&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"plugins=(git)"&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;HOME/.zshrc&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;.bak &lt;span class="s1"&gt;'s/plugins=(git)/plugins=(git zsh-autosuggestions zsh-syntax-highlighting)/'&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;HOME/.zshrc&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Plugins atualizados no ~/.zshrc"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"atuin init zsh"&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;HOME/.zshrc&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'eval "&lt;/span&gt;&lt;span class="p"&gt;$$(&lt;/span&gt;&lt;span class="s1"&gt;atuin init zsh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s1"&gt;"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$$&lt;/span&gt;HOME/.zshrc&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Inicialização do Atuin adicionada ao ~/.zshrc"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Como usar o Makefile:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Salve o bloco de código acima como &lt;code&gt;Makefile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Abra o seu terminal no diretório onde você o salvou.&lt;/li&gt;
&lt;li&gt;Execute o comando: &lt;code&gt;make&lt;/code&gt; ou &lt;code&gt;make all&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ele configurará tudo perfeitamente para você!&lt;/p&gt;

</description>
      <category>automation</category>
      <category>cli</category>
      <category>tooling</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Resgatando a Performance do Meu Macos com Docker Distribuído</title>
      <dc:creator>Higor Morais</dc:creator>
      <pubDate>Mon, 13 Oct 2025 13:40:20 +0000</pubDate>
      <link>https://dev.to/higorae/resgatando-a-performance-do-meu-macos-com-docker-distribuido-3kd4</link>
      <guid>https://dev.to/higorae/resgatando-a-performance-do-meu-macos-com-docker-distribuido-3kd4</guid>
      <description>&lt;p&gt;Trabalhar com Docker é essencial para o desenvolvimento moderno, mas, em máquinas com recursos limitados, a ferramenta pode se tornar um pesadelo. Recentemente, enfrentei o desafio de conciliar a praticidade dos containers com a performance sofrível do meu macbook.&lt;/p&gt;

&lt;p&gt;Com pouco espaço em disco e o alto consumo de recursos gerado pelo docker desktop, o sistema operacional e outras ferramentas essenciais, como o cursor (que também demanda memória), frequentemente sofriam. O resultado era um dia a dia de trabalho marcado por lentidão e travamentos.&lt;/p&gt;

&lt;p&gt;A solução? Mover o processamento. Decidi transformar uma pequena máquina que eu tinha parada em casa em um servidor docker dedicado, aliviando a carga do meu mac e recuperando a performance.&lt;/p&gt;

&lt;p&gt;Adotei uma estratégia para gerenciar meus containers no servidor remoto (&lt;code&gt;homeserver&lt;/code&gt;) com a mesma facilidade de um ambiente local, baseada em recursos nativos do sistema: contextos docker e chaves SSH configuradas.&lt;/p&gt;

&lt;p&gt;Abaixo, detalho os caminhos que tomei para alcançar um acesso remoto rápido, seguro e eficiente, liberando os recursos do meu Mac para o que realmente importa.&lt;/p&gt;




&lt;h3&gt;
  
  
  Passo 1: A Solução de Performance – Contextos Docker para Alívio de Recursos
&lt;/h3&gt;

&lt;p&gt;O primeiro passo foi abraçar a ferramenta nativa do Docker para alternar entre ambientes: &lt;strong&gt;Contextos Docker&lt;/strong&gt;. Esta foi a escolha ideal, pois me permite gerenciar meus containers no servidor remoto (&lt;code&gt;homeserver&lt;/code&gt;) com os mesmos comandos que uso localmente, mantendo a flexibilidade de voltar para o ambiente &lt;code&gt;default&lt;/code&gt; (minha máquina) quando necessário, como em viagens.&lt;/p&gt;

&lt;p&gt;Ao fazer isso, movi o pesado gerenciamento da virtual machine do docker desktop e o consumo de recursos dos containers para o servidor dedicado, deixando o mac livre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Como configurar o Contexto Docker:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gere as Chaves SSH&lt;/strong&gt; (Conforme o Passo 2, abaixo).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Crie o Contexto Remoto:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker context create homeserver &lt;span class="nt"&gt;--docker&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ssh://user@homeserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Para usar o servidor remoto:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker context use homeserver
&lt;span class="c"&gt;# Agora, 'docker ps' mostra os containers do servidor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Para voltar à sua máquina local (e reverter o consumo de recursos):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker context use default
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Passo 2: O Fim das Senhas – Chaves SSH para Autenticação Rápida
&lt;/h3&gt;

&lt;p&gt;Para garantir que a troca de contexto fosse instantânea — e para permitir que o Contexto Docker funcionasse sem interrupções —, a autenticação por senha precisava ser eliminada. A solução foi o uso de chaves SSH.&lt;/p&gt;

&lt;p&gt;Após gerar meu par de chaves, eu copiei a chave pública para o servidor remoto, permitindo que a conexão fosse estabelecida instantaneamente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gerar o par de chaves (local):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Copiar a chave pública (para o servidor):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id user@homeserver
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;Com as chaves configuradas, o Docker pode se conectar ao daemon remoto sem me pedir a senha, tornando a alternância de contexto imediata e fluida.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Passo 3: Acessando os Serviços Docker como "Localhost" com &lt;code&gt;~/.ssh/config&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Mudar o Docker para o servidor resolve o problema de performance, mas eu ainda precisava acessar os serviços (como um banco de dados ou um Localstack) de forma conveniente.&lt;/p&gt;

&lt;p&gt;Para isso, configurei o arquivo &lt;code&gt;~/.ssh/config&lt;/code&gt; para automatizar a criação de túneis SSH (Port Forwarding), permitindo que eu acesse, por exemplo, o PostgreSQL rodando no servidor simplesmente digitando &lt;code&gt;localhost:5432&lt;/code&gt; no meu cliente local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Minha configuração no arquivo &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="k"&gt;Host&lt;/span&gt; homeserver
    &lt;span class="c1"&gt;# O endereço IP ou DNS real do seu servidor (Substitua!)&lt;/span&gt;
    &lt;span class="k"&gt;HostName&lt;/span&gt; [IP-DO-HOMESERVER] 

    &lt;span class="c1"&gt;# Usuário SSH na máquina remota&lt;/span&gt;
    &lt;span class="k"&gt;User&lt;/span&gt; user

    &lt;span class="c1"&gt;# Porta SSH (padrão)&lt;/span&gt;
    &lt;span class="k"&gt;Port&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt; 

    &lt;span class="c1"&gt;# 1. Túnel para a porta 5432 (Ex: PostgreSQL)&lt;/span&gt;
    &lt;span class="c1"&gt;# Porta local 5432 aponta para a porta 5432 no homeserver&lt;/span&gt;
    &lt;span class="k"&gt;LocalForward&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt; localhost:5432 

    &lt;span class="c1"&gt;# 2. Túnel para a porta 4566 (Ex: Serviço AWS Localstack)&lt;/span&gt;
    &lt;span class="c1"&gt;# Porta local 4566 aponta para a porta 4566 no homeserver&lt;/span&gt;
    &lt;span class="k"&gt;LocalForward&lt;/span&gt; &lt;span class="m"&gt;4566&lt;/span&gt; localhost:4566 

    &lt;span class="c1"&gt;# Garante que a conexão não caia por inatividade&lt;/span&gt;
    &lt;span class="k"&gt;ServerAliveInterval&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
    &lt;span class="k"&gt;ServerAliveCountMax&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

    &lt;span class="c1"&gt;# Opcional: Não abre um terminal, apenas mantém o túnel ativo&lt;/span&gt;
    &lt;span class="k"&gt;RequestTTY&lt;/span&gt; &lt;span class="no"&gt;no&lt;/span&gt;

    &lt;span class="c1"&gt;# Opcional: Configuração para autenticação de host (em caso de IP estático)&lt;/span&gt;
    &lt;span class="k"&gt;StrictHostKeyChecking&lt;/span&gt; &lt;span class="no"&gt;no&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Para ativar o túnel e acessar os serviços:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; homeserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusão
&lt;/h3&gt;

&lt;p&gt;Ao combinar a conveniência dos &lt;strong&gt;Contextos Docker&lt;/strong&gt; para o gerenciamento de containers e a segurança dos &lt;strong&gt;Túneis SSH configurados&lt;/strong&gt; para o acesso aos serviços, consegui não apenas um ambiente de desenvolvimento mais saudável, mas também salvei meu tempo de vida evitando irritações desnecessárias com a falta de recursos da minha máquina.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>performance</category>
      <category>productivity</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
