Integrate

Hand the prompt below to Claude Code (or any AI assistant) in your host project.

Drop this into Claude Code

Open your host project (e.g. car-coating) in Claude Code, paste the prompt below as your first message. It contains everything Claude needs: adapter choice, vendoring steps, code snippets, env vars, and verification. Works for projects on Firebase, Supabase, or neither — the dashboard handles persistence.

Claude Code prompt (copy & paste into your host project)
You are integrating MeetsEntry, an external monitoring tool, into this Next.js project. The dashboard is already deployed at https://meetscsec.meetsc.co.jp and the backend (Supabase) is already provisioned. Your job is only to wire this project to send telemetry.

# What MeetsEntry is
A Sentry-equivalent monitoring SDK built with an Adapter Pattern. It captures:
- Unhandled exceptions on Client, Server, and Edge runtimes
- Web Vitals (LCP, INP, CLS, FCP, TTFB)
- Custom metrics and audit events
- User context (uid, email, role) — call setUser() after auth

Before transport it: scrubs PII (tokens, JWTs, emails, IPs, CC numbers), hashes errors for grouping (issue_hash), token-bucket rate-limits, and batches by size + interval + pagehide.

# How this project will send telemetry
Use the HttpAdapter pointing at the MeetsEntry dashboard's /api/ingest endpoint. This works regardless of whether this project uses Firebase, Supabase, neither, or some other backend — the dashboard handles persistence.

Dashboard ingest endpoint: https://meetscsec.meetsc.co.jp/api/ingest
Source repo (for reference): https://github.com/LucaTrabuio/meetsentry

# Steps

## 1. Add the SDK to this project

Vendor the SDK by copying lib/monitor/ from https://github.com/LucaTrabuio/meetsentry into this project's lib/monitor/. Then install the one runtime dep:

    npm install web-vitals

(If the user prefers a package install instead of vendoring, the SDK is internal — ask them which path to take.)

## 2. Create a top-level client provider

