linear-observability
Implement monitoring, logging, and alerting for Linear integrations. Use when setting up metrics collection, creating dashboards, or configuring alerts for Linear API usage. Trigger with phrases like "linear monitoring", "linear observability", "linear metrics", "linear logging", "monitor linear integration".
Install
mkdir -p .claude/skills/linear-observability && curl -L -o skill.zip "https://mcp.directory/api/skills/download/7592" && unzip -o skill.zip -d .claude/skills/linear-observability && rm skill.zipInstalls to .claude/skills/linear-observability
About this skill
Linear Observability
Overview
Production monitoring for Linear integrations using Prometheus metrics, structured logging with pino, health checks, and alerting rules. Track API latency, error rates, rate limit headroom, and webhook throughput.
Prerequisites
- Linear integration deployed
- Prometheus or Datadog for metrics
- Structured logging (pino, winston)
- Alerting system (PagerDuty, OpsGenie, Slack)
Instructions
Step 1: Define Metrics
// src/metrics/linear-metrics.ts
import { Counter, Histogram, Gauge, register } from "prom-client";
export const metrics = {
// API request tracking
apiRequests: new Counter({
name: "linear_api_requests_total",
help: "Total Linear API requests",
labelNames: ["operation", "status"],
}),
// Request duration
apiLatency: new Histogram({
name: "linear_api_request_duration_seconds",
help: "Linear API request duration",
labelNames: ["operation"],
buckets: [0.1, 0.25, 0.5, 1, 2, 5, 10],
}),
// Rate limit headroom
rateLimitRemaining: new Gauge({
name: "linear_rate_limit_remaining",
help: "Remaining rate limit budget",
labelNames: ["type"], // "requests" or "complexity"
}),
// Webhook tracking
webhooksReceived: new Counter({
name: "linear_webhooks_received_total",
help: "Total webhooks received",
labelNames: ["type", "action"],
}),
webhookProcessingDuration: new Histogram({
name: "linear_webhook_processing_seconds",
help: "Webhook processing duration",
labelNames: ["type"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
}),
// Cache effectiveness
cacheHits: new Counter({
name: "linear_cache_hits_total",
help: "Cache hit count",
labelNames: ["key"],
}),
cacheMisses: new Counter({
name: "linear_cache_misses_total",
help: "Cache miss count",
labelNames: ["key"],
}),
};
// Expose metrics endpoint
app.get("/metrics", async (req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
Step 2: Instrumented Client Wrapper
import { LinearClient } from "@linear/sdk";
function instrumentedCall<T>(
operation: string,
fn: () => Promise<T>
): Promise<T> {
const timer = metrics.apiLatency.startTimer({ operation });
return fn()
.then((result) => {
metrics.apiRequests.inc({ operation, status: "success" });
timer();
return result;
})
.catch((error: any) => {
const status = error.status === 429 ? "rate_limited" : "error";
metrics.apiRequests.inc({ operation, status });
timer();
throw error;
});
}
// Usage
const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY! });
const teams = await instrumentedCall("teams", () => client.teams());
const issues = await instrumentedCall("issues", () =>
client.issues({ first: 50 })
);
Step 3: Structured Logging
import pino from "pino";
const logger = pino({
level: process.env.LOG_LEVEL ?? "info",
formatters: {
level: (label) => ({ level: label }),
},
});
const linearLog = logger.child({ component: "linear" });
// Log API calls
function logApiCall(operation: string, durationMs: number, success: boolean, meta?: any) {
linearLog.info({
event: "api_call",
operation,
durationMs,
success,
...meta,
});
}
// Log webhook events
function logWebhook(type: string, action: string, deliveryId: string, meta?: any) {
linearLog.info({
event: "webhook",
type,
action,
deliveryId,
...meta,
});
}
// Log errors with context
function logError(operation: string, error: any) {
linearLog.error({
event: "error",
operation,
errorMessage: error.message,
errorStatus: error.status,
errorType: error.type,
// Never log API keys or tokens
});
}
Step 4: Health Check Endpoint
interface HealthCheck {
status: "healthy" | "degraded" | "unhealthy";
checks: Record<string, {
status: string;
latencyMs?: number;
error?: string;
}>;
timestamp: string;
}
async function checkLinearHealth(client: LinearClient): Promise<HealthCheck> {
const checks: HealthCheck["checks"] = {};
// Check API connectivity
const apiStart = Date.now();
try {
const viewer = await client.viewer;
checks.linear_api = {
status: "healthy",
latencyMs: Date.now() - apiStart,
};
} catch (error: any) {
checks.linear_api = {
status: "unhealthy",
latencyMs: Date.now() - apiStart,
error: error.message,
};
}
// Check rate limit headroom
try {
const resp = await fetch("https://api.linear.app/graphql", {
method: "POST",
headers: {
Authorization: process.env.LINEAR_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "{ viewer { id } }" }),
});
const remaining = parseInt(resp.headers.get("x-ratelimit-requests-remaining") ?? "5000");
metrics.rateLimitRemaining.set({ type: "requests" }, remaining);
checks.rate_limit = {
status: remaining > 100 ? "healthy" : "degraded",
latencyMs: remaining,
};
} catch {
checks.rate_limit = { status: "unknown" };
}
const overall = Object.values(checks).some(c => c.status === "unhealthy")
? "unhealthy"
: Object.values(checks).some(c => c.status === "degraded")
? "degraded"
: "healthy";
return { status: overall, checks, timestamp: new Date().toISOString() };
}
app.get("/health/linear", async (req, res) => {
const health = await checkLinearHealth(client);
res.status(health.status === "unhealthy" ? 503 : 200).json(health);
});
Step 5: Alerting Rules (Prometheus)
# prometheus/linear-alerts.yml
groups:
- name: linear
rules:
- alert: LinearHighErrorRate
expr: |
rate(linear_api_requests_total{status="error"}[5m])
/ rate(linear_api_requests_total[5m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Linear API error rate > 5%"
- alert: LinearRateLimitLow
expr: linear_rate_limit_remaining{type="requests"} < 100
for: 2m
labels:
severity: critical
annotations:
summary: "Linear rate limit remaining < 100 requests"
- alert: LinearHighLatency
expr: |
histogram_quantile(0.95, rate(linear_api_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "Linear API p95 latency > 2 seconds"
- alert: LinearWebhookProcessingSlow
expr: |
histogram_quantile(0.95, rate(linear_webhook_processing_seconds_bucket[5m])) > 5
for: 5m
labels:
severity: warning
annotations:
summary: "Webhook processing p95 > 5 seconds"
Step 6: Webhook Instrumentation
// Instrument webhook handler
app.post("/webhooks/linear", express.raw({ type: "*/*" }), async (req, res) => {
const start = Date.now();
// ... signature verification ...
const event = JSON.parse(req.body.toString());
const delivery = req.headers["linear-delivery"] as string;
metrics.webhooksReceived.inc({ type: event.type, action: event.action });
logWebhook(event.type, event.action, delivery);
res.json({ ok: true });
try {
await processEvent(event);
metrics.webhookProcessingDuration.observe(
{ type: event.type },
(Date.now() - start) / 1000
);
} catch (error: any) {
logError("webhook_processing", error);
}
});
Error Handling
| Error | Cause | Solution |
|---|---|---|
| Metrics not collecting | Missing instrumentation | Wrap all client calls with instrumentedCall() |
| Alerts not firing | Thresholds too high | Adjust based on actual traffic patterns |
| Health check timeout | Linear API slow | Add 10s timeout to health check |
| Log volume too high | Debug level in production | Set LOG_LEVEL=info in prod |
Examples
Quick Health Check
curl -s http://localhost:3000/health/linear | jq .
# { "status": "healthy", "checks": { "linear_api": { "status": "healthy", "latencyMs": 150 } } }
Resources
More by jeremylongshore
View all skills by jeremylongshore →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversAlibaba Cloud Observability offers cloud based network monitoring and cloud monitoring solutions for application perform
Integrate Google Cloud with direct access to resources. Securely sign in to Google Drive and more for seamless cloud man
Manage and monitor Apache Airflow clusters with full workflow, DAG, and task control, plus analytics and XCom access via
Agent Collaboration MCP Server — orchestrate AI agents via tmux sessions for parallel task delegation, real-time monitor
Coroot offers a robust data observability platform with Prometheus process monitoring, software network monitoring, and
Track coding accuracy with SlopWatch, an automated reconciliation software for verifying code changes and managing accou
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.