Guck MCP

Guck is a tiny, MCP-first telemetry store for agentic debugging

Guck

Guck is a tiny, MCP-first telemetry store for agentic debugging. It provides token-efficient log analytics by capturing JSONL telemetry events and exposing a minimal MCP toolset for fast, filtered queries.

Guck is designed to be:

  • Language-agnostic: emit JSONL from any runtime
  • Filter-first: no default tailing; MCP tools focus on targeted queries
  • Low-friction: small optional SDK, simple wrap CLI for stdout/stderr

Install

pnpm add -g @guckdev/cli
# or
npm install -g @guckdev/cli
# or
npx @guckdev/cli

Note: the guck command is provided by @guckdev/cli. If you already have the unrelated npm guck installed globally, uninstall it first. If you previously installed guck-cli, switch to @guckdev/cli.

Quick start

  1. Configure MCP (Codex/Claude/Copilot):
{
  "mcpServers": {
    "guck": {
      "command": "guck",
      "args": ["mcp"],
      "env": {
        "GUCK_CONFIG_PATH": "/path/to/.guck.json"
      }
    }
  }
}
  1. Drop‑in log capture (JS) — use auto‑capture, emit(), or both:
import "@guckdev/sdk/auto";
import { emit } from "@guckdev/sdk";

emit({ message: "hello from app" });
  1. Run your app; the MCP client will spawn guck mcp and logs are queryable via guck.stats / guck.search.

Vite drop-in (dev)

Add the Vite plugin to proxy /guck/emit during development:

import { defineConfig } from "vite";
import { guckVitePlugin } from "@guckdev/vite";

export default defineConfig({
  plugins: [guckVitePlugin()],
});

Then point the browser SDK at /guck/emit.

Monorepo layout

  • packages/guck-cli — CLI (wrap/emit/checkpoint/mcp)
  • packages/guck-core — shared config/types/store/redaction
  • packages/guck-js — JS SDK
  • packages/guck-mcp — MCP server
  • packages/guck-py — Python SDK
  • packages/guck-vite — Vite dev server plugin
  • specs — shared contract fixtures for parity tests

Python SDK (preview)

PyPI install:

pip install guck-sdk

Local dev install:

uv pip install -e packages/guck-py

Usage:

from guck import emit

emit({"message": "hello from python"})

Best practice (copy-paste)

  1. Add shared config (commit to repo):

.guck.json

{
  "version": 1,
  "enabled": true,
  "default_service": "api"
}

Optional: add .guck.local.json for per-dev overrides (ignored by git). You can run guck init to scaffold .guck.json.

  1. Add one line to AGENTS.md:
When debugging, use Guck telemetry first (guck.stats → guck.search; tail only if asked).
  1. Run:
guck wrap --service api --session session-001 -- <your command>
guck mcp

Session vs trace

Guck supports both session_id and trace_id, but they serve different purposes:

  • trace_id is request-scope correlation (a single transaction across services).
  • session_id is run-scope correlation (a dev run, test run, or local experiment).

session_id is useful even when you already have traces because many events are not tied to a trace (startup, background jobs, cron tasks, etc.). It also gives you a simple way to filter a whole dev run without wiring trace propagation.

Example:

export GUCK_SESSION_ID=session-001
guck wrap --service api --session session-001 -- pnpm run dev

Config

Guck reads .guck.json from your repo root. If present, .guck.local.json is merged on top for per-dev overrides.

Guck is enabled by default using built-in defaults. Add a .guck.json (and optional .guck.local.json) or set GUCK_CONFIG_PATH (or GUCK_CONFIG) to point at a config file or repo directory. You can also set "enabled": false inside the config to turn it off explicitly.

For MCP usage across multiple repos, each tool accepts an optional config_path parameter to point at a specific .guck.json.

Multi-service or multi-repo tracing (shared store)

To trace across local microservices (or multiple repos), point every service at the same absolute log directory via GUCK_DIR. This creates a single shared log store that guck.search can query across. Use a shared GUCK_SESSION_ID to correlate events and distinct service names to separate sources.

Example shared env:

export GUCK_DIR=/path/to/guck/logs
export GUCK_SESSION_ID=session-001
# optional: share a single config across repos
export GUCK_CONFIG_PATH=/path/to/shared/.guck.json

Example shared config:

{
  "version": 1,
  "enabled": true,
  "default_service": "api",
  "redaction": {
    "enabled": true,
    "keys": ["authorization","api_key","token","secret","password"],
    "patterns": ["sk-[A-Za-z0-9]{20,}","Bearer\\s+[A-Za-z0-9._-]+"]
  },
  "mcp": { "max_results": 200, "max_output_chars": 20000, "default_lookback_ms": 300000 }
}

Remote backends (CloudWatch/K8s) require optional SDK installs; install only if you use them.

JS SDK auto-capture (stdout/stderr)

The JS SDK can patch process.stdout and process.stderr to emit Guck events. Enable it early in your app startup:

import "@guckdev/sdk/auto";
// or
import { installAutoCapture } from "@guckdev/sdk";
installAutoCapture();

Config toggles:

{ "sdk": { "enabled": true, "capture_stdout": true, "capture_stderr": true } }

If you're using guck wrap, the CLI sets GUCK_WRAPPED=1 and the SDK auto-capture intentionally skips to avoid double logging.

Browser SDK (console + errors)

Use a dev server endpoint that accepts /guck/emit and writes events to the local store. In Vite, the @guckdev/vite plugin provides this endpoint. For other stacks, add a small endpoint that forwards payloads to your server-side emit().

Emit browser events:

import { createBrowserClient } from "@guckdev/browser";

