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: to 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 legge en MCP-server på et markedsføringsnettsted
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.
To verktøy, begge 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.
Arkitekturvalget: en Pages-funksjon, ikke en Astro-rute
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.
Håndskrevet JSON-RPC slår SDK-en for to verktøy
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 to 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 to 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.
Skrivebeskyttet først er en sikkerhetsavgjørelse, ikke en begrensning
Det fristende tredje 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 |
Skråstrek-fellen som forkaster POST-meldingskroppen din
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 dette passer inn i agentberedskap og GEO
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.
Hva vi bevisst utelot
En kort liste, fordi det å vite hva et bygg hopper over er like nyttig som å vite hva det inkluderer.
- Ingen
get_case_studies-verktøy ennå. En agent kan lese casestudiene frallms.txtog de lenkede sidene. Vi legger til verktøyet når en reell kunde ber om det, ikke før. - 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 to intensjonene 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.
Hvor du går videre
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.