Create app/monitor-provider.tsx with this content (substitute the projectId for this project's identifier — e.g. "car-coating" or "apollo-one"):

```tsx
"use client";
import { useEffect } from "react";
import { initMonitor, installBrowserHandlers, HttpAdapter, tryGetMonitor } from "@/lib/monitor";

export function MonitorProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    if (!tryGetMonitor()) {
      initMonitor({
        projectId: "<THIS_PROJECT_ID>",
        environment: process.env.NEXT_PUBLIC_APP_ENV ?? "production",
        adapter: new HttpAdapter({
          endpoint: process.env.NEXT_PUBLIC_MONITOR_ENDPOINT!,
          apiKey: process.env.NEXT_PUBLIC_MONITOR_KEY,
        }),
        batchSize: 20,
        flushIntervalMs: 5000,
        retentionDays: 30,
        // Optional: capture the last 20 console.* + fetch calls before
        // an error and attach them to the event so the dashboard shows
        // the lead-up. Off by default.
        breadcrumbs: {
          enabled: true,
          autoConsole: true,
          autoFetch: true,
        },
      });
    }
    installBrowserHandlers();
  }, []);
  return <>{children}</>;
}
```

## 3. Wire the provider into app/layout.tsx

Wrap children in <MonitorProvider>. Keep it inside <body> and around all routes:

```tsx
import { MonitorProvider } from "./monitor-provider";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <MonitorProvider>{children}</MonitorProvider>
      </body>
    </html>
  );
}
```

## 4. Server-side bootstrap (for Server Actions / Route Handlers)

Create lib/monitor-server.ts:

```ts
import { initMonitor, tryGetMonitor, HttpAdapter } from "@/lib/monitor";

if (!tryGetMonitor()) {
  initMonitor({
    projectId: "<THIS_PROJECT_ID>",
    environment: process.env.APP_ENV ?? process.env.NEXT_PUBLIC_APP_ENV ?? "production",
    adapter: new HttpAdapter({
      endpoint: process.env.MONITOR_ENDPOINT ?? process.env.NEXT_PUBLIC_MONITOR_ENDPOINT!,
      apiKey: process.env.MONITOR_KEY ?? process.env.NEXT_PUBLIC_MONITOR_KEY,
    }),
    batchSize: 5,
    flushIntervalMs: 1000,
  });
}
export {};
```

## 5. Wrap server actions and route handlers

For Server Actions:

```ts
"use server";
import { withMonitor } from "@/lib/monitor";
import "@/lib/monitor-server";

async function _doThing(input: Input) { /* existing logic */ }
export const doThing = withMonitor(_doThing, "doThing");
```

For Route Handlers:

```ts
import { withMonitorRoute } from "@/lib/monitor";
import "@/lib/monitor-server";

async function _POST(req: Request) { /* existing logic */ }
export const POST = withMonitorRoute(_POST, "myRoute");
```

## 6. Hook into auth (optional but valuable)

Wherever the existing auth state updates, call setUser/clearUser. For Firebase Auth:

```ts
import { tryGetMonitor } from "@/lib/monitor";
onAuthStateChanged(auth, (user) => {
  if (user) tryGetMonitor()?.setUser({ uid: user.uid, email: user.email });
  else tryGetMonitor()?.clearUser();
});
```

For Supabase Auth:

```ts
import { tryGetMonitor } from "@/lib/monitor";
supabase.auth.onAuthStateChange((_event, session) => {
  if (session?.user) tryGetMonitor()?.setUser({ uid: session.user.id, email: session.user.email });
  else tryGetMonitor()?.clearUser();
});
```

Emails are auto-redacted by the PII scrubber. If the project specifically wants user emails visible, pass `maskPatterns: []` to initMonitor (only do this if the user explicitly asks).

## 7. Environment variables

Add to .env.local and to the Vercel project's Environment Variables:

    NEXT_PUBLIC_MONITOR_ENDPOINT=https://meetscsec.meetsc.co.jp/api/ingest
    NEXT_PUBLIC_MONITOR_KEY=<ask user for the value — set as MONITOR_INGEST_KEY on the MeetsEntry Vercel project>
    NEXT_PUBLIC_APP_ENV=production  # or "preview" / "development"

## 8. Verify

1. Start the dev server: `npm run dev`
2. Trigger any code path that throws (or call `tryGetMonitor()?.captureMessage("smoke test from <project>")` from a temporary button).
3. Open https://meetscsec.meetsc.co.jp/dashboard
4. Confirm:
   - The event appears under "Recent events"
   - It's grouped on the left under "Issues" with the right issue_hash
   - The project_id matches "<THIS_PROJECT_ID>"
   - Any PII in the message is replaced with [REDACTED]
5. Remove the smoke-test button once verified.

# Constraints
- Do NOT log the API key or paste it into committed files
- Do NOT call initMonitor() outside a useEffect on the client; it must only run in the browser
- Do NOT change the SDK source under lib/monitor/ — treat it as vendored
- Do not introduce error boundaries that swallow exceptions before the SDK can see them — let errors propagate to window.onerror

Endpoints & secrets

Values referenced by the prompt above and by the env vars you set in the host project.

Dashboard URL
https://meetscsec.meetsc.co.jp
HTTP ingest endpoint
https://meetscsec.meetsc.co.jp/api/ingest
default for HttpAdapter (recommended)
Supabase function URL
https://csqxtxbexgczhsullpit.supabase.co/functions/v1/ingest
alternative if a project uses SupabaseAdapter directly
Supabase publishable key
sb_publishable_wAJ1B1pmGUVrhO0ibyMmWw_vmZie-Ul
safe to expose; RLS gates writes

Choosing an adapter for a host project

HttpAdapter ⭐
Recommended for car-coating + apolloOne
Posts JSON to the dashboard's /api/ingest. The host project doesn't need to know about Supabase or Firebase. One config for all projects. Use this unless you have a specific reason not to.
SupabaseAdapter
If the host project already uses Supabase
Posts directly to the Supabase Edge Function. Saves one hop. Useful when the host project's network policy already trusts the Supabase domain.
FirebaseAdapter
If you want host-project data in its own Firebase
Posts to a Firebase Cloud Function you deploy in the host project's own Firebase project. Note: events sent this way won't appear in the MeetsEntry dashboard (which reads Supabase) — useful only if you want telemetry colocated with the host's other Firestore data.

Code snippets per adapter

HttpAdapter (Firebase-using projects, Supabase-using projects — any project)

initMonitor — HttpAdapter
import { initMonitor, HttpAdapter, installBrowserHandlers } from "@/lib/monitor";

initMonitor({
  projectId: "car-coating",
  environment: process.env.NEXT_PUBLIC_APP_ENV ?? "production",
  adapter: new HttpAdapter({
    endpoint: "https://meetscsec.meetsc.co.jp/api/ingest",
    apiKey: process.env.NEXT_PUBLIC_MONITOR_KEY,
  }),
});
installBrowserHandlers();

SupabaseAdapter

initMonitor — SupabaseAdapter
import { initMonitor, SupabaseAdapter, installBrowserHandlers } from "@/lib/monitor";

initMonitor({
  projectId: "car-coating",
  environment: process.env.NEXT_PUBLIC_APP_ENV ?? "production",
  adapter: new SupabaseAdapter({
    url: "https://csqxtxbexgczhsullpit.supabase.co",
    anonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    mode: "edge",
    functionName: "ingest",
  }),
});
installBrowserHandlers();

FirebaseAdapter

Requires you to first deploy schema/firebase-function-ingest.ts to the host project's Firebase as a Cloud Function named "ingest". See the repo's docs/INTEGRATION.md.

initMonitor — FirebaseAdapter
import { initMonitor, FirebaseAdapter, installBrowserHandlers } from "@/lib/monitor";

initMonitor({
  projectId: "car-coating",
  environment: process.env.NEXT_PUBLIC_APP_ENV ?? "production",
  adapter: new FirebaseAdapter({
    functionUrl: process.env.NEXT_PUBLIC_FIREBASE_INGEST_URL!,
    apiKey: process.env.NEXT_PUBLIC_MONITOR_KEY,
  }),
});
installBrowserHandlers();

Verify the wire-up

  1. Run the host project locally and trigger any error path (or call tryGetMonitor()?.captureMessage("hello") from a temporary button).
  2. Open /dashboard on this site.
  3. Confirm: the event shows up, project_id is correct, PII in the message is replaced with [REDACTED], and repeated triggers collapse into the same issue.