const client = createBrowserClient({
  endpoint: "/guck/emit",
  service: "web",
  sessionId: "session-001",
});

await client.emit({ message: "hello from the browser" });

Auto-capture console output + unhandled errors:

const { stop } = client.installAutoCapture();

console.error("boom");

// call stop() to restore console and listeners (useful in component unmounts/tests)
stop();

Notes:

  • installAutoCapture() should usually be called once at app startup; repeated calls will wrap console multiple times.
  • If you install it inside a component or test, call stop() on cleanup to avoid duplicate logging.
  • For SPAs, it's fine to call installAutoCapture() once in your app entry (e.g. index.ts) and never call stop().
  • There is no prebuilt UMD/IIFE bundle yet; for vanilla JS you should use a bundler or a native ESM import.

Environment overrides

  • GUCK_CONFIG_PATH — explicit config path (file or repo dir)
  • GUCK_CONFIG — alias of GUCK_CONFIG_PATH
  • GUCK_DIR — store dir override (default: ~/.guck/logs)
  • GUCK_ENABLED — true/false
  • GUCK_SERVICE — service name
  • GUCK_SESSION_ID — session override
  • GUCK_RUN_ID — run id override

Checkpoint

guck checkpoint writes a .guck-checkpoint file in the root of your store dir (GUCK_DIR or ~/.guck/logs) containing an epoch millisecond timestamp. When MCP tools are called without since, Guck uses the checkpoint timestamp as the default time window. You can also pass since: "checkpoint" to explicitly anchor a query to the checkpoint.

Event schema (JSONL)

Each line in the log is a single JSON event:

{
  "id": "uuid",
  "ts": "2026-02-08T18:40:00.123Z",
  "level": "info",
  "type": "log",
  "service": "worker",
  "run_id": "uuid",
  "session_id": "session-123",
  "message": "speaker started",
  "data": { "turnId": 3 },
  "tags": { "env": "local" },
  "trace_id": "...",
  "span_id": "...",
  "source": { "kind": "sdk" }
}

Store layout

By default, Guck writes per-run JSONL files under ~/.guck/logs:

~/.guck/logs/<service>/<YYYY-MM-DD>/<run_id>.jsonl

Set GUCK_DIR to override the root.

Minimal CLI

Guck’s CLI is intentionally minimal. It exists to capture and serve telemetry; filtering is MCP-first.

  • guck init — create .guck.json
  • guck checkpoint — write .guck-checkpoint epoch timestamp
  • guck wrap --service <name> --session <id> -- <cmd...> — capture stdout/stderr
  • guck emit --service <name> --session <id> — append JSON events from stdin
  • guck mcp — start MCP server
  • guck upgrade [--manager <npm|pnpm|yarn|bun>] — update the CLI install

MCP tools

Guck exposes these MCP tools (filter-first):

  • guck.search
  • guck.search_batch
  • guck.stats
  • guck.sessions
  • guck.tail (available, but not default in docs)

Search and tail parameters

guck.search and guck.tail support additional output and query controls:

  • query — boolean search over message only (case-insensitive). Supports AND, OR, NOT, parentheses, and quoted phrases.
  • contains — substring search across message/type/session_id/data (unchanged).
  • formatjson (default) or text.
  • fields — when format: "json", project events to these fields. Dotted paths like data.rawPeak are supported.
  • flatten — when format: "json", emit dotted field paths as top-level keys (e.g. "data.rawPeak": 43).
  • template — when format: "text", format each line using tokens like {ts}|{service}|{message}. Dotted tokens like {data.rawPeak} are supported. Missing tokens become empty strings.
  • force — bypass output-size guard and return the full payload.
  • max_message_chars — per-message cap; trims the message field only.

Output is capped by mcp.max_output_chars. If a response would exceed the cap, the tool returns a warning instead of events/lines unless force=true. Warnings include avg_message_chars and max_message_chars computed from full, untrimmed messages.

Examples:

{ "query": "error AND (db OR timeout)" }
{ "format": "text", "template": "{ts}|{service}|{message}" }
{ "format": "json", "fields": ["ts", "level", "message"] }
{ "format": "json", "fields": ["ts", "data.rawPeak"], "flatten": true }

Batch search:

{
  "searches": [
    { "id": "errors", "query": "error", "limit": 50 },
    { "id": "warnings", "levels": ["warn"], "limit": 50, "max_message_chars": 200 }
  ]
}

Recommended minimal output for agents:

{ "format": "text", "template": "{ts}|{service}|{message}" }

AI usage guidance

Start with stats, then search, and only tail if needed:

  1. guck.stats with a narrow time window
  2. guck.search for relevant types/levels/messages
  3. guck.tail only when live-streaming is required

This keeps prompts short and avoids flooding the model with irrelevant logs.

Debugging strategy (recommended)

Use Guck as a tight loop to avoid log spam and wasted tokens:

  1. Scope with guck.stats (short time window, service/session).
  2. Inspect with guck.search for errors/warns or a specific boundary.
  3. Hypothesize the failing stage or component.
  4. Instrument only the boundary (entry/exit, inputs/outputs).
  5. Re-run and re-query the same narrow window.

This keeps investigations focused while still enabling deep, iterative debugging.

Redaction

Guck applies redaction on write and on read using configured key names and regex patterns.

Compatibility

Any language can emit Guck events by writing JSONL lines to the store. The optional SDK simply adds conveniences like run_id and redaction.

MCP server config example

{
  "mcpServers": {
    "guck": {
      "command": "guck",
      "args": ["mcp"],
      "env": {
        "GUCK_CONFIG_PATH": "/path/to/.guck.json"
      }
    }
  }
}

License

MIT

guck

Related Servers