Startseite / Artikel

WeatherSG MCP App: Tools Plus Interaktive Karte in ChatGPT und Claude

WeatherSG, eine MCP-App in TypeScript: Datenschnittstelle, Streamable HTTP, Deployment auf Cloud Run sowie Registrierung als Host, damit ChatGPT und Claude eine Echtzeit-Wetterkarte anzeigen können.

Verlag: REACTAPP.TOP16 Min. Lesezeit3664 Wörter

Quellen: How to Build and Deploy an MCP App

MCP-Apps erweitern das Model Context Protocol über Tools hinaus, die nur Text zurückgeben: Sie können interaktive UI-Ressourcen bereitstellen, die Hosts wie ChatGPT oder Claude anzeigen können. In dieser Anleitung wird WeatherSG in TypeScript entwickelt – mit Datenkontrakt, Wetterabruffunktion, herkömmlichem Tool, als zusammengefasste HTML-Ressource für die Karten-UI, Streamable-HTTP-Transport, Deployment in Cloud Run sowie Registrierung bei beiden Hosts.

Was eine MCP-App ist

Ein Server stellt weiterhin Tools und Ressourcen bereit; eine App liefert zusätzlich eine Frontend-Oberfläche, die der Host nach einem Toolaufruf einbettet. Der Ablauf ist: Prompt → Tool → UI-Ressource → Benutzerinteraktion → weitere Toolaufrufe bei Bedarf.

Warum TypeScript hier

Eine Sprache für Server und UI-Bündelung hält die Deployment-Einheit klein. Strikte Typen helfen dabei, dass der Datenkontrakt zwischen Normalisierungsmechanismen und der Kartenanzeige konsistent bleibt.

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

Bewahren Sie Geheimnisse im Plattformumfeld auf; integrieren Sie niemals API-Schlüssel in das mitgelieferte 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"
  }
}

Wählen Sie streambaren HTTP für Hosts, die eine dauerhafte MCP-Sitzungs-URL erwarten.

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

Die Gesundheitsüberprüfungen der Container müssen den MCP-Endpunkt aufrufen, nicht nur eine statische Seite.

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

Die Registrierung des Hosts ist eine Konfiguration – testen Sie erneut nach jeder Änderung der URL oder des Authentifizierungsmechanismus.

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

Betrachten Sie dies als Kontrollpunkt: Protokolltests müssen bestehen, bevor jegliche Verbindung zum Chat-Host hergestellt wird.

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

Bewahren Sie Geheimnisse im Plattformumfeld auf; integrieren Sie niemals API-Schlüssel in das mitgelieferte HTML.

return weatherSnapshotSchema.parse(snapshot);

Wählen Sie streambaren HTTP für Hosts, die eine dauerhafte MCP-Sitzungs-URL erwarten.

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

Die Gesundheitsüberprüfungen der Container müssen den MCP-Endpunkt aufrufen, nicht nur eine statische Seite.

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

Die Registrierung des Hosts ist eine Konfiguration – testen Sie erneut nach jeder Änderung der URL oder des Authentifizierungsmechanismus.

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

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

Betrachten Sie dies als Kontrollpunkt: Die Protokolltests müssen bestehen, bevor irgendetwas mit dem Chat-Host verbunden wird.

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

Bewahren Sie Geheimnisse im Plattformumfeld auf; integrieren Sie niemals API-Schlüssel in das mitgelieferte 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"],
          },
        },
      },
    }],
  }),
);

Wählen Sie streambaren HTTP für Hosts, die eine dauerhafte MCP-Sitzungs-URL erwarten.

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

Die Gesundheitsprüfungen der Container müssen den MCP-Endpunkt aufrufen, nicht nur eine statische Seite.

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

Die Registrierung des Hosts ist eine Konfiguration – testen Sie erneut nach jeder Änderung der URL oder des Authentifizierungsmechanismus.

npm run build

Betrachten Sie dies als Kontrollpunkt: Die Protokolltests müssen bestehen, bevor irgendetwas mit dem Chat-Host verbunden wird.

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

