agentsclimarketplace

Clickup observability

Skill ComeOnOliver/skillshub/skills/jeremylongshore/claude-code-plugins-plus-skills/clickup-observability

🧠 The right skill, one API call. AI agent skills registry with token-efficient skill resolution. 5,000+ skills from 500+ top repos.

Install
npx -y skills add ComeOnOliver/skillshub --skill clickup-observability

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

Monitor ClickUp API integrations with metrics, tracing, structured logging, and alerting using Prometheus, OpenTelemetry, and Grafana. Trigger: "clickup monitoring", "clickup metrics", "clickup observability", "monitor clickup", "clickup alerts", "clickup tracing", "clickup dashboard".

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.7 KB, as published. Nobody here has run it

ClickUp Observability

Overview

Monitor ClickUp API v2 integrations with metrics (request rate, latency, errors, rate limit usage), distributed tracing, and alerting.

Key Metrics

MetricTypeLabelsDescription
clickup_requests_totalCountermethod, endpoint, statusTotal API requests
clickup_request_duration_secondsHistogrammethod, endpointRequest latency
clickup_errors_totalCounterstatus_code, error_typeErrors by type
clickup_rate_limit_remainingGaugetoken_hashRate limit headroom
clickup_rate_limit_resets_totalCounterTimes we hit 429

Prometheus Instrumentation

import { Registry, Counter, Histogram, Gauge } from 'prom-client';

const registry = new Registry();

const requestCounter = new Counter({
  name: 'clickup_requests_total',
  help: 'Total ClickUp API v2 requests',
  labelNames: ['method', 'endpoint', 'status'] as const,
  registers: [registry],
});

const requestDuration = new Histogram({
  name: 'clickup_request_duration_seconds',
  help: 'ClickUp API request duration in seconds',
  labelNames: ['method', 'endpoint'] as const,
  buckets: [0.05, 0.1, 0.25, 0.5, 1, 2, 5],
  registers: [registry],
});

const rateLimitGauge = new Gauge({
  name: 'clickup_rate_limit_remaining',
  help: 'ClickUp rate limit remaining requests',
  registers: [registry],
});

const errorCounter = new Counter({
  name: 'clickup_errors_total',
  help: 'ClickUp API errors by status code',
  labelNames: ['status_code', 'error_type'] as const,
  registers: [registry],
});

Instrumented Client

async function instrumentedClickUpRequest<T>(
  path: string,
  options: RequestInit = {}
): Promise<T> {
  const method = options.method ?? 'GET';
  // Normalize endpoint for cardinality control (replace UUIDs)
  const endpoint = path.replace(/\/[a-zA-Z0-9]{6,}(?=\/|$|\?)/g, '/:id');
  const timer = requestDuration.startTimer({ method, endpoint });

  try {
    const response = await fetch(`https://api.clickup.com/api/v2${path}`, {
      ...options,
      headers: {
        'Authorization': process.env.CLICKUP_API_TOKEN!,
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });

    // Update rate limit gauge
    const remaining = response.headers.get('X-RateLimit-Remaining');
    if (remaining) rateLimitGauge.set(parseInt(remaining));

    const status = response.ok ? 'success' : `${response.status}`;
    requestCounter.inc({ method, endpoint, status });

    if (!response.ok) {
      const body = await response.json().catch(() => ({}));
      errorCounter.inc({
        status_code: String(response.status),
        error_type: body.ECODE ?? 'unknown',
      });
      throw new Error(`ClickUp ${response.status}: ${body.err}`);
    }

    return response.json();
  } catch (error) {
    if (!(error instanceof Error && error.message.startsWith('ClickUp'))) {
      errorCounter.inc({ status_code: 'network', error_type: 'fetch_error' });
    }
    throw error;
  } finally {
    timer();
  }
}

OpenTelemetry Tracing

import { trace, SpanStatusCode } from '@opentelemetry/api';

const tracer = trace.getTracer('clickup-integration', '1.0.0');

async function tracedClickUpCall<T>(
  operationName: string,
  path: string,
  fn: () => Promise<T>
): Promise<T> {
  return tracer.startActiveSpan(`clickup.${operationName}`, async (span) => {
    span.setAttribute('clickup.path', path);
    span.setAttribute('clickup.method', 'GET');

    try {
      const result = await fn();
      span.setStatus({ code: SpanStatusCode.OK });
      return result;
    } catch (error: any) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
      span.recordException(error);
      throw error;
    } finally {
      span.end();
    }
  });
}

Structured Logging

import pino from 'pino';

const logger = pino({ name: 'clickup', level: process.env.LOG_LEVEL ?? 'info' });

function logClickUpCall(data: {
  method: string;
  path: string;
  status: number;
  durationMs: number;
  rateLimitRemaining?: number;
  error?: string;
}): void {
  const level = data.status >= 500 ? 'error' : data.status >= 400 ? 'warn' : 'info';
  logger[level]({
    service: 'clickup',
    ...data,
  }, `ClickUp ${data.method} ${data.path} -> ${data.status} (${data.durationMs}ms)`);
}

Alert Rules

# prometheus/clickup_alerts.yaml
groups:
  - name: clickup
    rules:
      - alert: ClickUpHighErrorRate
        expr: rate(clickup_errors_total[5m]) / rate(clickup_requests_total[5m]) > 0.05
        for: 5m
        labels: { severity: warning }
        annotations:
          summary: "ClickUp API error rate > 5%"

      - alert: ClickUpHighLatency
        expr: histogram_quantile(0.95, rate(clickup_request_duration_seconds_bucket[5m])) > 3
        for: 5m
        labels: { severity: warning }
        annotations:
          summary: "ClickUp P95 latency > 3s"

      - alert: ClickUpRateLimitLow
        expr: clickup_rate_limit_remaining < 10
        for: 1m
        labels: { severity: critical }
        annotations:
          summary: "ClickUp rate limit nearly exhausted"

      - alert: ClickUpAuthFailures
        expr: increase(clickup_errors_total{status_code="401"}[5m]) > 0
        labels: { severity: critical }
        annotations:
          summary: "ClickUp authentication failures detected"

Metrics Endpoint

app.get('/metrics', async (req, res) => {
  res.set('Content-Type', registry.contentType);
  res.send(await registry.metrics());
});

Error Handling

IssueCauseSolution
High cardinalityDynamic IDs in labelsNormalize paths (replace IDs with :id)
Missing metricsUninstrumented code pathWrap all API calls through instrumented client
Alert stormThreshold too sensitiveTune for duration and threshold
Trace gapsMissing context propagationEnsure span context is passed

Resources

Next Steps

For incident response, see clickup-incident-runbook.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.