Todo mundo está dissecando o MCP como se fosse a Pedra Rosetta da IA. Diagramas! Whitepapers! Líderes de pensamento fazendo TikToks! Mas onde, meus queridos druidas de dados, estão os pacotes reais? nobody's showing the actual packets. É como tentar aprender a cirurgia a partir de um cartaz motivacional.Claro, o quadro teórico é *inspirador*, mas eu quero ver o sangue e os intestinos!Onde está o JSON bruto?Onde estão os dejetos de stdin/stdout?Como eu deveria animar minha criação quando eu nem posso ver o relâmpago? É como tentar aprender a cirurgia a partir de um cartaz motivacional.Claro, o quadro teórico é *inspirador*, mas eu quero ver o sangue e os intestinos!Onde está o JSON bruto?Onde estão os dejetos de stdin/stdout?Como eu deveria animar minha criação quando eu nem posso ver o relâmpago? Tentar aprender um protocolo sem descarte de fios é como tentar aprender a cercar de um PowerPoint. Give me the electrons or give me death. Agora que temos o nosso mantra, vamos cavar! Esse é o seu modelo de negócio? Seu Plano de Negócios MCP: Conheça o MCP O que é um milagre? (um milagre ocorre) Esse lucro Soa familiar? Claro que é! É o equivalente de arranque de transformar chumbo em ouro. Alquimia, mas com muito mais JSON. Felizmente... # Você não está sozinho! Parece que linhas de tinta foram derramadas sobre este chamado Protocolo de Contexto Modelo. Todo mundo está falando sobre os LLMs evoluindo, chamando ferramentas, chamando funções, bidirecional isso e capacidade que. eu, é claro, não sou exceção. Mas uma coisa continuou cravando no meu tronco cerebral enquanto passei por este caminho alegre de lenha de teoria e abstração: "What's this look like ON THE WIRE???" Todo o processo é documentado em Mas eu não quero filosofia, eu quero Eu quero me sentir como o Dr. Frankenstein tendendo ao meu golem, relâmpago e tudo. ModeloContextoProtocolo.io see Ai de mim, um mero morador entre os que poderiam ser iluminados, até que eu me abri assim! Observações iniciais do campo O MCP é muito, muito novo. A borda sangrando. Possivelmente ainda sangrando. Talvez um dia, livros inteiros serão escritos sobre sua política interna, como algum tipo de convenção constitucional digital. Mas, por enquanto, uma coisa se destaca acima de tudo: O Calling Tool é tudo o que você precisa Se ampliarmos o caminho, o caminho para este caso de uso único e crítico - Podemos ignorar a Podemos simplificar até estarmos olhando para o belo esqueleto do sistema: apenas quatro tipos de mensagens para a saída e três para a entrada. (Explicaremos por que os números não correspondem exatamente um pouco mais tarde.) tool invocation muito Inicialização Inicialização Ferramentas / Lista Ferramentas / Call (e os 3 resultados que retornam) Você pode entender todo o protocolo sabendo sobre essas sete mensagens. Por exemplo, o tempo (de ) ModeloContextoProtocolo.io ModeloContextoProtocolo.io Nós usaremos — um exemplo de servidor MCP simples que permite que você pesquise dados de previsão e alerta. Você pode encontrá-lo em seu GitHub aqui: [ weather.py https://github.com/modelcontextprotocol/quickstart-resources/blob/main/weather-server-python/weather.py Vamos falar sobre o que realmente acontece "no fio" quando você fala sobre esta coisa. Primeiro de tudo, o MCP Isso significa que a especificação não se importa como você se conecta ao servidor. HTTPS? Sockets da Web? Tubos nomeados? Tapping em um antigo cabo de vampiro Ethernet? Não importa. Agnóstico do transporte. Na prática, o método mais comum é lançar o servidor como um subprocesso e comunicar e Isso lhe dá um bom canal de comunicação privado. (Tecnicamente duplex completo, mas se você adicionar , vamos chamá-lo de 1.5-duplex para ser bonito). stdin stdout stderr Como as mensagens fluem O MCP usa o JSON-RPC 2.0 sobre o fio. Isso lhe dá um protocolo de solicitação/resposta, além de notificações. Cada mensagem é enviada como uma por linha de JSON. É como um serviço de telegrama digital onde cada linha é um pacote completo de significado. É isso na especificação? Alguma coisa; não realmente. A especificação a deixa aberta. Mas este JSON delimitado pela nova linha é o idioma padrão. O mito de "stdin / stdout considerado prejudicial" Sim, você pode encontrar algumas postagens dramáticas no blog que afirmam que esse padrão é "perigoso" ou "não confiável". Parece assustador!Não queremos iniciar um incêndio no laboratório, não é?Por que as pessoas diriam isso se não fosse verdade? Você vê, em Ye Olden Times, linhas extras longas podem causar superfluxos de tampão (olá, Morris Worm). Felizmente, a maioria dos softwares modernos não sofre deste problema (tanto quanto! Os dedos cruzados!). Os analistas JSON modernos são rápidos e resilientes. Quer enviar um alerta meteorológico de 256 MB? Vá para ele. Esses avisos geralmente vêm de pessoas que ligam manualmente descritores de arquivos em C. Você não está fazendo isso. um módulo que foi construído especificamente para esse tipo de tarefa. subprocess # fork off an MCP subprocess import subprocess as sp # Redirect stderr to /dev/null stderr = sp.DEVNULL # Start the weather.py process process = sp.Popen( ["uv", "run", "weather.py"], stdin=sp.PIPE, stdout=sp.PIPE, stderr=stderr ) Fácil, certo? Todos nativos de Python. Sem pacotes extras. Baterias incluídas. O O módulo substitui os fluxos I/O da criança por tubos, dando-lhe domínio completo.Também podemos enviar os nossos novos sinais de processo, se assim o desejarmos. subprocess Agora, estamos finalmente prontos para fazer a ciência do protocolo como uma proper digital necromancer! Título Original: It's Embarrassingly Simple O MCP, em todas as suas regalias enterprise-robe-and-scepter, é apenas isso: YOU: "Hello, I speak robot." SERVER: "Delightful. I also speak robot." YOU: "Excellent, we both can speak robot!" YOU: "What tricks can you do?" SERVER: "I can juggle and summon storms." YOU: "Storm, please!" SERVER: "⛈️ As you wish." Todo este protocolo é apenas uma maneira muito formal de pedir a alguém para fazer uma coisa e obter a confirmação de que eles fizeram isso. O resto é apenas cerimônia de classe empresarial e cola brilhante em torno deste abraço bonito e simples. 7 Intercâmbio de interesses. Isso é isso. Colocando o monstro juntos: um protótipo de trabalho Vamos gerar um servidor meteorológico e interrogá-lo como um cientista louco. Dica #1: A melhor maneira de entender um protocolo é abusá-lo até que ele grite, em seguida, patch-lo até que ele coopera. Dica #1: A melhor maneira de entender um protocolo é abusá-lo até que ele grite, em seguida, patch-lo até que ele coopera. Dica #2: Se você torturar seus LLMs, eu não serei responsável pelo que acontece com você em The After Times When AI Taketh Over! Dica #2: Se você torturar seus LLMs, eu não serei responsável pelo que acontece com você em The After Times When AI Taketh Over! Passo 1: Convocando nosso Minion Digital # Behold! We create life! # (fork off an MCP subprocess) import subprocess # Start the weather.py process # Use `uv run` to execute the script in the uv environment # Redirect stdin, stdout to PIPE for communication # Redirect stderr to DEVNULL to suppress error messages process = subprocess.Popen( ["uv", "run", "weather.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr) # Behold! We create life! process = subprocess.Popen( ["uv", "run", "weather.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL # Silence the screams ) O O módulo substitui os fluxos de I/O da criança por tubos, dando-lhe controle total sobre a conversa.É lindamente simples: agora você tem um processo dedicado de criança em seu beck and call, pronto para executar o que você lançar nele. subprocess Você pode até mesmo enviar sinais para realmente mostrar quem é o chefe. Sob condições normais, quando o processo parental sai, a criança desce com o navio - então você não tem que se preocupar com processos zumbis envolvendo seu sistema (bem, na maioria das vezes). Você notará que estamos usando para o processo. não é necessário, mas está rapidamente se tornando o padrão de ouro para ferramentas modernas de Python. Se você ainda não o verificou, você definitivamente deve - é um game-changer. Quick aside uv uv Se você ainda está usando pip, precisamos conversar. Se você ainda estiver usando Nós precisamos conversar. pip Passo 2: O primeiro encontro incômodo Todo bom relacionamento começa com a identificação mútua.Vamos começar anunciando-nos (como um cavalheiro): # Define the initialize request init_request = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": { "name": "MyMCPClient", "version": "1.0.0" } } } Esta é a nossa O primeiro pacote que enviamos para estabelecer a comunicação diz ao servidor três coisas cruciais: falamos de MCP, que versão estamos usando e que capacidades trazemos para a mesa. Inauguração Salvo Você notará que isso segue o formato JSON-RPC 2.0, e isso será consistente durante toda a nossa conversa com o servidor. Let's fire it off. import json def send_mcp_request(process, request): """Sends a JSON-RPC request to the subprocess's stdin.""" json_request = json.dumps(request) + '\n' # Add newline delimiter process.stdin.write(json_request.encode('utf-8')) process.stdin.flush() # Ensure the data is sent immediately # 1. Send the initialize request print("Sending initialize request...") send_mcp_request(process, init_request) Devemos ver este resultado: Sending initialize request... Agora, vamos ouvir o que o servidor tem a dizer sobre tudo até agora: def read_mcp_response(process): """Reads a JSON-RPC response from the subprocess's stdout.""" # Assuming the server sends one JSON object per line line = process.stdout.readline().decode('utf-8') if line: print(" . . . len is", len(line)) return json.loads(line) return None print("Sending initialized request...") send_mcp_request(process, notified_request) O servidor, sendo educado, apresenta-se de volta: . . . len is 266 Received response after initialization:{'id': 1, 'jsonrpc': '2.0', 'result': {'capabilities': {'experimental': {}, 'prompts': {'listChanged': False}, 'resources': {'listChanged': False, 'subscribe': False}, 'tools': {'listChanged': False}}, 'protocolVersion': '2025-03-26', 'serverInfo': {'name': 'weather', 'version': '1.9.4'}}} "Eu sou um servidor meteorológico, eu não mudarei aleatoriamente minhas habilidades no meio da conversa, e eu definitivamente não vou te fantasma." Translation Ele está compartilhando informações de versão do protocolo e detalhes básicos, mas o O prêmio é o verdadeiro prêmio. So, what's the server actually telling us here? capabilities Estamos ignorando as capacidades para esta demonstração, mas verifique isso: nós Assine os eventos "listChanged" se o nosso servidor fosse o tipo para adicionar ou remover ferramentas dinamicamente.Há realmente um pequeno sistema de pub / sub escondido no protocolo MCP - você pode ouvir todos os tipos de eventos.Nosso servidor weather.py é muito simples para qualquer coisa fantástica, mas está lá se você precisar. Poderia O mesmo acontece com os “prompts” e os “recursos” – estamos ignorando-os completamente.Claro, poderíamos rodar nossa própria implementação, mas isso perde o ponto de vista de A ideia é que diferentes sistemas lidem com preocupações diferentes, para que você não tenha que reinventar cada roda.Você pode escolher e escolher quais partes do protocolo para implementar, mas se você quiser brincar bem com outras ferramentas de MCP, você melhor ficar com a especificação. API separation Alright, we're connected and ready to rock, right? Wrong. O servidor ainda está sentado lá, tocando seu pé digital, esperando por nós para completar o aperto de mão. notified_request = { "jsonrpc": "2.0", "method": "notifications/initialized" } Esse é o jeito de JSON-RPC dizer "fogo e esqueça" - nenhuma resposta esperada ou necessária. Ao contrário da dança de solicitação / resposta usual que estamos fazendo, esta é uma Pense nisso como enviando um pacote ACK: "Hey servidor, eu estou pronto para rolar!" Notice the missing id notification Nenhuma identidade significa: "Não responda, eu confio que você saiba o que fazer com essas informações." Nenhuma identidade significa: "Não responda, eu confio que você saiba o que fazer com essas informações." Enquanto isso... de volta ao rancho... (de volta à história!) Enquanto isso... de volta ao rancho... (de volta à história!) Lembre-se que o servidor, sendo razoavelmente cortês, já respondeu com um manifesto de suas habilidades. Então vamos confirmar: Na verdade, estamos prontos para a festa. # yes we are indeed ready to party # Acknowledge the server so it knows we approve print("// Sending initialized request...") send_mcp_request(process, notified_request) Agora, o servidor sabe começar a esperar pedidos. Passo 3: “Mostre-me o que você tem” Tempo para ver quais brinquedos este servidor trouxe para o playground: tools_list_request = { "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": { } } # 2. Send the tools/list request print("// Sending tools/list request...") send_mcp_request(process, tools_list_request) Nós conseguimos exatamente o resultado que esperávamos...Perfeito! // Sending tools/list request... Agora, vamos ler o resultado... É hora de ver quais tesouros descobrimos: # Read the server's response to the tools/list request tools_list_response = read_mcp_response(process) print("// Received tools list response:", end='') pprint(tools_list_response) E nosso servidor orgulhosamente exibe suas mercadorias: previsões, alertas e outros males meteorológicos. . . . len is 732 // Received tools list response:{'id': 2, 'jsonrpc': '2.0', 'result': {'tools': [{'description': 'Get weather alerts for a US state.\n' '\n' ' Args:\n' ' state: Two-letter US state code ' '(e.g. CA, NY)\n' ' ', 'inputSchema': {'properties': {'state': {'title': 'State', 'type': 'string'}}, 'required': ['state'], 'title': 'get_alertsArguments', 'type': 'object'}, 'name': 'get_alerts'}, {'description': 'Get weather forecast for a location.\n' '\n' ' Args:\n' ' latitude: Latitude of the ' 'location\n' ' longitude: Longitude of the ' 'location\n' ' ', 'inputSchema': {'properties': {'latitude': {'title': 'Latitude', 'type': 'number'}, 'longitude': {'title': 'Longitude', 'type': 'number'}}, 'required': ['latitude', 'longitude'], 'title': 'get_forecastArguments', 'type': 'object'}, 'name': 'get_forecast'}]}} Whoa! isso é um JSON! OK, vamos cavar nas coisas boas. muito Essa é a nossa mina de ouro - cada objeto representa uma ferramenta que o LLM pode invocar. (Torneio da trama: também podemos chamá-los, o que é exatamente o que estamos fazendo agora!) See that tools Fato engraçado: Os campos de ‘descrição’ são como o seu LLM decide qual função chamar.Pense nisso como Tinder para ferramentas, com a sua IA olhando para o seu telefone tentando decidir se deve deslizar para a esquerda ou para a direita Fato engraçado: Os campos de ‘descrição’ são como o seu LLM decide qual função chamar.Pense nisso como Tinder para ferramentas, com a sua IA olhando para o seu telefone tentando decidir se deve deslizar para a esquerda ou para a direita : OpenAI originalmente tentou marcar isso como "chamada de função" - o que é... Mas em algum lugar ao longo do caminho, a indústria decidiu coletivamente que "ferramentas" soavam mais frescas (ou talvez mais acessíveis?), e agora chamamos toda a dança de "chamada de ferramentas". One interesting side note Tecnicamente Anatomy of a tool (pay attention, this is where the magic lives): Nome: Nome real da função — nenhum tipo permitido aqui! Este é o nome definitivo, canônico da ferramenta; vamos usá-lo para se referir a esta ferramenta quando a chamamos. Descrição: Inglês simples para o LLM ler (isso é literalmente o que a IA olha quando decide se deve usar sua ferramenta) InputSchema: JSON Schema definindo quais parâmetros você precisa OutputSchema: Conspicuamente ausente! Tudo só retorna uma "grande cadeia" e espera pelo melhor Nome Descrição INTRODUÇÃO OutputSistema Porque estamos todos basicamente retornando JSON embrulhado em cordas e fingindo que é uma decisão de design. Isso realmente faz sentido quando você considera a evolução de "chamadas de funções" para "chamadas de ferramentas". as funções tradicionais podem retornar qualquer tipo, enquanto as ferramentas de linha de comando do Unix (o que o nome de alguma forma implica) apenas pulam texto. Alguns modelos até têm switches para forçar a saída JSON, então, em teoria, seu LLM poderia esperar dados estruturados a cada vez. Então, novamente, as ferramentas podem retornar texto simples, CSV, HTML, ou realmente qualquer coisa. modelos multimodais podem até mesmo retornar áudio, imagens, vídeo ou feeds de detecção de objetos ao vivo - as possibilidades são maravilhosamente caóticas. Bem, temos nossa caixa de ferramentas carregada. Vamos fazer nossa primeira chamada de ferramentas MCP! Passo 4: O Momento da Verdade De qualquer forma, temos uma lista de ferramentas, agora o que? vamos chamar uma! Escolhemos uma ferramenta. Vamos escolher para manter as coisas simples, já que não precisamos de latitude e longitude. Seria uma escolha melhor. get_alerts get_forecast tools_call_request = { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "get_alerts", "arguments": { "state": "TX" } } } Eu escolhi o Texas porque tudo é maior lá, incluindo os desastres meteorológicos. Espere, segure-se – por que é “ferramentas/chamadas” e não “ferramentas/chamadas”? Um Ok, certo, "ferramenta / chamada" soaria mais natural em inglês, mas aparentemente, a consistência com os outros endpoints ganha. Com todos os nossos patos de dados em uma fileira agora, podemos pressionar o Botão Vermelho Grande. # 3. Send the tools/call request print("// Sending tools/call request...") send_mcp_request(process, tools_call_request) # Read the server's response to the tools/call request tools_call_response = read_mcp_response(process) print("// Received tools call response:", end='') pprint(tools_call_response) [BEEP BEEP BOOP BOOP] (Este é o som que o Big Red Button faz) ] [Drumroll please O pensamento do servidor... processamento... e... e... ] [And the crowd goes wild! Voilà! dados meteorológicos reais se materializam. Alertas, inundações, tornados, os trabalhos. Tudo embrulhado em JSON estruturado, assim como seu terapeuta ordenou: (cortado, ninguém quer ver 11 páginas de lixo JSON) // Sending tools/call request... . . . len is 51305 // Received tools call response:{'id': 3, 'jsonrpc': '2.0', 'result': {'content': [{'text': '\n' 'Event: Flood Advisory\n' 'Area: Hidalgo, TX; Willacy, TX\n' 'Severity: Minor\n' 'Description: * WHAT...Flooding caused by ' 'excessive rainfall continues.\n' '\n' '* WHERE...A portion of Deep South Texas, ' 'including the following\n' 'counties, Hidalgo and Willacy.\n' '\n' '* WHEN...Until 245 PM CDT.\n' '\n' '* IMPACTS...Minor flooding in low-lying and ' 'poor drainage areas.\n' '\n' '* ADDITIONAL DETAILS...\n' '- At 205 PM CDT, Doppler radar indicated ' 'heavy rain due to\n' 'thunderstorms. Minor flooding is ongoing or ' 'expected to begin\n' 'shortly in the advisory area. Between 2 and ' '5 inches of rain\n' 'have fallen.\n' '- Additional rainfall amounts up to 1 inch ' 'are expected over\n' 'the area. This additional rain will result ' 'in minor flooding.\n' '- Some locations that will experience ' 'flooding include...\n' 'Harlingen, Elsa, Edcouch, La Villa, Lasara, ' 'La Villa High\n' 'School, Monte Alto, Jose Borrego Middle ' 'School, Satiago\n' 'Garcia Elementary School, Edcouch Police ' 'Department, Edcouch\n' 'City Hall, Edcouch Volunteer Fire ' 'Department, Edcouch-Elsa\n' 'High School, Laguna Seca, Carlos Truan ' 'Junior High School,\n' 'Elsa Police Department, Lyndon B Johnson ' 'Elementary School,\n' 'Elsa Public Library, Olivarez and Lasara ' 'Elementary School.\n' '- http://www.weather.gov/safety/flood\n' "Instructions: Turn around, don't drown when " 'encountering flooded roads. Most flood\n' '\n' 'The next statement will be issued Tuesday ' 'morning at 830 AM CDT.\n', 'type': 'text'}], 'isError': False}} Observe que Verifique sempre este campo, a menos que você goste de depurar falhas misteriosas às 3 da manhã. Victory! isError: false Parece bom! estamos recebendo dados limpos de cadeia de volta (não streaming para complicar as coisas), o que nos dá opções.Podemos analisar esses dados meteorológicos e massageá-los para o LLM, ou simplesmente passar a resposta bruta e deixar o modelo descobrir.A maioria das implementações vai com a última abordagem - por que fazer trabalho extra quando os LLMs são bastante bons em analisar texto estruturado? Mas se você estiver construindo algo sofisticado, o pré-processamento da saída da ferramenta pode ser incrivelmente poderoso.Você pode formatá-lo, filtrá-lo, combiná-lo com outras fontes de dados ou transformá-lo exatamente no que seu aplicativo precisa. Nós registramos com sucesso um servidor MCP, inicializamos a conexão, chamamos uma ferramenta e processamos os resultados. And that's a wrap! The Full Monty: Seu Cliente MCP Completo Aqui está todo o ritual glorioso em um círculo de convocação: import subprocess import json from pprint import pprint def send_mcp_request(process, request): json_request = json.dumps(request) + '\n' process.stdin.write(json_request.encode('utf-8')) process.stdin.flush() def read_mcp_response(process): line = process.stdout.readline().decode('utf-8') return json.loads(line) if line else None requests = { 'init': { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": {"name": "MyMCPClient", "version": "1.0.0"} } }, 'initialized': { "jsonrpc": "2.0", "method": "notifications/initialized" }, 'list_tools': { "jsonrpc": "2.0", "id": 2, "method": "tools/list" }, 'call_tool': { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "get_alerts", "arguments": {"state": "TX"}} } } process = subprocess.Popen( ["uv", "run", "weather.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ) try: send_mcp_request(process, requests['init']) pprint(read_mcp_response(process)) send_mcp_request(process, requests['initialized']) send_mcp_request(process, requests['list_tools']) tools = read_mcp_response(process) print("Available tools:", [t['name'] for t in tools['result']['tools']]) send_mcp_request(process, requests['call_tool']) result = read_mcp_response(process) print("Weather alert received:", len(result['result']['content'][0]['text']), "characters") finally: process.terminate() A Grande Revelação You just built an MCP client using nothing but Python's standard library. Sem frameworks. Sem dependências externas. Sem magia. Apenas tubos de subprocessamento e JSON – as mesmas ferramentas que você teve desde Python 2.7. Seja Claude falando com seu banco de dados, GPT-4 chamando suas APIs, ou a "plataforma revolucionária de fluxo de trabalho de IA" de alguma startup - por baixo de tudo isso, é apenas isso: Spawn process. Send JSON. Read JSON. Repeat. É como descobrir o Mágico de Oz é apenas um cara com um sistema de som realmente bom. É como descobrir o Mágico de Oz é apenas um cara com um sistema de som realmente bom. Os próximos passos para a loucura Agora que você viu a barriga da besta (em código), você pode: Construa servidores MCP personalizados que façam sua oferta (não mais esperando que outra pessoa escreva a integração) Debug MCP conexões como um necromancer de rede quando eles inevitavelmente quebrar no pior momento possível Projetar melhores ferramentas sabendo exatamente como os LLMs consomem seus metadados Escreva melhores ferramentas tão irresistivelmente descritas que seus LLMs se apaixonem Otimize o inferno de tudo porque você entende o protocolo acima de tudo Ou apenas automatizar seu alimentador de gato. eu não julgo. A Bela Revelação The miracle in step 2 of your business plan? It was you, all along. O milagre no passo 2 do seu plano de negócios? Construa algo estranho. Quer ver este código em ação? O exemplo completo vive aqui: [https://gitlab.com/-/snippets/4864350] https://gitlab.com/-/snippets/4864350 https://gitlab.com/-/snippets/4864350?embedable=true