Bewahren Sie Geheimnisse im Plattformumfeld auf; integrieren Sie niemals API-Schlüssel in das mitgelieferte 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",
  });
});

Wählen Sie streambaren HTTP für Hosts, die eine dauerhafte MCP-Sitzungs-URL erwarten.

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

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

Die Gesundheitsprüfungen der Container müssen den MCP-Endpunkt aufrufen, nicht nur eine statische Seite.

npm run dev

Die Registrierung des Hosts ist eine Konfiguration – testen Sie erneut nach jeder Änderung der URL oder des Authentifizierungsmechanismus.

npx @modelcontextprotocol/inspector@latest

Betrachten Sie dies als Kontrollpunkt: Die Protokolltests müssen bestehen, bevor irgendeine Verbindung zum Chat-Host hergestellt wird.

git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps/examples/basic-host
npm install
SERVERS='["http://localhost:3000/mcp"]' npm start

Behalten Sie Geheimnisse im Plattformumfeld; integrieren Sie niemals API-Schlüssel in das mitgelieferte HTML.

npm run typecheck
npm test
npm run build

Wählen Sie streambaren HTTP für Hosts, die eine dauerhafte MCP-Sitzungs-URL erwarten.

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

Die Gesundheitsprüfungen der Container müssen den MCP-Endpunkt erreichen, nicht nur eine statische Seite.

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

Die Registrierung des Hosts ist eine Konfiguration – testen Sie erneut nach jeder Änderung der URL oder des Authentifizierungsmechanismus.

gcloud auth login
gcloud config set project YOUR_PROJECT_ID
gcloud services enable \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com

Betrachten Sie dies als Kontrollpunkt: Die Protokolltests müssen bestehen, bevor irgendeine Verbindung zum Chat-Host hergestellt wird.

gcloud run deploy weathersg \
  --source . \
  --region asia-southeast1 \
  --allow-unauthenticated \
  --set-env-vars PUBLISHER_NAME=WeatherSG

Behalten Sie Geheimnisse im Plattformumfeld; integrieren Sie niemals API-Schlüssel in das mitgelieferte HTML.

https://YOUR_SERVICE_HOST/mcp

Wählen Sie streambaren HTTP für Hosts, die eine dauerhafte MCP-Sitzungs-URL erwarten.

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

Die Gesundheitsüberprüfungen des Containers müssen den MCP-Endpunkt erreichen, nicht nur eine statische Seite.

Is it raining anywhere in Singapore right now?

Show me the Singapore weather map.

Now show only rainfall stations.

Die Registrierung des Hosts ist eine Konfiguration – testen Sie erneut nach jeder Änderung der URL oder des Authentifizierungsmechanismus.

https://YOUR_SERVICE_HOST/mcp

Betrachten Sie dies als Kontrollpunkt: Die Protokolltests müssen erfolgreich sein, bevor irgendeine Verbindung zum Chat-Host hergestellt wird.

Testen vor der Chat-Oberfläche

Üben Sie die Tools und Ressourcen mit einem MCP-Inspektor oder einem skriptbasierten Client. Chat-Hosts verstärken Fehler; ein funktionierendes lokales Protokoll spart Stunden Zeit.

Einrichten und registrieren

Schicken Sie den Container zu Cloud Run (oder einem ähnlichen Dienst) und weisen Sie anschließend die ChatGPT- sowie Claude-Connector auf die öffentliche MCP-URL mit den erforderlichen Authentifizierungs-Headern hin. Überprüfen Sie erneut die Interaktion nach Abschluss der TLS-Verschlüsselung.

Fazit

Eine MCP-App ist eine Produktoberfläche: Vertrag, Tools, UI-Ressourcen, Transportmechanismus, Container sowie Host-Konfiguration. Fehlt eine Schicht, funktioniert die Demo im Client eines anderen Nutzers nicht.

Versionen der API-Verträge neben den Embedder-ID‑Werten.

Trenne die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht duplizieren.

