Strona główna / Artykuły

Aplikacja WeatherSG MCP: Narzędzia Plus – interaktywna mapa w ChatGPT i Claude

WeatherSG, aplikacja MCP w TypeScript: umowa danych, Streamable HTTP, rozwiązanie Cloud Run oraz rejestracja hosta, aby ChatGPT i Claude mogły wyświetlać żywą mapę pogody.

Wydawca: REACTAPP.TOP16 min czytania3664 słów

Źródła: How to Build and Deploy an MCP App

Aplikacje MCP rozszerzają Model Context Protocol poza narzędzia, które zwracają tekst: mogą dostarczać interaktywne zasoby UI, które hosty takie jak ChatGPT lub Claude mogą wyświetlić. Ten przewodnik pokazuje, jak stworzyć WeatherSG w TypeScript – kontrakt danych, pobieranie informacji o pogodzie, zwykłe narzędzie, interfejs mapy spakowany jako jeden plik HTML, transport HTTP typu Streamable, rozpakowanie kontenera w Cloud Run oraz rejestrację w obu hostach.

Czym jest aplikacja MCP

Serwer nadal udostępnia narzędzia i zasoby; aplikacja dodatkowo dostarcza front-end, który host wstawia po wyborze jej podczas wywołania narzędzia. Cykl to: prompt → narzędzie → zasób UI → interakcja użytkownika → dalsze wywołania narzędzi w razie potrzeby.

Dlaczego tutaj TypeScript

Jeden język do obsługi serwera i pakowania interfejsu UI sprawia, że jednostka do rozpakowania pozostaje mała. Ścisłe typy pomagają utrzymać spójność kontraktu danych pomiędzy normalizatorami a interfejsem mapy.

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

Zachowuj tajemnice w środowisku platformy; nigdy nie włączaj kluczy API do pakietowanego 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"
  }
}

Zaleca się używanie strumieniowalnego HTTP dla hostów, które oczekują trwałego adresu URL sesji MCP.

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

Sprawdzania stanu kontenera muszą dotrzeć do punktu końcowego MCP, a nie tylko do strony statycznej.

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>;

Rejestracja hosta to kwestia konfiguracji — sprawdź ponownie po każdej zmianie adresu URL lub metod autoryzacji.

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;
  }
}

Traktuj to jako punkt kontrolny: testy protokołu powinny przejść przed podłączeniem do hosta czatu.

const [forecastResult, rainfallResult] = await Promise.allSettled([
  fetchValidated(TWO_HOUR_FORECAST_URL, forecastSchema, "Forecast"),
  fetchValidated(RAINFALL_URL, rainfallSchema, "Rainfall"),
]);

Zachowuj tajemnice w środowisku platformy; nigdy nie włączaj kluczy API do pakietowanego HTML.

return weatherSnapshotSchema.parse(snapshot);

Zaleca się używanie strumieniowalnego HTTP dla hostów, które oczekują trwałego adresu URL sesji MCP.

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.",
  },
);

