Alle dissekerer MCP som om det er Rosetta Stone af AI. Diagrammer! whitepapers! tanke ledere laver TikToks! Men hvor, mine kære data druider, er de faktiske pakker? her er hvad der driver mig nuts: nobody's showing the actual packets. Det er som at forsøge at lære kirurgi fra en motiverende plakat. Sikker på, at den teoretiske ramme er *inspirerende*, men jeg vil se blodet og tarmene! Hvor er den rå JSON? Hvor er de stdin/stdout dumps? Hvordan skal jeg animere min skabelse, når jeg ikke engang kan se lynet? Det er som at forsøge at lære kirurgi fra en motiverende plakat. Sikker på, at den teoretiske ramme er *inspirerende*, men jeg vil se blodet og tarmene! Hvor er den rå JSON? Hvor er de stdin/stdout dumps? Hvordan skal jeg animere min skabelse, når jeg ikke engang kan se lynet? At forsøge at lære en protokol uden tråd dumps er som at forsøge at lære hegn fra en PowerPoint. Give me the electrons or give me death. Nu hvor vi har vores mantra, lad os grave ind! Er det din forretningsmodel? Din MCP forretningsplan: Lær MCP Er der sket et mirakel? (og så sker der et mirakel) Denne fortjeneste Lyder bekendt? Selvfølgelig gør det! Det er start-up svarende til at gøre bly til guld. Alkemi, men med en MANGE mere JSON. Heldigvis... #Du er ikke alene! Det ser ud til, at striber af blæk er blevet udgydt over denne såkaldte Model Context Protocol. Alle taler om LLMs udvikler, værktøj opkald, funktion opkald, bidirektionel dette og evnen til det. jeg, selvfølgelig, er ingen undtagelse. "What's this look like ON THE WIRE???" Hele protokollen er dokumenteret i Men jeg vil ikke have filosofi, jeg vil have Jeg vil gerne føle mig som Dr. Frankenstein tendens til min golem, lyn og alt. Ikke vade gennem en anden semantisk haystack af uklar BNF-smagede syntax grammatik. Modelkontekstprotokoll.io see Ve mig, en blottet beboer blandt dem, der ville blive oplyst, indtil jeg knækkede det åbne mig selv på den måde! Indledende observationer fra marken MCP er meget, meget nyt. Blødende kant. Muligvis stadig blødende. Måske en dag, hele bøger vil blive skrevet om dens interne politik, som en slags digital konstitutionel konvention. Men foreløbig er der først og fremmest én ting: Tool Calling er alt hvad du behøver Hvis vi zoomer vej, vej ind på denne enkelt, kritiske brugssag - Vi kan ignorere a Vi kan forenkle, indtil vi ser på det smukke skelet af systemet: kun fire meddelelsestyper til output, og tre til input. (Vi vil forklare, hvorfor tallene ikke matcher nøjagtigt lidt senere.) tool invocation Lotte Initialiserer Initialiseret Værktøjer / liste Værktøjer / Call (Og de 3 resultater, der kommer tilbage) Du kan forstå hele protokollen ved at kende om disse syv beskeder. Eksempel på vejr (fra ) Modelkontekstprotokoll.io Modelkontekstprotokoll.io Vi vil bruge — en simpel MCP server eksempel, der giver dig mulighed for at forespørge forudsigelse og advarselsdata. Du kan finde det i deres GitHub her: [ Det er weather.py https://github.com/modelcontextprotocol/quickstart-resources/blob/main/weather-server-python/weather.py Lad os tale om, hvad der faktisk sker "på ledningen", når du taler til denne ting. Først ud: MCP er Det betyder, at specifikationen ikke er ligeglad med, hvordan du opretter forbindelse til serveren. HTTPS? Web Sockets? Navngivne rør? Tapping ind i en gammel Ethernet vampyrkabel? En agnostiker af transport. I praksis er den mest almindelige metode at starte serveren som en underproces og kommunikere over og Dette giver dig en dejlig lille privat kommunikationskanal. (Teknisk set fuld duplex, men hvis du tilføjer , vi vil kalde det 1.5-duplex for at være sød). stdin stdout stderr Hvordan meddelelser flyder MCP bruger JSON-RPC 2.0 over tråden. Dette giver dig en anmodning/svar protokol, plus meddelelser. Det er som en digital telegramtjeneste, hvor hver linje er en komplet pakke af betydning. Er dette i spec? En slags; ikke rigtig. spec efterlader det åbent. Men denne newline-afgrænsede JSON er standardidiomet. Myten om "stdin / stdout betragtes som skadeligt" Ja, du kan finde nogle dramatiske blogindlæg, der hævder, at dette mønster er "farligt" eller "upålideligt." Vi ønsker ikke at starte en brand i laboratoriet, gør vi ikke? Du kan se, i Ye Olden Times kan ekstra lange linjer forårsage bufferoverstrømninger (hello, Morris Worm). Heldigvis lider de fleste moderne software ikke af dette problem (så meget! Fingers krydset!). Moderne JSON-analysatorer er både hurtige og modstandsdygtige. Vil du sende en 256 MB vejrvarsel? Gå for det. Bare... måske ikke gøre det hvert sekund. Disse advarsler kommer normalt fra folk, der manuelt fører filbeskrivere i C. Du gør ikke det. Modul, der er bygget præcis til denne type opgave. 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 ) Alle Python native. ingen ekstra pakker. Batterier inkluderet. Den Modulet erstatter barnets I/O-strømme med rør, hvilket giver dig fuldstændig dominans. subprocess Nu er vi endelig klar til at gøre protokol videnskab som en proper digital necromancer! Plot Twist: Det er pinligt simpelt MCP, i alle sine enterprise-robe-and-scepter regalia, er netop dette: 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." Hele denne protokol er bare en meget formel måde at bede nogen om at gøre en ting og få bekræftelse på, at de gjorde det. Resten er bare enterprise-grade ceremoni og glitter lim omkring denne smukt enkle håndtag. Syv gode udvekslinger. Det er det. Stitching the Monster Together: En fungerende prototype Vi kommer til at spå en vejrserver og afhøre den som en ordentlig skør videnskabsmand. Tip #1: Den bedste måde at forstå en protokol på er at misbruge den, indtil den skriger, og derefter patch den, indtil den samarbejder. Tip #1: Den bedste måde at forstå en protokol på er at misbruge den, indtil den skriger, og derefter patch den, indtil den samarbejder. Tip #2: Hvis du torturerer dine LLM'er, vil jeg ikke blive holdt ansvarlig for, hvad der sker med dig i The After Times When AI Taketh Over! Tip #2: Hvis du torturerer dine LLM'er, vil jeg ikke blive holdt ansvarlig for, hvad der sker med dig i The After Times When AI Taketh Over! Trin 1: Indkald vores digitale minion # 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 ) Den Modulet erstatter barnets I/O-strømme med rør, hvilket giver dig fuldstændig kontrol over samtalen.Det er smukt simpelt: Du har nu en dedikeret børneproces på din beck-and-call, klar til at udføre hvad du kaster på det. subprocess Du kan endda sende signaler for virkelig at vise, hvem der er chef. Under normale omstændigheder, når forælderprocessen forlader, går barnet ned med skibet - så du behøver ikke at bekymre dig om zombieprocesser, der forstyrrer dit system (ja, for det meste). Du vil bemærke, at vi bruger for at spire processen. Dette er ikke nødvendigt, men er hurtigt ved at blive guldstandarden for moderne Python-værktøjer.Hvis du ikke har tjekket det ud endnu, bør du absolut - det er en game-changer. Quick aside uv uv Hvis du stadig bruger pip, skal vi tale. Hvis du stadig bruger Vi har brug for at tale. pip Trin 2: Den ubehagelige første date Alle gode relationer begynder med gensidig identifikation.Vi vil begynde med at annoncere os selv (som en gentleman): # 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" } } } Dette er vores Den fortæller serveren tre afgørende ting: vi taler MCP, hvilken version vi bruger, og hvilke funktioner vi bringer til bordet. Åbning af salve Du vil bemærke, at dette følger JSON-RPC 2.0-formatet, og det vil være konsistent i hele vores samtale med serveren. 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) Vi bør se dette output: Sending initialize request... Lad os nu høre, hvad serveren har at sige om alt hidtil: 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) Serveren, der er høflig, præsenterer sig selv tilbage: . . . 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'}}} "Hej! jeg er en vejr server, jeg vil ikke tilfældigt ændre mine evner midt i samtalen, og jeg vil bestemt ikke spøgelse dig." Translation Det deler protokol version info og grundlæggende detaljer, men Sektionen er den rigtige præmie. So, what's the server actually telling us here? capabilities Vi ignorerer mulighederne for denne demo, men tjek dette ud: vi abonnere på "listChanged" begivenheder, hvis vores server var den type til at tilføje eller fjerne værktøjer dynamisk. Der er faktisk en snigende lille pub / sub-system gemmer sig i MCP-protokollen - du kan lytte til alle slags begivenheder. vores weather.py-server er for enkel til nogen af de fancy ting, men det er der, hvis du har brug for det. kunne Det samme gør sig gældende med "prompts" og "ressourcer" - vi hopper over dem helt. Hele ideen er, at forskellige systemer håndterer forskellige bekymringer, så du ikke behøver at genopfinde hvert hjul. Du kan vælge og vælge, hvilke dele af protokollen der skal implementeres, men hvis du vil spille godt med andre MCP-værktøjer, vil du bedre holde dig til specifikationen. API separation Alright, we're connected and ready to rock, right? Wrong. Serveren sidder stadig der, trykker sin digitale fod, venter på os til at fuldføre håndtrykket. notified_request = { "jsonrpc": "2.0", "method": "notifications/initialized" } Det er JSON-RPC's måde at sige "fire and forget" - ingen reaktion forventet eller påkrævet. Tænk på det som at sende en ACK-pakke: "Hej server, jeg er klar til at rulle!" Notice the missing id notification Ingen id betyder: "Svar ikke, jeg vil stole på, at du ved, hvad du skal gøre med disse oplysninger." betyder: "Svar ikke, jeg vil stole på, at du ved, hvad du skal gøre med disse oplysninger." Nej ikke id I mellemtiden ... tilbage på ranchen ... (tilbage til historien!) I mellemtiden ... tilbage på ranchen ... (tilbage til historien!) Husk, at serveren, der var rimeligt høflig, allerede svarede med et manifest af sine evner. Så lad os bekræfte: Ja, vi er virkelig klar til fest. # 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) Serveren kan nu begynde at vente på anmodninger. Trin 3: “Show Me What You Got” Tid til at se, hvad legetøj denne server bragte til legepladsen: 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) Vi får præcis det resultat, vi forventede...Perfekt! // Sending tools/list request... Nu, lad os læse tilbage produktionen ... Tid til at se, hvilke skatte vi har opdaget: # 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) Og vores server stolt viser sine varer: prognoser, advarsler og andre meteorologiske fejl. . . . 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! det er en JSON! okay, lad os grave ind i de gode ting. Lotte Det er vores guldmine - hvert objekt repræsenterer et værktøj, som LLM kan påberåbe sig. (Plot twist: vi kan også kalde dem, hvilket er præcis, hvad vi gør lige nu!) See that tools Sjovt faktum: De 'beskrivelse' felter er, hvordan din LLM beslutter, hvilken funktion at kalde. Tænk på det som Tinder for værktøjer, med din AI ser på sin telefon forsøger at beslutte, om at svejse til venstre eller højre Sjovt faktum: De 'beskrivelse' felter er, hvordan din LLM beslutter, hvilken funktion at kalde. Tænk på det som Tinder for værktøjer, med din AI ser på sin telefon forsøger at beslutte, om at svejse til venstre eller højre : OpenAI forsøgte oprindeligt at mærke dette som "funktion opkald" - hvilket er... Men et eller andet sted undervejs besluttede branchen kollektivt, at "værktøjer" lød køligere (eller måske mere tilgængelige?), og nu kalder vi hele dansen "værktøjsopkald". One interesting side note Teknisk Anatomy of a tool (pay attention, this is where the magic lives): Navn: Funktionens faktiske navn – ingen typos er tilladt her! Dette er det definitive, kanoniske navn på værktøjet; vi vil bruge det til at referere til dette værktøj, når vi kalder det. Beskrivelse: Almindelig engelsk for LLM at læse (dette er bogstaveligt talt, hvad AI ser på, når man beslutter, om man skal bruge dit værktøj) inputSchema: JSON Schema definerer, hvilke parametre du har brug for outputSchema: Conspicuously missing! Alt bare returnerer en "stor streng" og håber på det bedste Navn Beskrivelse af Indtastningsplan Udgangsplan Hvorfor ingen output skema? Fordi vi alle grundlæggende returnerer JSON indpakket i strenge og foregiver, at det er en design beslutning. Traditionelle funktioner kan returnere enhver type, mens Unix kommandolinjeværktøjer (hvilket navngivningen på en eller anden måde indebærer) bare spytter tekst ud. Nogle modeller har endda switches til at tvinge JSON-udgang, så i teorien kan din LLM forvente strukturerede data hver gang. Så igen kan værktøjer returnere simpel tekst, CSV, HTML eller virkelig noget. Multi-modale modeller kunne endda returnere lyd, billeder, video eller live objektdetektionsfeeds - mulighederne er vidunderligt kaotiske. Okay, vi har vores værktøjskasse indlæst. Lad os gøre vores første MCP værktøj opkald! Trin 4: Sandhedens øjeblik Uanset hvad, vi har en liste over værktøjer, nu hvad? lad os kalde en! Vi vælger et værktøj. lad os vælge for at holde tingene enkle, da vi ikke har brug for breddegrad og længdegrad. Det ville være et bedre valg. get_alerts get_forecast tools_call_request = { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "get_alerts", "arguments": { "state": "TX" } } } Jeg valgte Texas, fordi alt er større der, herunder vejrkatastrofer. Vent, hold op - hvorfor er det "værktøjer / opkald" og ikke "værktøj / opkald"? En Okay, sikkert, "værktøj / opkald" ville lyde mere naturligt på engelsk, men tilsyneladende vinder konsistens med de andre endpoints. Med alle vores data ducks i træk nu, kan vi trykke på den store røde knap. # 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] (Dette er lyden af den store røde knap) ] [Drumroll please Serverens tænkning ... behandling ... og ... ] [And the crowd goes wild! Reelle vejrdata materialiserer. Advarsler, oversvømmelser, tornadoer, arbejdet. Alt indpakket i struktureret JSON, ligesom din terapeut bestilte: (trimmet ned, ingen ønsker at se 11 sider af JSON dumps) // 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}} Bemærk at -Tjek altid dette felt, medmindre du nyder at debugge mystiske fejl ved 3 AM. Victory! isError: false Vi får rene strengedata tilbage (ingen streaming for at komplicere tingene), hvilket giver os muligheder. Vi kunne analysere disse vejrdata og massere det til LLM, eller bare passere det rå svar og lade modellen finde ud af det. De fleste implementeringer går med sidstnævnte tilgang - hvorfor gøre ekstra arbejde, når LLM'er er ret gode til at analysere struktureret tekst? Men hvis du bygger noget sofistikeret, kan forbehandling af værktøjets output være utroligt kraftfuldt. Du kan formatere det, filtrere det, kombinere det med andre datakilder eller omdanne det til præcis, hvad dit program har brug for. Vi har med succes registreret en MCP-server, initialiseret forbindelsen, kaldt et værktøj og behandlet resultaterne. And that's a wrap! Full Monty: Din komplette MCP-klient Her er hele den herlige ritual i en indkaldende cirkel: 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() Den store åbenbaring You just built an MCP client using nothing but Python's standard library. Ingen rammer. ingen eksterne afhængigheder. ingen magi. bare subprocessrør og JSON - de samme værktøjer, du har haft siden Python 2.7. Er dette "produktion-klar"? Ærligt? Ikke helt - men det er overraskende tæt. Uanset om det er Claude, der taler til din database, GPT-4, der kalder dine API'er, eller nogle startups "revolutionære AI-arbejdsflowplatform" - bag det hele, er det bare dette: Spawn process. Send JSON. Read JSON. Repeat. Det er som at opdage Wizard of Oz er bare en fyr med et virkelig godt lydsystem. Det er som at opdage Wizard of Oz er bare en fyr med et virkelig godt lydsystem. Dine næste skridt til galskab Nu hvor du har set dyrets mave (i kode), kan du: Byg brugerdefinerede MCP-servere, der gør dit bud (ikke længere vente på, at nogen anden skriver integrationen) Debugger MCP-forbindelser som en netværksnekromancer, når de uundgåeligt bryder sammen på det værste mulige tidspunkt Design bedre værktøjer ved at vide præcis, hvordan LLM'er forbruger dine metadata Skriv bedre værktøjer så uimodståeligt beskrevet, at dine LLM'er falder i kærlighed Optimer helvede ud af alt, fordi du forstår protokollen overhead Eller bare automatisere din kat foder. jeg dømmer ikke. Den smukke åbenbaring The miracle in step 2 of your business plan? It was you, all along. Det mirakel i trin 2 af din forretningsplan? det var dig, hele tiden. Byg noget mærkeligt. Vil du se denne kode i aktion? Det fulde eksempel lever her: [https://gitlab.com/-/snippets/4864350] https://gitlab.com/-/snippets/4864350 https://gitlab.com/-/snippets/4864350?embedable=true