Protokolliere die Abruf-ID‑Werte zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstelle die Indizes in einem Rhythmus neu, der dem Änderungsgrad der Quellen entspricht.

Weise Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionen der API-Verträge neben den Embedder-ID‑Werten.

Trenne die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht duplizieren.

Protokolliere die Abruf-ID‑Werte zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstelle die Indizes in einem Rhythmus neu, der dem Änderungsgrad der Quellen entspricht.

Weise Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionen der API-Verträge neben den Embedder-ID‑Werten.

Trenne die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht duplizieren.

Protokolliere die Abruf-ID‑Werte zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus neu, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Verschieben Sie die Versionen der API-Verträge neben die Embedder-ID.

Trennen Sie für Unit-Tests die HTTP-Handler von den reinen Services.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht abwandeln.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus neu, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Verschieben Sie die Versionen der API-Verträge neben die Embedder-ID.

Trennen Sie für Unit-Tests die HTTP-Handler von den reinen Services.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht abwandeln.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus neu, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Verschieben Sie die Versionen der API-Verträge neben die Embedder-ID.

Trennen Sie für Unit-Tests die HTTP-Handler von den reinen Services.

Dunkle/helle UI-Themen dürfen die Abruflogik nicht abwandeln.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus, der dem Änderungsgrad der Quelle entspricht.

Weisen Sie Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionieren Sie die API-Verträge neben den Embedder-IDs.

Trennen Sie HTTP-Handler von reinen Services für Unit-Tests.

Dunkle/helle UI-Themen dürfen die Abruflogik nicht abwandeln.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus, der dem Änderungsgrad der Quelle entspricht.

Weisen Sie Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionieren Sie die API-Verträge neben den Embedder-IDs.

Trennen Sie HTTP-Handler von reinen Services für Unit-Tests.

Dunkle/helle UI-Themen dürfen die Abruflogik nicht abwandeln.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus, der dem Änderungsgrad der Quelle entspricht.

Weisen Sie Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionen der API-Verträge neben den Embedder-ID‑Werten.

Trenne die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht duplizieren.

Protokolliere die Abruf-ID‑Werte zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstelle die Indizes in einem Rhythmus neu, der dem Änderungsgrad der Quellen entspricht.

Weise Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionen der API-Verträge neben den Embedder-ID‑Werten.

Trenne die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht duplizieren.

Protokolliere die Abruf-ID‑Werte zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstelle die Indizes in einem Rhythmus neu, der dem Änderungsgrad der Quellen entspricht.

Weise Anfragen ab, wenn die wichtigsten Segmente schwach erscheinen.

Versionen der API-Verträge neben den Embedder-ID‑Werten.

Trenne die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht duplizieren.

Protokolliere die Abruf-ID‑Werte zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus neu, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Verschieben Sie die Versionen der API-Verträge neben die Embedder-ID.

Trennen Sie die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht abspalten.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus neu, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Verschieben Sie die Versionen der API-Verträge neben die Embedder-ID.

Trennen Sie die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/lichte UI-Themen dürfen die Abruflogik nicht abspalten.

Protokollieren Sie die Abruf-IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus neu, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Verschieben Sie die Versionen der API-Verträge neben die Embedder-ID.

Trennen Sie die HTTP-Handler von den reinen Services für Unit-Tests.

Dunkle/helle UI-Themen dürfen die Abruflogik nicht verändern.

Protokollieren Sie die Abruf IDs zusammen mit den Chat-Schritten zur späteren Sortierung.

Erstellen Sie die Indizes in einem Rhythmus, der dem Wandel der Quelldaten entspricht.

Weigern Sie sich, wenn die wichtigsten Segmente schwach erscheinen.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Bewahren Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen auf.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie Fixtures, Verantwortliche sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Halten Sie die Fixtures, Eigentümer sowie Rollback-Hinweise neben den eingehenden und eingesetzten Änderungen bereit.

Bewahren Sie Konfigurationen, Eigentümerangaben sowie Notizen zu Rollbacks neben den eingehenden und eingesetzten Änderungen auf.