Det meste av veiledningen om Model Context Protocol antar at du bygger en server foran en nettbutikk eller en database, noe med ordrer og lagerbeholdning. Vi gjorde det mindre opplagte: vi gjorde et statisk markedsføringsnettsted om til en levende, skrivebeskyttet MCP-server. Enhver MCP-klient kan nå sende en POST til ett enkelt endepunkt og spørre hvilke tjenester som finnes eller hvordan man starter en henvendelse, i typed JSON-RPC, uten å tolke en eneste side med HTML.
Dette er en praktikers gjennomgang av det bygget. Det er bevisst smalt: en håndfull skrivebeskyttede verktøy, ingen database, ingen skriving. Den smalheten er selve poenget, og de fleste av de interessante avgjørelsene kommer fra den. Det som følger er hvorfor et innholdsnettsted i det hele tatt har nytte av et MCP-endepunkt, hvorfor endepunktet er en Cloudflare Pages-funksjon og ikke en rammeverksrute, hvorfor vi håndskrev JSON-RPC i stedet for å dra inn SDK-en, og den ene skråstrek-buggen som stille bryter enhver POST hvis du overser den.
Hvorfor et statisk markedsføringsnettsted trenger en MCP-server
En nettbutikk eksponerer MCP fordi en agent trenger å utføre en handling: sjekke lager, bygge en handlekurv, legge inn en ordre. Et markedsføringsnettsted har ingen handlekurv. Så hva er verktøyflaten til?
Det ærlige svaret er agentoppdagelse. Nettstedet vårt kjørte allerede et oppdagelseslag for agenter: en llms.txt, et sett med .well-known-dokumenter, og et MCP-serverkort på /.well-known/mcp/server-card.json. Det kortet beskrev en server, dens transport og dens evner. Det erklærte til og med en tools-evne. Det var ett problem: ingenting bodde bak det. Kortet annonserte en server som ikke fantes. En agent som fulgte kortet og forsøkte å koble til, fikk ingenting.
Det er en vanlig tilstand for disse filene. De blir generert, de blir annonsert i en Link-header, og så peker de mot en ressurs som aldri ble bygget. Kortet er et løfte uten implementering.
Så målet var ikke å finne opp en ny flate. Det var å gjøre en eksisterende, annonsert flate ekte. Verktøyene følger direkte av hva et markedsføringsnettsted er til for:
check_servicesreturnerer tjenestekatalogen: id, navn, beskrivelse, kategori og kanonisk URL.request_quotereturnerer den lokaliserte kontakt-URL-en og instruksjoner for å sende inn en henvendelse.
Den første versjonen hadde disse to. Tre skrivebeskyttede verktøy til kom senere, hvert som en ny oppføring i TOOLS-arrayet og en ny gren i callTool, uten endringer i transporten:
check_tech_stackreturnerer teknologistakken vi jobber med, gruppert etter kategori og med et valgfritt kategorifilter.get_case_studiesreturnerer casestudiene med stakken deres, filtrert etter et valgfritt søkeord.search_blog_postssøker i bloggen etter tittel, beskrivelse, kategorier, tagger eller slug, med valgfrie filtre for språk og kategori og en grense for antall treff.
Fem verktøy, alle skrivebeskyttet. En agent kan nå ramse opp hva vi gjør og gi en bruker det riktige neste steget, deterministisk, i stedet for å gjette ut fra prosa.
Hvordan kjøre en MCP-server som Cloudflare Pages-funksjon
Nettstedet er bygget med Astro i statisk utdatamodus. Det finnes ingen SSR-adapter. Hver side er forhåndsgjengitt til HTML på byggetidspunktet, og de API-lignende filene det serverer (tjenestekatalog-JSON-en, agentprofilen) er statiske filer under public/, ikke serverruter.
Det ene faktumet driver hele designet. Et dynamisk endepunkt som leser en meldingskropp og returnerer et beregnet svar, kan ikke være en Astro-rute her, fordi det ikke finnes noen server til å kjøre det. Å legge til en ville bety å bytte utdatamodusen til hybrid eller server og feste en adapter, som er en stor, risikabel endring på et bygg som allerede produserer tusenvis av sider.
Nettstedet kjører imidlertid allerede håndskrevne Cloudflare Pages-funksjoner: en middleware som håndterer omdirigeringer, og noen få små API-behandlere. Det er sømmen. Et dynamisk MCP-endepunkt er bare én Pages-funksjon til, functions/mcp.ts, som sitter ved siden av de andre. Den deployes med det vanlige bygget, trenger ingen adapter, og endrer ingenting ved hvordan sidene gjengis.
Lærdommen kan generaliseres. Når du vil legge til ett dynamisk endepunkt på et ellers statisk nettsted, ikke ty til rammeverkets servermodus. Ty til den edge-funksjonsprimitiven verten din allerede gir deg. På Cloudflare Pages er det en Function. Kostnaden er én fil, ikke en ombygging av gjengivelsesmodellen din.
Hvordan bygge en MCP-server uten offisiell SDK
Refleksen når du leser “MCP-server” er å installere @modelcontextprotocol/sdk. For en stdio-server med mange verktøy, ressurser og prompter er den refleksen riktig. For noen få skrivebeskyttede verktøy på en edge-funksjon er den det ikke.
MCP-wire-protokollen er JSON-RPC 2.0. En server som svarer en MCP-klient trenger å håndtere et lite sett med metoder:
initialize, der serveren returnerer sin protokollversjon, evner og identitet.tools/list, der den returnerer verktøydefinisjonene med deres JSON Schema.tools/call, der den kjører et navngitt verktøy og returnerer resultatet pakket inn i MCP-innhold.- varsler som
notifications/initialized, som ikke bærer noen id og ikke forventer noe svar.
Det er hele flaten for en skrivebeskyttet server. Å implementere den for hånd er omtrent 120 linjer. Utsendingen er en enkel switch:
switch (method) {
case "initialize":
return ok({ protocolVersion, capabilities: { tools: { listChanged: false } }, serverInfo });
case "tools/list":
return ok({ tools: TOOLS });
case "tools/call":
return ok({ content: [{ type: "text", text: callTool(name, args, services).text }] });
default:
return err(-32601, `Method not found: ${method}`);
}Vei det mot SDK-en. SDK-en er designet for stdio og for hele protokollen. På en edge-kjøretid arver du dens bundling-antakelser og dens transportmaskineri for funksjoner du ikke bruker. For noen få leseverktøy er en avhengighet du må resonnere om en dårligere avveining enn 120 linjer du har full kontroll over. Dette er den samme skrivebeskyttet-først-tilbakeholdenheten anvendt på avhengigheter: eksponer minimum, eie minimum.
Det ene som er verdt å gjøre nøye for hånd er feilkontrakten. JSON-RPC har definerte feilkoder, og MCP-klienter forventer dem: -32700 for en tolkningsfeil, -32601 for en ukjent metode, -32602 for dårlige parametre. Å få dem riktig er det som får en håndskrevet server til å oppføre seg som en ekte en overfor en streng klient.
Hvorfor en offentlig MCP-server bør være skrivebeskyttet
Det fristende neste verktøyet er ett som fanger opp en lead: ta et navn, en e-post og en melding, og send den til oss på e-post. Ikke bygg det som et åpent MCP-verktøy.
Et offentlig endepunkt med et skrivbart verktøy er et spam-sluk. Hvem som helst på internett kan kalle tools/call med submit_quote og en falsk nyttelast, og nå er innboksen din, CRM-en din eller databasen din et mål uten friksjon og uten et menneske i loopen. I det øyeblikket et verktøy skriver, trenger endepunktet autentisering, hastighetsbegrensning og misbrukshåndtering, som er en stor flate for et markedsføringsnettsted å forsvare.
Så request_quote skriver ingenting. Det returnerer den lokaliserte kontakt-URL-en og strukturert veiledning:
if (name === "request_quote") {
const lang = LOCALES.includes(String(args?.lang)) ? String(args.lang) : "en";
return { text: JSON.stringify({
contact_url: CONTACT_URLS[lang],
method: "web-form",
note: "Read-only endpoint. Submit the inquiry through the contact form; this tool does not send it for you.",
reply_time: "within one working day",
}, null, 2) };
}Agenten får alt den trenger for å føre brukeren videre: den riktige kontaktsiden for brukerens språk, og en klar erklæring om at innsendingen skjer gjennom skjemaet. Mennesket blir værende i loopen. Endepunktet har ingenting å misbruke.
Dette er ikke et svakere design fremtvunget av forsiktighet. Det er den samme holdningen vi anbefaler til kunder som bygger MCP for sine egne systemer: skrivebeskyttet først, legg til skriving bare bak autentisering når det er en konkret grunn. En skrivebeskyttet server er ærlig om hva den er, og den er trygg å la stå åpen.
| Designvalg | Åpent lese-skrive-verktøy | Skrivebeskyttet overlevering |
|---|---|---|
| Autentisering påkrevd | Ja | Nei |
| Spam-flate | Høy | Ingen |
| Menneske i loopen | Valgfritt | Alltid |
| Kode å forsvare | Hastighetsbegrensning, validering, misbrukshåndtering | Ingen |
| Egnet for et offentlig markedsføringsnettsted | Dårlig | God |
Hvorfor omdirigering til skråstrek mister POST-kroppen
Denne kostet reell feilsøkingstid, så det er verdt å si det rett ut.
Nettstedet vårt kanonikaliserer URL-er til en etterfølgende skråstrek. Middlewaren 301-omdirigerer enhver sti uten en til versjonen med skråstrek. Det er greit for sider. Det er en stille katastrofe for et JSON-RPC-endepunkt.
En POST /mcp treffer omdirigeringen og returnerer 301 til /mcp/. Mange HTTP-klienter, når de følger omdirigeringen, sender ikke POST-meldingskroppen på nytt, eller nedgraderer metoden. MCP-klienten ser et tomt eller mislykket svar og konkluderer med at serveren er ødelagt. Serveren er i orden. Forespørselen kom aldri frem med meldingskroppen sin.
Løsningen er ikke å bekjempe middlewaren. Det er å annonsere URL-en nettstedet faktisk serverer. MCP-serverkortet og enhver oppdagelsesreferanse peker mot /mcp/, med etterfølgende skråstrek, slik at en velfungerende klient sender POST direkte til den kanoniske URL-en og aldri berører omdirigeringen. Hvis rammeverket eller verten din normaliserer skråstreker, bestem hvilken form som er kanonisk og annonser nøyaktig den formen overalt.
Du kan verifisere det levende endepunktet på samme måte som vi gjør:
curl -s -X POST https://wppoland.com/mcp/ \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'En POST uten skråstrek til samme vert vil vise deg 301-en i stedet.
Hvordan en MCP-server hjelper med GEO og AI-agenter
Generative engine optimization handler for det meste om å være lesbar og verifiserbar for modeller. Strukturerte data, klare entiteter, konsistente påstander på tvers av dine egne flater og eksterne. Et levende MCP-endepunkt er en sterk versjon av den lesbarheten: det er ikke et hint om innholdet ditt, det er et kallbart grensesnitt til det.
To ting gjør det verdt den lille innsatsen selv mens adopsjonen er tidlig. For det første lukker det gapet mellom hva oppdagelseslaget annonserer og hva som finnes. Et kort som peker mot en fungerende server er et sammenhengende signal; et kort som peker mot ingenting er et ødelagt et, og ødelagte signaler er verre enn fraværende. For det andre, for hvem som helst som selger denne evnen, er endepunktet bevis. Vi bygger MCP-servere for kunder, og den mest troverdige demonstrasjonen av det er en offentlig, skrivebeskyttet en som kjører på vårt eget domene, ved siden av den åpen kildekode WooCommerce MCP-serveren vi publiserer.
Ingen større AI-leverandør forplikter seg formelt til å lese MCP-serverkort eller llms.txt i dag. Det er et rimelig forbehold og vi sier det rett ut. Endepunktet er billig å kjøre, det gjør et annonsert løfte sant, og det er en fungerende gjenstand snarere enn en påstand. De tre til sammen klarer terskelen.
MCP-telemetri fra et annet nettsted, oktober 2026: Amir Pournasserian talte trafikken til MCP-serveren på nettstedet sitt i tre og en halv dag (14. til 18. oktober 2026). Klienter åpnet rundt 300 tilkoblinger (308 initialize, 281 tools/list) og gjorde 29 verktøykall, 15 søk og 14 hentinger. Mange presenterte seg som kataloger og rangeringer for MCP-servere eller agenter. De spurte også etter rundt et dusin oppdagelsesadresser serveren hans ikke har, blant dem /.well-known/mcp/server-card.json, som han sporer tilbake til tidligere utkast av Server Cards. Det er adressen kortet vårt bruker, lenket fra vår /.well-known/ai-catalog.json. Forslaget MCP Server Cards, SEP-2127, nå med status Final, tillater kort på enhver ikke-reservert adresse som en AI-katalog peker til, og anbefaler <streamable-http-url>/server-card. Han fant ingen utbredt klient som følger en katalog på egen hånd. Claude Code, Claude.ai, VS Code og Cursor tar serveradressen fra deg eller fra sitt eget galleri. Ett privat nettsted og tre og en halv dag med data. De fleste klientene hentet verktøylisten, få kalte et verktøy.
Hvilke MCP-funksjoner en liten server kan droppe
En kort liste, fordi det å vite hva et bygg hopper over er like nyttig som å vite hva det inkluderer.
- Ingen verktøy på forskudd.
get_case_studiesmanglet i den første versjonen, og agenter leste casestudiene frallms.txtog de lenkede sidene til verktøyet kom. Hvert verktøy på listen svarer i dag på et spørsmål agenter faktisk stiller. - Ingen
resources- ellerprompts-evne. Serveren erklærer baretools, fordi det er alt den implementerer. Å erklære en evne du ikke serverer er verre enn å utelate den: en klient som kallerresources/listfår en feil i stedet for et rent “støttes ikke”. - Ingen SDK, som dekket ovenfor.
- Ingen skriving, som dekket ovenfor.
Hver utelatelse er en avgjørelse, ikke en forglemmelse. Endepunktet gjør nøyaktig det de skrivebeskyttede verktøyene krever og ikke noe mer, som er grunnen til at det er lite nok til å resonnere om i én økt og trygt nok til å la stå åpent mot internett.
Hva MCP-spesifikasjonen krever av et Streamable HTTP-endepunkt
Transporten bak POST /mcp/ er Streamable HTTP. MCP-spesifikasjonen for transporter (versjon 2025-06-18) definerer den som erstatningen for HTTP+SSE-transporten fra versjon 2024-11-05. De fleste reglene handler om strømming, og en skrivebeskyttet server oppfyller dem uten å strømme noe som helst.
- Hver JSON-RPC-melding fra klienten er en ny HTTP POST til én endepunktsti. Klienten må sende en
Accept-header som lister bådeapplication/jsonogtext/event-stream. - Når POST-kroppen er en notifikasjon eller et svar, må serveren svare
202 Accepteduten kropp. Funksjonen vår gjør det fornotifications/initialized. - Når kroppen er en forespørsel, velger serveren: åpne en SSE-strøm, eller returnere ett JSON-objekt med
Content-Type: application/json. Klienten må håndtere begge, så en server uten noe å strømme kan alltid svare med vanlig JSON. - Sesjoner er valgfrie. En server kan sette en
Mcp-Session-Id-header i svaret påinitialize, men ingenting tvinger den til det. Uten sesjons-ID finnes det ingen sesjonslagring å drive, og det passer en edge-funksjon som ikke holder tilstand mellom forespørsler. - Etter initialiseringen må klienten sende headeren
MCP-Protocol-Versioni hver forespørsel. Mangler headeren, og serveren ikke har en annen måte å finne versjonen på, bør den anta2025-03-26. En ugyldig eller ikke-støttet verdi må få400 Bad Request.
Den samme siden har en sikkerhetsadvarsel for denne transporten. Servere må validere Origin-headeren på alle innkommende tilkoblinger, servere som kjører lokalt bør bare lytte på 127.0.0.1 og ikke på alle nettverksgrensesnitt, og servere bør autentisere tilkoblinger. Angrepet spesifikasjonen navngir, er DNS rebinding: et eksternt nettsted bruker den besøkendes nettleser til å nå en MCP-server på vedkommendes egen maskin.
Hvordan forhandling av MCP-protokollversjon fungerer
Versjonsforhandlingen skjer inne i initialize, og livssyklusspesifikasjonen holder den kort. Klienten må sende en protokollversjon den støtter, og det bør være den nyeste den støtter. Støtter serveren den versjonen, må den svare med den samme. Hvis ikke, må den svare med en annen versjon den støtter, også her helst den nyeste. En klient som ikke støtter versjonen i svaret, bør koble fra.
Protokollversjoner er datostrenger som 2024-11-05, 2025-03-26 og 2025-06-18. Funksjonen vår bruker 2025-06-18 som standard. For en håndskrevet server koker regelen ned til én beslutning: listen over versjoner du svarer for. Livssyklussiden viser formen på feilen ved et misforhold: kode -32602, meldingen “Unsupported protocol version” og et data-objekt med de støttede versjonene og den forespurte.
Når forhandlingen er ferdig, må begge sider respektere den forhandlede versjonen og bare bruke kapabiliteter som ble forhandlet. Det er den formelle grunnen til at serveren over bare deklarerer tools: en kapabilitet i svaret på initialize er et løfte klienten har lov til å handle ut fra.
Hvordan merke et MCP-verktøy som skrivebeskyttet
MCP-skjemaet gir hvert verktøy et valgfritt annotations-objekt med fire hint om atferd:
| Hint | Betydning | Standard |
|---|---|---|
readOnlyHint | verktøyet endrer ikke omgivelsene sine | false |
destructiveHint | verktøyet kan gjøre destruktive endringer; gjelder bare når readOnlyHint er false | true |
idempotentHint | gjentatte kall med de samme argumentene har ingen ekstra effekt; gjelder bare når readOnlyHint er false | false |
openWorldHint | verktøyet kan samhandle med en åpen verden av eksterne enheter | true |
Standardverdiene er pessimistiske. Et verktøy uten annotasjoner beskrives som et som kan skrive, kan slette data og kan nå utenfor sitt eget domene. Verktøyene i dette bygget har ingen annotasjoner, så hintene alene ville beskrevet dem slik. Å legge til readOnlyHint: true i hver verktøydefinisjon er en endring på én linje.
Skjemaet sier også at disse egenskapene er hint og ikke en pålitelig beskrivelse av atferd, og verktøyspesifikasjonen sier at klienter må behandle annotasjoner som ikke-klarerte med mindre de kommer fra klarerte servere. Et hint erstatter aldri kontrollen. Den skrivebeskyttede egenskapen til dette endepunktet ligger i koden, der ingen verktøy har en skrivevei. Annotasjonen beskriver bare egenskapen for en klient som velger å stole på den.
Verktøyspesifikasjonen gir hver server tre plikter: validere all inndata til verktøy, ha riktig tilgangskontroll og begrense hvor ofte verktøy kan kalles. Funksjonen vår dekker den første ved å avvise feilformede argumenter med -32602. Spesifikasjonen skiller også mellom to typer feil: protokollfeil, som et ukjent verktøy, går tilbake som JSON-RPC-feil, mens et verktøy som kjørte og feilet, returnerer et vanlig resultat med isError: true, slik at modellen ser feilteksten og kan reagere på den.
Flere guider om å bygge MCP-servere
For en trinnvis praktisk gjennomgang av oppsett, JSON-RPC-validering og tilkobling av Claude Desktop til vårt live-endepunkt, les vår WPPoland live MCP-server guide. Hvis du vil ha den fyldigere versjonen av disse avgjørelsene, dekker klyngen rundt dette innlegget det tilstøtende terrenget: å bygge en MCP-server for WooCommerce for det tilstandsbærende, butikkvendte tilfellet, MCP vs REST: når hver vinner for om du i det hele tatt trenger MCP, og MCP-autentiseringsmønstre for øyeblikket du legger til et skrivbart verktøy og trenger autentisering. Den kommersielle versjonen av dette arbeidet finner du på tjenestesiden for MCP-serverutvikling.
Kortversjonen får plass i én setning: et markedsføringsnettsted kan være en MCP-server, endepunktet er én edge-funksjon, hold alle verktøy skrivebeskyttet, og annonser URL-en verten din faktisk serverer.