Sprawdzania stanu kontenera muszą dotrzeć do punktu końcowego MCP, a nie tylko do strony statycznej.

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}.`,
      }],
    };
  },
);

Rejestracja hosta to kwestia konfiguracji — sprawdź ponownie po każdej zmianie adresu URL lub metod autoryzacji.

import {
  registerAppResource,
  registerAppTool,
  RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";

export const WEATHER_APP_URI =
  "ui://weathersg/singapore-weather-map/v1.html";

Traktuj to jako punkt kontrolny: testy protokołu muszą przejść przed podłączeniem hosta do czatu.

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,
      }],
    };
  },
);

Zachowuj sekrety w środowisku platformy; nigdy nie włączaj kluczy API do pakietowanego 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"],
          },
        },
      },
    }],
  }),
);

Zaleca się użycie strumieniującego HTTP dla hostów, które oczekują trwałego adresu URL sesji MCP.

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);
});

Sprawdzanie stanu kontenera musi dotyczyć punktu końcowego MCP, a nie tylko strony statycznej.

await build({
  entryPoints: ["web/src/main.tsx"],
  bundle: true,
  minify: true,
  format: "esm",
  platform: "browser",
  write: false,
  outdir: "dist/web-bundle",
});

Rejestracja hosta to kwestia konfiguracji — przetestuj ponownie po każdej zmianie adresu URL lub metod autoryzacji.

npm run build

Traktuj to jako punkt kontrolny: testy protokołu muszą przejść przed podłączeniem hosta do czatu.

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);
});

Zachowuj sekrety w środowisku platformy; nigdy nie włączaj kluczy API do pakietowanego 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",
  });
});

Zaleca się użycie strumieniującego HTTP dla hostów, które oczekują trwałego adresu URL sesji MCP.

const port = Number(process.env.PORT ?? 3000);
const app = createWeatherHttpApp();

app.listen(port, "0.0.0.0", () => {
  console.log(`WeatherSG listening on ${port}`);
});

Sprawdzanie stanu kontenera musi dotyczyć punktu końcowego MCP, a nie tylko strony statycznej.

npm run dev

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

npm run typecheck
npm test
npm run build

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"]

docker build -t weathersg .
docker run --rm -p 3000:3000 weathersg

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

https://YOUR_SERVICE_HOST/mcp

curl -fsS https://YOUR_SERVICE_HOST/health
curl -fsS https://YOUR_SERVICE_HOST/ready

Kontrole stanu kontenera muszą dotrzeć do punktu końcowego MCP, a nie tylko do statycznej strony.

Is it raining anywhere in Singapore right now?

Show me the Singapore weather map.

Now show only rainfall stations.

https://YOUR_SERVICE_HOST/mcp

Traktuj to jako punkt kontrolny: testy protokołu powinny przejść pomyślnie przed podłączeniem hosta do czatu.

Testowanie przed interfejsem czatu

Wykorzystaj narzędzia i zasoby za pomocą inspektora MCP lub skryptowanego klienta. Hosty czatu mogą powiększać problemy; lokalne działanie protokołu bez błędów oszczędza wiele czasu.

Rozwój i rejestracja

wyślij kontener do Cloud Run (lub równoważnego rozwiązania), a następnie skieruj łączniki ChatGPT i Claude na publiczny URL MCP z wymaganymi nagłówkami autoryzacji. Sprawdź ponownie interakcję z mapą po zakończeniu transmisji TLS.

Zakończenie

Aplikacja MCP to całość produktu: umowa, narzędzia, zasoby interfejsu użytkownika, mechanizm transportu, kontener oraz konfiguracja hosta. Brak jakiejkolwiek z tych warstw powoduje, że demonstracja nie działa w klientach innych osób.

Wersje umów API obok identyfikatorów embederów.

Rozdzielaj handlery HTTP od czystych usług w celu przeprowadzania testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

Wersje umów API obok identyfikatorów embederów.

Rozdzielaj handlery HTTP od czystych usług w celu przeprowadzania testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

Wersje umów API obok identyfikatorów embederów.

Rozdzielaj handlery HTTP od czystych usług w celu przeprowadzania testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbuduj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć prośby, gdy najważniejsze segmenty wydają się słabe.

Zaktualizuj umowy API obok identyfikatorów embederów.

Rozdziel przetwarzacze HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisz identyfikatory pobrań wraz z kolejnościami rozmów do późniejszej analizy.

Odbuduj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć prośby, gdy najważniejsze segmenty wydają się słabe.

Zaktualizuj umowy API obok identyfikatorów embederów.

Rozdziel przetwarzacze HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisz identyfikatory pobrań wraz z kolejnościami rozmów do późniejszej analizy.

Odbuduj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć prośby, gdy najważniejsze segmenty wydają się słabe.

Zaktualizuj umowy API obok identyfikatorów embederów.

Rozdziel przetwarzacze HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą rozgałęziać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

wersjonuj umowy API obok identyfikatorów embederów.

Rozdzielaj obsługę HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą rozgałęziać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

wersjonuj umowy API obok identyfikatorów embederów.

Rozdzielaj obsługę HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą rozgałęziać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

Wersje umów API obok identyfikatorów embederów.

Rozdzielaj handlery HTTP od czystych usług w celu przeprowadzania testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

Wersje umów API obok identyfikatorów embederów.

Rozdzielaj handlery HTTP od czystych usług w celu przeprowadzania testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbudowuj indeksy zgodnie z tempem zmian w źródłach danych.

Odrzucaj prośby, gdy najważniejsze segmenty wydają się słabe.

Wersje umów API obok identyfikatorów embederów.

Rozdzielaj handlery HTTP od czystych usług w celu przeprowadzania testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisuj identyfikatory pobrań wraz z kolejnościami rozmów w celu późniejszej analizy.

Odbuduj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć prośby, gdy najważniejsze segmenty wydają się słabe.

Zaktualizuj umowy API obok identyfikatorów embederów.

Rozdziel przetwarzacze HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisz identyfikatory pobrań wraz z kolejnościami rozmów do dalszej analizy.

Odbuduj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć prośby, gdy najważniejsze segmenty wydają się słabe.

Zaktualizuj umowy API obok identyfikatorów embederów.

Rozdziel przetwarzacze HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą oddzielać logiki pobierania danych.

Zapisz identyfikatory pobrań wraz z kolejnościami rozmów do dalszej analizy.

Odbuduj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć prośby, gdy najważniejsze segmenty wydają się słabe.

Zaktualizuj umowy API obok identyfikatorów embederów.

Rozdziel przetwarzacze HTTP od czystych usług w celu testów jednostkowych.

Tematy interfejsu ciemnego/jasnego nie mogą modyfikować logiki pobierania danych.

Zapisuj identyfikatory pobrań zgodnie z kolejnością rozmów w czacie w celu późniejszej analizy.

Odbudowuj indeksy w tempie odpowiadającym zmianom w źródle danych.

Odrzuć operację, jeśli najważniejsze segmenty wydają się słabe.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowuj pliki konfiguracyjne, osoby odpowiedzialne oraz notatki o odwróceniu zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj dane referencyjne, osoby odpowiedzialne oraz notatki dotyczące odwrócenia zmian obok procesów pobierania i wdrażania zmian.

Zachowaj informacje o konfiguracjach, właścicielach oraz notatkach dotyczących cofania zmian obok procesów wprowadzania i wdrażania zmian.

Literatura pokrewna

  • Neo4j MCP Server połączony z ChatGPT — Stwórz serwer MCP, podłącz narzędzia Neo4j, zarejestruj go w ChatGPT i zweryfikuj ścieżki odczytu z czatu do Cyphera.