Прыкладніка WeatherSG MCP: інструменты плюс інтэрактыўная карта ў ChatGPT і Claude
WeatherSG — аплікацыя MCP на TypeScript: дакт кантракт, Streamable HTTP, размешчэнне на Cloud Run і рэгістрацыя хоста, каб ChatGPT і Claude паказвалі жывую карту пагоды.
Крыніцы: How to Build and Deploy an MCP App
MCP-прыглы дадзеюць протаколу Model Context Protocol магчымасці, якія выходзяць за межы інструментаў, якія вяртаюць тэкст: яны можаў выдаваць інтэрактыўныя рэсурсы UI, якія хосты на кшталт ChatGPT або Claude можаюць прадстаўляць. У гэтым кераванні ствараецца WeatherSG на TypeScript — контракт дадзэнняў, запрашанне паветарных даных, звычны інструмент, UI-інтэрфейс для карты, упакаваныя ў адны HTML-рэсурс, транспорт Streamable HTTP, размешчэнне ў кантэйнеры на Cloud Run і рэгістрацыя ў обох хостах.
Што такое MCP-прыглы
Сервер як і раней выкладзе інструменты і рэсурсы; прыглы дадае таксама фронт-энд, які хост убудоввае пасля таго, як выберае яго пасля вызову інструмента. Цяперашняе цыклічнае прадзеянне: запіт → інструмент → рэсурс UI → адносіны з корыстнікам → дальнейшыя вызовы інструментаў, якія ўзьмасця неабходныя.
Чаму тут TypeScript
Адна мова для сервера і упакавкі UI дапамагае залічыць елементы, якія трэба размясціць, маленькімі. Строгія тыпы дапамагаюць кантракту дадзэнняў застацца точным між нормалізаторамі і інтэрфейсам карты.
mkdir weathersg
cd weathersg
npm init -y
npm install \
@modelcontextprotocol/sdk \
@modelcontextprotocol/ext-apps \
express leaflet react react-dom zod
npm install --save-dev \
@types/express @types/leaflet @types/node \
@types/react @types/react-dom \
esbuild supertest tsx typescript vitest
Зберагаюце таямніцы ў серавэры платформы; ніколі не включайце кантыяны API ў атрыбуты HTML-файлаў.
{
"scripts": {
"build": "npm run build:web && tsc -p tsconfig.json",
"build:web": "node scripts/build-web.mjs",
"dev": "npm run build:web && tsx watch src/index.ts",
"start": "node dist/src/index.js",
"test": "vitest run",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"engines": {
"node": ">=20"
}
}
Для хостаў, якія чакаюць стабільны URL сесіі MCP, лепш выкарыстоўваць протакол HTTP з можлівасцю стрымавання дадзеных.
weathersg/
src/
weather/
schema.ts
client.ts
http-app.ts
index.ts
server.ts
web/
src/
main.tsx
styles.css
scripts/
build-web.mjs
tests/
Dockerfile
package.json
tsconfig.json
Перакананні пра стан кантэйнераў должны выконваліцца на канцэнтры MCP, а не толькі на статычной сторонце.
import { z } from "zod";
export const weatherLayerSchema = z.enum([
"all",
"forecast",
"rainfall",
]);
const forecastAreaSchema = z.object({
id: z.string(),
name: z.string(),
latitude: z.number(),
longitude: z.number(),
condition: z.string(),
category: z.enum([
"clear",
"cloudy",
"showers",
"thunderstorms",
"windy",
"hazy",
"unknown",
]),
});
const rainfallStationSchema = z.object({
id: z.string(),
name: z.string(),
latitude: z.number(),
longitude: z.number(),
rainfallMm: z.number().nonnegative(),
});
export const weatherSnapshotSchema = z.object({
layer: weatherLayerSchema,
generatedAt: z.string(),
forecast: z.object({
updatedAt: z.string(),
validFrom: z.string(),
validTo: z.string(),
validPeriodText: z.string(),
areas: z.array(forecastAreaSchema),
}).nullable(),
rainfall: z.object({
observedAt: z.string(),
readingType: z.string(),
unit: z.string(),
stations: z.array(rainfallStationSchema),
}).nullable(),
summary: z.object({
headline: z.string(),
areasWithRain: z.number().int().nonnegative(),
reportingRainStations: z.number().int().nonnegative(),
}),
warnings: z.array(z.string()),
sources: z.array(z.object({
label: z.string(),
url: z.string().url(),
})),
});
export type WeatherSnapshot = z.infer<typeof weatherSnapshotSchema>;
Рэгістрацыя хоста ўважаецца часткай настройкаў — прабавайце знова пасля кожных змян URL або методаў аўтэнтыфікацыі.
async function fetchValidated<T>(
url: string,
schema: z.ZodType<T>,
sourceLabel: string,
): Promise<{ value: T; warning?: string }> {
const cached = cache.get(url);
try {
const response = await fetch(url, {
headers: { Accept: "application/json" },
signal: AbortSignal.timeout(8_000),
});
if (!response.ok) {
throw new Error(`Weather service returned HTTP ${response.status}`);
}
const value = schema.parse(await response.json());
cache.set(url, {
value,
expiresAt: Date.now() + 60_000,
});
return { value };
} catch (error) {
if (cached) {
return {
value: cached.value as T,
warning: `${sourceLabel} could not be refreshed`,
};
}
throw error;
}
}
Спрыяйце гэтаму як пункту пераканання: тэсты протакола должны быць успешныя перад пачатком налагоджэння вязей з хостам для чата.
const [forecastResult, rainfallResult] = await Promise.allSettled([
fetchValidated(TWO_HOUR_FORECAST_URL, forecastSchema, "Forecast"),
fetchValidated(RAINFALL_URL, rainfallSchema, "Rainfall"),
]);
Зберагаюце таямніцы ў серавэры платформы; ніколі не включайце кантыяны API ў атрыбуты HTML-файлаў.
return weatherSnapshotSchema.parse(snapshot);
Для хостаў, якія чакаюць стабільны URL сесіі MCP, лепш выкарыстоўваць протакол HTTP з можлівасцю стрымавання дадзеных.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { getSingaporeWeather } from "./weather/client.js";
import {
weatherLayerSchema,
weatherSnapshotSchema,
} from "./weather/schema.js";
const server = new McpServer(
{ name: "weathersg", version: "0.1.0" },
{
instructions:
"Use get_singapore_weather for factual weather questions. " +
"Use show_singapore_weather_map when the user asks to see, " +
"map, compare, or explore weather across Singapore.",
},
);
Перакананні пра стан кантэйнераў должны выконваліцца на канцэнтры MCP, а не толькі на статычной сторонце.
server.registerTool(
"get_singapore_weather",
{
title: "Get Singapore weather",
description:
"Get current official two-hour forecasts and five-minute " +
"rainfall observations across Singapore.",
inputSchema: {
layer: weatherLayerSchema.default("all"),
},
outputSchema: weatherSnapshotSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false,
},
},
async ({ layer }) => {
const snapshot = await getSingaporeWeather(layer);
return {
structuredContent: snapshot,
content: [{
type: "text",
text: `${snapshot.summary.headline} Snapshot generated at ${snapshot.generatedAt}.`,
}],
};
},
);
Рэгістрацыя хоста ўважаецца часткой настройкаў — прабавайце знова пасля кожных змян URL або методаў аўтэнтыфікаціі.
import {
registerAppResource,
registerAppTool,
RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
export const WEATHER_APP_URI =
"ui://weathersg/singapore-weather-map/v1.html";
Спрытваце гэта як пункт контролю: тэсты пратаку должны быць успешныя перад будаваннем з’ёднання з хостам чату.
registerAppTool(
server,
"show_singapore_weather_map",
{
title: "Show Singapore weather map",
description:
"Show an interactive map of official forecasts and rainfall " +
"observations across Singapore.",
inputSchema: {
layer: weatherLayerSchema.default("all"),
},
outputSchema: weatherSnapshotSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false,
},
_meta: {
ui: { resourceUri: WEATHER_APP_URI },
"openai/toolInvocation/invoking": "Reading Singapore weather…",
"openai/toolInvocation/invoked": "Singapore weather map ready",
},
},
async ({ layer }) => {
const snapshot = await getSingaporeWeather(layer);
return {
structuredContent: snapshot,
content: [{
type: "text",
text: snapshot.summary.headline,
}],
};
},
);
Сакрэтныя даны трэба захаваць у сераўе платформы; ніколі не включайце кантрольныя клучы API ў аб’еднаны HTML-код.
registerAppResource(
server,
"Singapore weather map",
WEATHER_APP_URI,
{ description: "Interactive map of Singapore weather" },
async () => ({
contents: [{
uri: WEATHER_APP_URI,
mimeType: RESOURCE_MIME_TYPE,
text: loadWeatherAppHtml(),
_meta: {
ui: {
prefersBorder: true,
csp: {
connectDomains: [],
resourceDomains: ["https://www.onemap.gov.sg"],
},
},
},
}],
}),
);
Для хостаў, якія чакаюць URL стойкай сесіі MCP, лепш выкарыстоўваць HTTP з можлівасцю стрымавання дадзеных.
import { App } from "@modelcontextprotocol/ext-apps";
import { createRoot } from "react-dom/client";
const app = new App(
{ name: "WeatherSG map", version: "0.1.0" },
{},
{ autoResize: true },
);
function parseSnapshot(value: unknown): WeatherSnapshot | null {
if (!value || typeof value !== "object") return null;
const candidate = value as Partial<WeatherSnapshot>;
if (!candidate.summary || !Array.isArray(candidate.sources)) return null;
if (!candidate.forecast && !candidate.rainfall) return null;
return candidate as WeatherSnapshot;
}
app.ontoolresult = (result) => {
const snapshot = parseSnapshot(result.structuredContent);
if (snapshot) deliverSnapshot(snapshot);
};
createRoot(document.getElementById("root")!).render(<WeatherApp />);
void app.connect().catch((error) => {
console.error("Unable to connect to the host", error);
});
Перакананні ў стане кантэйнера должны адбывацца на канцэнтры MCP, а не толькі на статычной сторонце.
await build({
entryPoints: ["web/src/main.tsx"],
bundle: true,
minify: true,
format: "esm",
platform: "browser",
write: false,
outdir: "dist/web-bundle",
});
Рэгістрацыя хоста ёсць часткаю налаштаванняў — прайшоце тэсты знова пасля кожных змян URL або метода автаналізацыі.
npm run build
Спрытваце гэта як пункт контролю: тэсты пратаку должны быць успешныя перад будаваннем з’ёднання з хостам чату.
import { createMcpExpressApp } from
"@modelcontextprotocol/sdk/server/express.js";
import { StreamableHTTPServerTransport } from
"@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createWeatherMcpServer } from "./server.js";
const app = createMcpExpressApp();
app.post("/mcp", async (request, response) => {
response.setHeader("Cache-Control", "no-store");
const server = createWeatherMcpServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
await server.connect(transport);
response.on("close", () => {
void transport.close();
void server.close();
});
await transport.handleRequest(request, response, request.body);
});
Сакрэтныя даны трэба захаваць у сераўе платформы; ніколі не включайце кантрольныя клучы API ў аб’еднаны HTML-код.
app.get("/health", (_request, response) => {
response.json({ status: "ok", service: "weathersg" });
});
app.get("/ready", (_request, response) => {
const ready = existsSync("dist/web/weather-app.html");
response.status(ready ? 200 : 503).json({
status: ready ? "ready" : "not_ready",
});
});
Для хостаў, якія чакаюць URL стойкай сесіі MCP, лепш выкарыстоўваць HTTP з можлівасцю стрымавання дадзеных.
const port = Number(process.env.PORT ?? 3000);
const app = createWeatherHttpApp();
app.listen(port, "0.0.0.0", () => {
console.log(`WeatherSG listening on ${port}`);
});
Перакананні ў стане кантэйнера должны адбывацца на канцэнтры MCP, а не толькі на статычной сторонце.
npm run dev
Рэгістрацыя хоста ўскладнена канфігурацыяй — прабавайце знова пасля кожных змян URL або аўтэнтыфікацыі.
npx @modelcontextprotocol/inspector@latest
Спрытывайце гэта як пункт контролю: тэсты протаклау зяўляецца паспелі да таго, каб было наладзенае з’яўленне хоста для чату.
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps/examples/basic-host
npm install
SERVERS='["http://localhost:3000/mcp"]' npm start
Зберагаюце секрэты ў серавысі платформы; ніколі не включайце ключы API ў архіваваны HTML.
npm run typecheck
npm test
npm run build
Для хостаў, якія чакаюць стабільны URL сесіі MCP, лепш выкарыстоўваць HTTP з можлівасцю стрымавання.
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s \
CMD ["node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/ready').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"]
CMD ["node", "dist/src/index.js"]
Перакананні ў стане контэйнера павінны адбывацца на канцэнтры MCP, а не толькі на статычной сторонце.
docker build -t weathersg .
docker run --rm -p 3000:3000 weathersg
Рэгістрацыя хоста ўскладнена канфігурацыяй — прабавайце знова пасля кожных змян URL або аўтэнтыфікацыі.
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
gcloud services enable \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com
Спрытывайце гэта як пункт контролю: тэсты протаклау зяўляецца паспелі да таго, каб было наладзенае з’яўленне хоста для чату.
gcloud run deploy weathersg \
--source . \
--region asia-southeast1 \
--allow-unauthenticated \
--set-env-vars PUBLISHER_NAME=WeatherSG
Зберагаюце секрэты ў серавысі платформы; ніколі не включайце ключы API ў архіваваны HTML.
https://YOUR_SERVICE_HOST/mcp
Для хостаў, якія чакаюць стабільны URL сесіі MCP, лепш выкарыстоўваць HTTP з можлівасцю стрымавання.
curl -fsS https://YOUR_SERVICE_HOST/health
curl -fsS https://YOUR_SERVICE_HOST/ready
Перакантрольванні стану контэйнера павінны выконвацца на канцэнтры MCP, а не толькі на статычной сторонце.
Is it raining anywhere in Singapore right now?
Show me the Singapore weather map.
Now show only rainfall stations.
Рэгістрацыя хоста ўскладнена канфігурацыяю — прабавайце знова пасля кожных змян URL або автанаціі.
https://YOUR_SERVICE_HOST/mcp
Спрытваце гэта як пункт перакантрольвання: тэсты протакола павінны пройсці перш чым будзе налажана вяроўнасць з чат-хостам.
Тэставанне перад інтэрфейсам чата
Апрабавайце інструменты і рэсурсы за дапамогою інспектара MCP або скрыптаванага кліента. Чат-хосты паўтараюць адказы пра нявернасці; правільны локальны протакол заўсёды дапамагае заадно зберагчы час.
Развяртанне і рэгістрацыя
Аправіце контэйнер у Cloud Run (або адпаведны сервіс), пасля чаго налаштуйце канектары ChatGPT і Claude на публічны URL MCP з неабходнымі заголовкамі автанаціі. Перакантрольвыце взаімадзейнаўанне пасля завершэння працы TLS.
Заключанне
MCP App — гэта сукупнасць продуктовых элементаў: кантракт, інструменты, рэсурсы інтэрфейса, спосаб перадачы даных, контэйнер і налаштаванні хоста. Калі прыглядзецца не да ўсіх элементаў, дэманстрацыя не будзе працаваць у кліенце іншай адміністрацыі.
Кантракты API версій разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав на елементарныя часткі неабходна аддзельнае апрацоўванне запытанняў HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны выкарыстоўваць аднойчынную логіку запрашэння даных.
Зяўляйце логі з ідэнтыфікаторамі запрашэнняў разам з параграмі чату для пазнейшага аналізу.
Перабудавайце індэксы з такой частотай, якая адпавядае змінам у выхадных даных.
Адмовіце, калі найважлівейшыя сегменты выглядаюць слабкімі.
Кантракты API версій разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав на елементарныя часткі неабходна аддзельнае апрацоўвання запытанняў HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны выкарыстоўваць аднойчынную логіку запрашэння даных.
Зяўляйце логі з ідэнтыфікаторамі запрашэнняў разам з параграмі чату для пазнейшага аналізу.
Перабудавайце індэксы з такой частотай, якая адпавядае змінам у выхадных даных.
Адмовіце, калі найважлівейшыя сегменты выглядаюць слабкімі.
Кантракты API версій разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав на елементарныя часткі неабходна аддзельнае апрацоўвання запытанняў HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны выкарыстоўваць аднойчынную логіку запрашэння даных.
Зяўляйце логі з ідэнтыфікаторамі запрашэнняў разам з параграмі чату для пазнейшага аналізу.
Паўтараць стварэнне індэксаў па тым жа рытму, як і змена кантэнту у вучыльным матэрыяле.
Адхіляць запыткі, калі найважлівейшыя сегменты выглядаюць слабкімі.
Версіяваць кантракты API разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав аддзеліць хандлеры HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны перакладваць логіку адзысквання дадзеных.
Зяўляць логі з ідэнтыфікаторамі запыткаў разам з пунктамі чату для пазнейшага аналізу.
Паўтараць стварэнне індэксаў па тым жа рытму, як і змена кантэнту у вучыльным матэрыяле.
Адхіляць запыткі, калі найважлівейшыя сегменты выглядаюць слабкімі.
Версіяваць кантракты API разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав аддзеліць хандлеры HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны перакладваць логіку адзысквання дадзеных.
Зяўляць логі з ідэнтыфікаторамі запыткаў разам з пунктамі чату для пазнейшага аналізу.
Паўтараць стварэнне індэксаў па тым жа рытму, як і змена кантэнту у вучыльным матэрыяле.
Адхіляць запыткі, калі найважлівейшыя сегменты выглядаюць слабкімі.
Версіяваць кантракты API разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав аддзеліць хандлеры HTTP ад чыстых служб.
Тэмы інтэрфейса для тэмнага/яскравага рэжыма не павінны выкарыстоўваць аднародную логіку запрашэння даных.
Зявіце логі з ідэамі запрашэння даных разам з паворотамі ў чаце для пазнейшага аналізу.
Перабудавайце індэксы за тым самым рытамам, які і змена выхадных даных.
Адмовіцеся, калі верхнія сегменты выглядаюць слабкімі.
Указвайце версіі контрактав API разам з ідэямі вбудоввання.
Для тэстаў на елементарныя часткі аддзельна выкарыстоўвайце апрацоўшчыкі HTTP ад чыстых служб.
Тэмы інтэрфейса для тэмнага/яскравага рэжыма не павінны выкарыстоўваць аднародную логіку запрашэння даных.
Зявіце логі з ідэамі запрашэння даных разам з паворотамі ў чаце для пазнейшага аналізу.
Перабудавайце індэксы за тым самым рытамам, які і змена выхадных даных.
Адмовіцеся, калі верхнія сегменты выглядаюць слабкімі.
Указвайце версіі контрактав API разам з ідэямі вбудоввання.
Для тэстаў на елементарныя часткі аддзельна выкарыстоўвайце апрацоўшчыкі HTTP ад чыстых служб.
Тэмы інтэрфейса для тэмнага/яскравага рэжыма не павінны выкарыстоўваць аднародную логіку запрашэння даных.
Зявіце логі з ідэамі запрашэння даных разам з паворотамі ў чаце для пазнейшага аналізу.
Перабудавайце індэксы за тым самым рытамам, які і змена выхадных даных.
Адмовіцеся, калі верхнія сегменты выглядаюць слабкімі.
Кантракты API разных версій палячыяся ля ідэнтыфікатораў embedder.
Для тэстав на елементарныя часткі неабходна аддзельна обработка запытак HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны выкарыстоўваць аднойчынную логіку запрашэння даных.
Зяўляйце логі з ідэнтыфікаторамі запрашэнняў разам з параграмі чату для пазнейшага аналізу.
Перабудавайце індэксы з тым жа тымпам, як і зменшаецца колькасць выхадных даных.
Адмовіце, калі найважлівейшыя сегменты выглядаюць слабкімі.
Кантракты API разных версій палячыяся ля ідэнтыфікатораў embedder.
Для тэстав на елементарныя часткі неабходна аддзельна обработка запытак HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны выкарыстоўваць аднойчынную логіку запрашэння даных.
Зяўляйце логі з ідэнтыфікаторамі запрашэнняў разам з параграмі чату для пазнейшага аналізу.
Перабудавайце індэксы з тым жа тымпам, як і зменшаецца колькасць выхадных даных.
Адмовіце, калі найважлівейшыя сегменты выглядаюць слабкімі.
Кантракты API разных версій палячыяся ля ідэнтыфікатораў embedder.
Для тэстав на елементарныя часткі неабходна аддзельна обработка запытак HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны выкарыстоўваць аднойчынную логіку запрашэння даных.
Зяўляйце логі з ідэнтыфікаторамі запрашэнняў разам з параграмі чату для пазнейшага аналізу.
Паўтараць стварэнне індэксаў па тым жа рытму, як і змена кантэнту у вучыльным матэрыяле.
Адхіляць запыткі, калі найважлівейшыя сегменты выглядаюць слабкімі.
Версіяваць кантракты API разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав аддзеліць хандлеры HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны перакладваць логіку адзысквання дадзеных.
Зяўляць логі з ідэнтыфікаторамі запыткаў разам з пунктамі чату для пазнейшага аналізу.
Паўтараць стварэнне індэксаў па тым жа рытму, як і змена кантэнту у вучыльным матэрыяле.
Адхіляць запыткі, калі найважлівейшыя сегменты выглядаюць слабкімі.
Версіяваць кантракты API разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав аддзеліць хандлеры HTTP ад чыстых служб.
Тэмы інтэрфейса «тэмная/яскравая» не павінны перакладваць логіку адзысквання дадзеных.
Зяўляць логі з ідэнтыфікаторамі запыткаў разам з пунктамі чату для пазнейшага аналізу.
Паўтараць стварэнне індэксаў па тым жа рытму, як і змена кантэнту у вучыльным матэрыяле.
Адхіляць запыткі, калі найважлівейшыя сегменты выглядаюць слабкімі.
Версіяваць кантракты API разам з ідэнтыфікаторамі вбудоўвачаў.
Для тэстав аддзеліць хандлеры HTTP ад чыстых служб.
Тэмныя/светлыя тэмы інтэрфейса не павінны змянюваць логіку запошуку.
Запісваць ідэнтыфікаторы логаў разам з паказваннямі у чаце для пазнейшага аналізу.
Павтараць стварэнне індэксаў па тым жа рытму, як і змены у выхідных даных.
Адмовіцца, калі найважлівейшыя сегменты выглядаюць слабкімі.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаць фікстуры, адпаведных власнікаў і прыметкі паўранення разам з змянамі, якія былі імпортаваны чы розглядваны.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запошчання і розгортання змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце фікстуры, адміністратароў і прыметкі паўранення разам з процэсамі запрацоўкі і розпавесці змян.
Зберагаюце інформацыю працоўных прыладоў, ўласнікаў і запісаў аб вярнэнні да пачатковага стану разам з інформацыяй пра практычнае застосаванне і розпаковку змян.