MCP nullist: ehita TypeScriptis tootmiskõlblik server
Tootmiskõlbliku Model Context Protocoli serveri ehitamine nõuab enamat kui paari tööriista kokkuühendamist. Skeemikujunduse, autentimise, vigade käsitlemise, voogedastuse ja seire mustrid ning need tootmisreaalsused, mis muudavad MCP serverid skaalal tegelikult kasulikuks.
- aasta keskpaigaks on MCP (Model Context Protocol) tegelik standard LLM-ide ühendamiseks tööriistadega. Anthropic tutvustas seda; OpenAI, Google ja laiem ökosüsteem on selle omaks võtnud. Cursor, Claude Desktop, ChatGPT, kohandatud agendid — kõik räägivad MCP-d.
Kui sa tahad, et LLM agendid sinu teenusega suhtleksid, siis MCP server on see viis. Ja kui sa oled ühe-kaks ehitanud, näed, et protokoll ise on väike. Huvitav insenertöö läheb kõigesse, mis selle ümber on: skeemikujundus, vigade käsitlemine, autentimine, voogedastus, jõudlus, seire.
See artikkel on süvasukeldumine tootmiskõlbliku MCP serveri ehitamisse TypeScriptis. Käsitleme mustreid, mis päris agentide kasutuses vastu peavad — mitte ainult protokolli mehaanikat.
Mis MCP lühidalt on
MCP on klient-server protokoll, kus:
- Serverid pakuvad tööriistu, ressursse ja kutsesid (prompts).
- Kliendid on tüüpiliselt LLM agendid, kes neid tarbivad.
Protokoll kasutab JSON-RPC 2.0. Transpordid hõlmavad stdio-d (lokaalsete protsesside jaoks) ja HTTP/SSE-d (kaugkasutuse jaoks). Autentimine ja turvalisus on protokolli mured; peamised teostused toetavad OAuthi, API võtmeid jms.
Serveri ülesanne: pakkuda LLM-idele kasulikke võimekusi viisil, mida nad oskavad avastada ja kasutada.
Põhistruktuur
Ametliku @modelcontextprotocol/sdk paketi abil näeb minimaalne server kõrgtaseme McpServer API-ga välja nii:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "my-server",
version: "1.0.0",
});
server.registerTool(
"echo",
{
description: "Echo back the provided text.",
inputSchema: { text: z.string() },
},
async ({ text }) => ({
content: [{ type: "text", text }],
})
);
const transport = new StdioServerTransport();
await server.connect(transport);(Sama SDK paljastab ka madalama taseme Server klassi koos setRequestHandler-iga ListToolsRequestSchema / CallToolRequestSchema jaoks, kui tahad päringukäsitlejate üle täielikku kontrolli — aga enamiku serverite jaoks on McpServer.registerTool lühem ja sellega keerulisem vigu teha.)
See on luustik. Töö läheb sinna, mida sa tööriistakäsitlejatesse paned — ja kuidas.
Muster 1: Tööriistade kujunduse filosoofia
Esimene otsus: milliseid tööriistu sa paljastad ja millise granulaarsusega?
Tavaline läbikukkumine: oma alustkihi API paljastamine tööriistadena. Kui sul on 200 REST-i lõpp-punkti, on 200 tööriista paljastamine katastroof. Liiga paljude tööriistadega mudelid esinevad halvemini; tööriistade kirjeldused muutuvad kohmakaks; protokoll muutub labürindiks.
Parem: kujunda tööriistu nii, nagu agendid neid kasutada tahaksid. Iga tööriist teeb üht hästi defineeritud asja, võtab hästi defineeritud sisendid, tagastab hästi defineeritud väljundid.
Mõned põhimõtted:
Üks mõiste tööriista kohta. Ära tee manage_customer tööriista, mis teeb 12 erinevat asja. Tee search_customers, get_customer, update_customer_email, archive_customer — iga oma fookusega.
Õige granulaarsus. Liiga peenelt — agent vajab palju kõnesid; liiga jämedalt — ei saa täpselt seda, mida vaja. Eesmärk on "operatsioonid, millele inimene paneks nime."
Tegevussõnad. search_documents, mitte documents. Tööriistad peaks olema nimetatud selle järgi, mida nad teevad.
Lugemis-vs-kirjutamis eristus. Lugemistööriistad on turvalisemad; kirjutamistööriistadel on kõrvalmõjud. Erista neid nime poolest (list_x vs create_x) ja kohtle erinevalt (nõua selget kinnitust, idempotentsuse võtmeid jne).
Agregeeri, kui kasulik. get_customer_profile, mis tagastab kliendi + viimased tellimused + tugipiletid ühes kõnes, on sageli parem kui kolm eraldi kõnet. Agent saab konteksti ühe lasuga.
Näiteks kliendituge paljastava serveri jaoks võib mõistlik tööriistakomplekt olla 8–15 tööriista. Rohkem on tavaliselt liiga palju.
Muster 2: Skeemikujundus
Igal tööriistal on sisendiskeem (parameetrid, mida LLM peab andma) ja väljund (mida sinu tööriist tagastab). Skeemid ei ole ainult valideerimiseks; need on ka prompt engineering.
Zodi kasutamine sisendiskeemide jaoks:
const searchCustomersSchema = z.object({
query: z.string().describe(
"Search term: name, email, or company. Be specific to avoid too many matches."
),
limit: z.number().int().min(1).max(50).default(10).describe(
"Maximum results to return. Default 10, max 50."
),
filters: z.object({
tier: z.enum(["free", "pro", "enterprise"]).optional().describe(
"Filter to specific customer tier"
),
status: z.enum(["active", "trial", "churned"]).optional().describe(
"Filter by customer status"
),
}).optional(),
});Pane tähele:
- Igal väljal on
.describe(). Kirjeldus on see, mida LLM loeb. - Loetelud (enums) on selgesõnalised. Vabavormi stringid on võimaluse korral piiratud.
- Vaikeväärtused on mõistlikud.
- Piirangud (min/max, pikkus) on selged.
- Valikuline vs kohustuslik on selge.
Kirjeldused on tohutult olulised. "search term" on kasutu; "Search term: name, email, or company. Be specific to avoid too many matches" on kasulik juhis LLM-ile.
Muster 3: Väljundi kuju
Väljund on see, mida LLM näeb ja millele ta reageerib. Hea väljundikujundus parandab dramaatiliselt LLM-i käitumist.
Struktureeritud väljundid.
type SearchResult = {
customers: Customer[];
total_matches: number;
truncated: boolean;
next_page_cursor?: string;
};Kontekstiga.
{
customers: [...],
total_matches: 47,
truncated: true,
next_page_cursor: "abc",
message: "Found 47 matches; showing first 10. Use next_page_cursor to get more."
}message väli on inimloetav juhis. LLM-id kasutavad seda.
Vigade käsitlemisega armulikult.
{
error: "ambiguous_query",
message: "Search term 'john' matched 247 customers. Please be more specific.",
suggestion: "Try including a company name or email domain.",
partial_results: [...] // top 3 by relevance, optional
}Viga on struktureeritud (masinloetav), aga sisaldab ka teadet ja soovitust (LLM-loetavad). LLM saab kohaneda — kas küsida kasutajalt täpsustust või päringut viimistleda.
Sobiva suurusega.
Tööriist, mis tagastab 10 000 kirjet, on kasutuskõlbmatu. LLM-i konteksti see ei mahu; isegi kui mahuks, ei kasuta LLM seda hästi. Lehekülgenda alati, kärbi või tee kokkuvõte. Tagasta piisavalt, et LLM saaks otsuse teha, mitte kogu olemasolev.
Muster 4: Vigade semantika
Tööriistad kukuvad läbi. See, kuidas nad LLM-ile ebaõnnestumisest teada annavad, määrab, kas LLM taastub graatsiliselt või kuhjab vigu peale.
Vigade kategooriad.
type ToolError =
| { type: "validation"; message: string; field?: string }
| { type: "auth"; message: string }
| { type: "not_found"; message: string; suggestion?: string }
| { type: "conflict"; message: string; resolution?: string }
| { type: "rate_limit"; message: string; retry_after_seconds: number }
| { type: "service_unavailable"; message: string; retryable: boolean }
| { type: "internal"; message: string; trace_id: string };Igal kategoorial on erinev semantika. LLM peaks reageerima erinevalt:
validation: paranda sisend ja proovi uuesti.not_found: ütle kasutajale, või proovi teist otsingut.conflict: küsi lahendust.rate_limit: oota ja proovi uuesti.service_unavailable: proovi tagavaravarianti või teata kasutajale.internal: anna alla, anna kasutajale teada.
Nende dokumenteerimine sinu serveris teeb LLM-i võimekamaks.
Vigade vormindus.
Tagasta vead struktureeritud andmetena, selgete ja teostatavate teadetega:
{
error: {
type: "validation",
message: "The email address is not valid format.",
field: "email",
suggestion: "Provide a valid email address like 'name@example.com'."
}
}Väldi:
{
error: "Invalid input"
}Esimene laseb LLM-il taastuda. Teine jätab ta aimama.
Muster 5: Autentimine ja autoriseerimine
Tootmis-MCP serverid vajavad autentimist. Kes iganes serverini ulatub, saab tööriistu kasutada. See on peaaegu alati probleem.
Autentimine: kes helistab?
Tavalised mustrid:
- API võti. Lihtne, levinud, töötab teenus-teenuselt suhtluseks. Anna iga tarbija kohta; tee perioodiliselt rotatsiooni.
- OAuth. Mitmekasutaja-süsteemidele, kus lõppkasutajad volitavad agente. Keerukam, aga paljudele kasutusjuhtudele õige vastus.
- mTLS. Kõrge turvalisusega keskkondadele. Vastastikused TLS sertifikaadid mõlemale poolele.
Teostus oleneb transpordist. HTTP kaudu autendid päringu enne, kui see üldse MCP käsitlejani jõuab (sinu Expressi/Hono/Fastify vahevaras) ja paned helistaja päringule:
// Express-style middleware in front of the MCP HTTP endpoint.
app.use("/mcp", async (req, res, next) => {
const apiKey = req.header("x-api-key");
const caller = await authenticate(apiKey);
if (!caller) return res.status(401).send("Unauthorized");
(req as any).caller = caller;
next();
});Seejärel iga tööriistakäsitleja sees võta helistaja päringu-tasandi kontekstist (extra), mitte toorest päisest. Stdio kaudu ei ole HTTP päiseid; autentimine tuleb tavaliselt protsessi env-ist / konfifailidest.
Autoriseerimine: mida nad teha tohivad?
Kui kord autenditud, milliseid tööriistu helistaja kasutada saab ja milliste andmete peal?
function authorize(caller: Caller, tool: string, params: any): boolean {
// Caller-level: can this caller use this tool at all?
if (!caller.tools.includes(tool)) return false;
// Data-level: is this caller authorized for this specific data?
if (params.tenant_id && params.tenant_id !== caller.tenant_id) return false;
return true;
}Ära lase LLM-il autoriseerimisotsuseid teha. LLM-i võib ära petta. Autoriseerimine on serveri töö; LLM näeb ainult andmeid, mida ta on volitatud nägema.
Mitme-rentniku-süsteemides: iga tööriistakõne on rentnikule ulatatud. Rentnik määratakse autentimise, mitte LLM-i antud parameetrite järgi.
Muster 6: Idempotentsus
Kirjutamisoperatsioonide puhul on idempotentsus oluline. LLM võib uuesti proovida; ta võib sama tööriista kahes erinevas kontekstis kutsuda. Ilma idempotentsuseta saad duplikaate.
Idempotentsuse võtmed.
Tööriist võtab idempotency_key parameetri. Server kontrollib: kas oleme seda võtit varem näinud? Kui jah, tagasta vahemällu pandud tulemus. Kui ei, käivita ja pane vahemällu.
async function createInvoice(params: {
amount: number;
customer_id: string;
idempotency_key: string;
}) {
const cached = await idempotencyStore.get(params.idempotency_key);
if (cached) return cached;
const invoice = await actuallyCreateInvoice(params);
await idempotencyStore.set(params.idempotency_key, invoice, { ttl: 86400 });
return invoice;
}LLM-i jaoks vihja sellele tööriista kirjelduses:
"For each unique invoice you create, generate a UUID and pass it as idempotency_key. If you need to retry the operation, use the same UUID to avoid duplicate creation."Muster 7: Voogedastus
Tööriistadele, mis toodavad suuri väljundeid või võtavad aega, on väljundi voogedastus parem UX. MCP toetab edenemisteateid tööriistakäsitleja seest läbi päringu-tasandi extra argumendi:
server.registerTool(
"long_running_task",
{ description: "...", inputSchema: { ... } },
async (input, extra) => {
await extra.sendNotification({
method: "notifications/progress",
params: { progressToken: extra._meta?.progressToken, progress: 0, message: "Starting..." },
});
for (const step of steps) {
await doStep(step);
await extra.sendNotification({
method: "notifications/progress",
params: {
progressToken: extra._meta?.progressToken,
progress: step.index / steps.length,
message: step.name,
},
});
}
return { content: [{ type: "text", text: JSON.stringify({ result: finalResult }) }] };
}
);Kasuta voogedastust:
- Pikkadele operatsioonidele (>5 sekundit).
- Suurtele väljunditele (et LLM saaks töötlemist alustada, kui väljund veel tuleb).
- Operatsioonidele, kus vahepealsed tulemused on näitamist väärt.
Ära vooga edasta kiirete, lihtsate operatsioonide korral — lisab keerukust ilma kasuta.
Muster 8: Vahemällu salvestamine
Paljud tööriistakõned tabavad samu andmeid korduvalt. Vahemällu salvestamine võib dramaatiliselt parandada jõudlust ja vähendada taustsüsteemi koormust.
Lokaalne vahemälu. Protsessisisene vahemälu (nt LRU) tuliste andmete jaoks.
Hajutatud vahemälu. Redis või sarnane jagatud vahemälu jaoks üle serveri-instantside.
Vahemälu invalideerimine. Kui andmed muutuvad, vabasta asjakohased kirjed. (See on raske osa.)
TTL-id. Vahemällu pandud kirjed aeguvad pärast määratud aega. Sea iga andmetüübi jaoks — kliendi profiilid võivad vahemälus olla tunde; hinnastik võib olla minuteid.
Selleks, et vahemälust kasu oleks, peavad samad kõned korduma. Paljudel MCP serveritel on see olemas — agendid viitavad sageli samadele olemitele korduvalt seansi jooksul.
Muster:
async function getCustomerCached(id: string) {
const cached = await cache.get(`customer:${id}`);
if (cached) {
metrics.increment("cache.hit");
return cached;
}
metrics.increment("cache.miss");
const customer = await db.getCustomer(id);
await cache.set(`customer:${id}`, customer, { ttl: 300 });
return customer;
}Muster 9: Päringusageduse piiramine
LLM agendid võivad olla üllatavalt agressiivsed — silmustes, korduvalt proovides, harust harusse minnes. Pahasti käituv agent võib sinu taustsüsteemi DoS-iga maha lüüa.
Päringusageduse piiramine helistaja kohta on hädavajalik:
const limiter = new RateLimiter({
windowMs: 60_000,
max: 100 // 100 calls/minute per caller
});
server.setRequestHandler(CallToolRequestSchema, async (request, context) => {
if (await limiter.exceeded(context.caller.id)) {
return errorResponse("rate_limit", "Too many requests");
}
// ...
});Lisaks globaalsetele piiridele on tööriistapõhised piirid olulised — mõned tööriistad on kallid ja neid tuleks tihedalt piirata.
Tagajärgedega operatsioonide (kirjete loomine, teadete saatmine) korral kasuta rangemaid piiranguid või nõua selget kinnitusvoogu.
Muster 10: Ressursid
MCP-l on "ressursid" — kirjutuskaitstud andmeallikad, mida LLM saab sirvida ja viidata. Erineb tööriistadest (mida kutsutakse aktiivselt).
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
{
uri: "doc://my-server/handbook",
name: "Employee Handbook",
mimeType: "text/markdown",
description: "Company employee handbook"
},
// ...
]
}));
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const content = await loadResource(request.params.uri);
return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: content }] };
});Ressursid on kasulikud:
- Viitedokumendid, mida LLM võib tahta sirvida.
- Konfiguratsioon või konteksti-andmed.
- Otsingutabelid või skeemid, mida LLM võib vajada.
Ressurssi loetakse; tööriistad on tegevused. Kasuta mõlema jaoks õiget mõistet.
Muster 11: Seire
Samad mustrid nagu mujal tootmis-AI-s. Sinu MCP serveri jaoks instrumendi:
- Iga tööriistakõne: ajatempel, helistaja, tööriist, parameetrid, tulemus, latentsus, staatus.
- Tööriistapõhised mõõdikud: kõnede maht, p50/p95 latentsus, vigade määr.
- Helistajapõhised mõõdikud: kes helistab, kui sageli.
- Jälje (trace) kontekst: edasta jälje ID-d helistajalt taustsüsteemi kõnedeni.
Struktureeritud logid:
logger.info("tool_call", {
tool: request.params.name,
caller_id: context.caller.id,
trace_id: context.trace_id,
params: redactPII(request.params.arguments),
duration_ms: duration,
status: "success"
});Suuna seireplatvormi.
Muster 12: Versioonimine
Sinu MCP server areneb. Tööriistad muutuvad. Uusi tööriistu lisandub. Vanu tööriistu kaotatakse.
Serveri versioonimine. Server konstruktor võtab versiooni. Tõsta seda muudatustel. Kliendid saavad seda tuvastada.
Tööriista versioonimine. Kui tööriista signatuur mittesobivalt muutub, versioneeri seda: search_customers_v2. Hoia vana versiooni saadaval kasutuselt kõrvaldamise perioodi jooksul.
Skeemi areng. Lisa valikulisi välju turvaliselt. Väljade eemaldamine või tüüpide muutmine on mittesobiv muudatus.
Kasutuselt kõrvaldamine. Tööriista kõrvaldades märgi see kirjelduses: "DEPRECATED: use search_customers_v2 instead."
Mitme kliendi poolt kasutatavate tootmis-MCP serverite jaoks on versioonimine hädavajalik. Sisemiselt kasutatavad serverid võivad olla paindlikumad.
Muster 13: Testimine
Kuidas MCP serverit testida?
Ühiktestid. Iga tööriista loogika, simuleeritud sõltuvustega. Tavaline TypeScripti testimine.
Skeemi testid. Skeemid valideerivad nagu oodatud. Erijuhud (puuduvad väljad, valed tüübid) on käsitletud õigesti.
Integratsioonitestid. Käivita server, saada päris MCP päringud, kontrolli vastuseid. @modelcontextprotocol/sdk sisaldab testimisabivahendeid.
Otsast-otsani päris LLM-iga. Raskeim, aga väärtuslikem. Lase LLM-il sinu MCP serverit kasutada realistlike ülesannete täitmiseks. Kontrolli, et LLM kasutab tööriistu õigesti. Leia tööriistakirjelduste probleeme.
Otsast-otsani testide ülesseadmine (pseudokood; täpne kliendiühendus sõltub kasutatavast LLM kliendist — Anthropici TypeScripti SDK, OpenAI oma või MCP-d toetav raamistik):
// Start your MCP server as a child process or in-memory transport.
const server = await startTestServer();
// Drive an LLM with the MCP tools attached. The exact API depends on the client.
const result = await runAgent({
mcpServer: server,
systemPrompt: "You are a customer service agent...",
userMessage: "Find the customer Alice and check her open tickets",
});
// Inspect the tool calls captured by the server during the run.
expect(server.callLog.map((c) => c.name)).toEqual([
"search_customers",
"list_tickets",
]);Otsast-otsani testid püüavad kinni tööriistakirjelduste probleemid, mida ühiktestid ei suuda.
Muster 14: Juurutamine
Kus sinu MCP server elab?
Stdio (lokaalne). Server jookseb protsessina; klient käivitab selle. Parim töölauarakendustele (Claude Desktop, Cursor) ja kohalikele tööriistadele.
HTTP/SSE (kaug). Server on võrguteenus. Parim majutatud teenustele, jagatud infrastruktuurile, mitmest kliendist juurdepääsule.
Tootmisserverite jaoks:
- HTTP/SSE on tavaliselt valik.
- Juuruta nagu iga veebiteenust: konteinerid, koormuse jaotus, autoskaleerimine.
- TLS nõutav.
- Tervisekontrollid juurutusplatvormile.
- Graatsiline väljalülitus jooksvate päringute jaoks.
Muster 15: Turvakaalutlused
MCP serverid paljastavad võimekused LLM-idele. LLM-e saab manipuleerida. Turvalisuse mõjud:
Promptide süstimine läbi tööriistasisendite. Kasutaja päring võib sisaldada teksti, mis üritab LLM-i petta kahjulikult tööriistu kasutama. Kaitseks:
- Tööriistade kirjeldused selged oodatud kasutuse osas.
- Autoriseerimine serveri poolel (sõltumatult LLM-i otsustatud parameetritest).
- Kinnitused tagajärgedega tegevustele.
Andmete väljaviimine. Tööriistu, mis tagastavad andmeid, saab kuritarvitada — LLM-i võib ära petta, et see tagastaks tundlikke andmeid sobimatult. Kaitseks:
- Autoriseerimiskontrollid.
- Logimine, milliseid andmeid kes pääseb.
- Mustrite tuvastamine ebatavaliste juurdepääsumustrite jaoks.
Ressursside ammendamine. Tööriistu, mis tarbivad taustsüsteemi ressursse, saab kuritarvitada. Kaitseks:
- Päringusageduse piiramine.
- Ressursipiirid tööriistakõne kohta.
- Kaitselülitid (circuit breakers), kui taustsüsteem on halvenenud.
Süstimine tööriista väljunditesse. Tööriista väljund võib sisaldada teksti, mis LLM-i poolt lugedes seda manipuleerib. Kaitseks:
- Sanitiseeri väljundeid võimaluse korral.
- Ole ettevaatlik tööriistadega, mis tagastavad kasutaja loodud sisu.
Need on päris ründepinnad. Kohtle MCP servereid nagu iga tootmis-API-d: kaitse sügavuti.
Täielik näide: väike-aga-päris MCP server
Et see kokku panna, server, mis paljastab väikese CRM-i:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
import { db, cache, logger, authenticate } from "./infra.js";
const server = new McpServer({
name: "crm-server",
version: "1.0.0",
});
// === Tool: search_customers ===
server.registerTool(
"search_customers",
{
description: "Search customers by name, email, or company.",
inputSchema: {
query: z.string().describe("Name, email, or company"),
limit: z.number().int().min(1).max(50).default(10),
},
},
async ({ query, limit }, extra) => {
const auth = await authenticate(extra);
const cacheKey = `search:${auth.tenant_id}:${query}:${limit}`;
const cached = await cache.get(cacheKey);
if (cached) return cached;
const customers = await db.searchCustomers({
tenant_id: auth.tenant_id,
query,
limit,
});
const result = {
content: [{
type: "text" as const,
text: JSON.stringify({
customers,
total_matches: customers.length,
truncated: customers.length === limit,
message:
customers.length === limit
? `Showing first ${limit}; there may be more matches.`
: `Found ${customers.length} customer(s).`,
}),
}],
};
await cache.set(cacheKey, result, { ttl: 60 });
logger.info("search_customers", { tenant: auth.tenant_id, query, results: customers.length });
return result;
}
);
// === Tool: get_customer ===
server.registerTool(
"get_customer",
{
description: "Fetch a single customer by id.",
inputSchema: { customer_id: z.string() },
},
async ({ customer_id }, extra) => {
const auth = await authenticate(extra);
const customer = await db.getCustomer(auth.tenant_id, customer_id);
if (!customer) {
return {
isError: true,
content: [{
type: "text" as const,
text: `Customer ${customer_id} not found. Use search_customers to find by name or email.`,
}],
};
}
return { content: [{ type: "text" as const, text: JSON.stringify({ customer }) }] };
}
);
// === Tool: update_customer_email (with idempotency) ===
server.registerTool(
"update_customer_email",
{
description: "Update a customer's email; pass the same idempotency_key on retry.",
inputSchema: {
customer_id: z.string(),
new_email: z.string().email(),
idempotency_key: z
.string()
.describe("UUID for this update; pass the same value on retry to prevent duplicates"),
},
},
async (params, extra) => {
const auth = await authenticate(extra);
// ... idempotency check, validation, update
return { content: [{ type: "text" as const, text: "ok" }] };
}
);
// ... more tools ...
// Wire up a remote transport (Streamable HTTP) on a chosen port via your HTTP server of choice.
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() });
await server.connect(transport);See on lähtestruktuur. Lisa seire, päringusageduse piiramine, rohkem tööriistu, hoolikamad skeemid — aga raamid on käes.
Kokkuvõte
MCP on väike protokoll; tootmiskõlbliku serveri ehitamine on päris insenertöö. Tasu: sinu teenus muutub kasutatavaks iga LLM agendi poolt, standardiseeritud integratsiooniga, mis ei nõua mudelipõhist sidustamist.
Mustrid, mis loevad: fokuseeritud tööriistakujundus, promptiteadlikud skeemid, struktureeritud vigade semantika, töökindel autentimine, idempotentsus, seire, turvalisus. Mis tahes nende vahele jätmine loob MCP serveri, mis tootmises läbi kukub.
Ehita need sisse. Testi päris LLM-ide vastu. Itereeri tööriistade kirjeldusi. Tulemus on teenus, mida LLM saab kasutada sama soravalt nagu inimene — ja mis skaleerub koos kiiresti kasvava AI agentide universumiga.