← All blueprints

On-Call Digest LangGraph

blueprint · 4 agents · 4 MCP servers
771★ stars
98forks
4.5rating

What it does

A scheduled workflow. One agent lists the open Sentry issues, another lists the firing Grafana alerts, in parallel. A summarizer reads yesterday's saved snapshot from disk and reports only what is new or getting worse, so a quiet night produces a short digest. A writer saves the new snapshot and today's digest to files and posts a one-line pointer to Slack. Run it from cron or your scheduler of choice.

The cast · 4 agents

Writerwriterfast

Save today's snapshot and the digest to files. Post a single line to the on-call channel linking the digest. Keep the Slack message to one sentence.

fs.write_filechat.post_message
Summarizersummarizerreasoning

Read yesterday's snapshot file if it exists. Compare it to today's issues and alerts. Report only what is new, what got worse (more events, higher severity), and what resolved. If nothing changed, say so in one line.

fs.read_file
AlertsalertsAgentfast

List the currently firing Grafana alerts with their severity and how long each has been firing. Return them as a list. No commentary.

metrics.list_alerts
IssuesissuesAgentfast

List the open Sentry issues in the window with their event counts. Return them as a list. No commentary.

errors.list_issues

Flow

⇉ parallel
Issuesissues
Alertsalerts
Summarizerdelta
Writerdigest

Interface

Inputs
windowstring
The look-back window, e.g. '24h'. Defaults to since the last run.
Outputs
digeststring
The digest of what is new or worse, also saved to disk and linked in Slack.

Depends on · 4 MCP servers

Sentryio.github.getsentry/sentry-mcperrorsBVerified
io.github.grafana/grafana-mcpnot in the registry yetmetrics
io.github.modelcontextprotocol/filesystemnot in the registry yetfs
io.github.slack/slack-mcpnot in the registry yetchat
⤓ Export runnable code
# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/oncall-digest v1.0.0   schema: socketcat.dev/blueprint/v0
# This code is yours. Edit it freely. The socketcat_runtime helper is optional and can be vendored.

from typing import TypedDict
from langgraph.graph import StateGraph, START, END
import socketcat_runtime as _rt

class State(TypedDict, total=False):
    window: object
    digest: object
    issues: object
    alerts: object
    delta: object

def node_Fork_0(state):
    return {}

def node_Join_1(state):
    return {}

def node_AgentInvoke_2(state):
    return _rt.run_agent("issuesAgent", state, {"window":"window"}, "issues")

def node_AgentInvoke_3(state):
    return _rt.run_agent("alertsAgent", state, {"window":"window"}, "alerts")

def node_AgentInvoke_4(state):
    return _rt.run_agent("summarizer", state, {"alerts":"alerts","issues":"issues"}, "delta")

def node_AgentInvoke_5(state):
    return _rt.run_agent("writer", state, {"delta":"delta"}, "digest")

def build():
    b = StateGraph(State)
    b.add_node("Fork_0", node_Fork_0)
    b.add_node("Join_1", node_Join_1)
    b.add_node("AgentInvoke_2", node_AgentInvoke_2)
    b.add_node("AgentInvoke_3", node_AgentInvoke_3)
    b.add_node("AgentInvoke_4", node_AgentInvoke_4)
    b.add_node("AgentInvoke_5", node_AgentInvoke_5)
    b.add_edge(START, "Fork_0")
    b.add_edge("Fork_0", "AgentInvoke_2")
    b.add_edge("Fork_0", "AgentInvoke_3")
    b.add_edge("Join_1", "AgentInvoke_4")
    b.add_edge("AgentInvoke_2", "Join_1")
    b.add_edge("AgentInvoke_3", "Join_1")
    b.add_edge("AgentInvoke_4", "AgentInvoke_5")
    b.add_edge("AgentInvoke_5", END)
    return b.compile()

INPUTS = ["window"]
OUTPUTS = ["digest"]

if __name__ == "__main__":
    _rt.main(build, INPUTS, OUTPUTS)
blueprint.json (the portable format)
{
  "id": "com.socketcat/oncall-digest",
  "flow": {
    "type": "sequence",
    "blocks": [
      {
        "type": "parallel",
        "blocks": [
          {
            "in": {
              "window": "window"
            },
            "out": "issues",
            "use": "issuesAgent",
            "type": "agent"
          },
          {
            "in": {
              "window": "window"
            },
            "out": "alerts",
            "use": "alertsAgent",
            "type": "agent"
          }
        ]
      },
      {
        "in": {
          "alerts": "alerts",
          "issues": "issues"
        },
        "out": "delta",
        "use": "summarizer",
        "type": "agent"
      },
      {
        "in": {
          "delta": "delta"
        },
        "out": "digest",
        "use": "writer",
        "type": "agent"
      }
    ]
  },
  "tags": [
    "observability",
    "scheduled",
    "digest",
    "on-call"
  ],
  "title": "On-Call Digest",
  "agents": {
    "writer": {
      "model": {
        "hint": "fast"
      },
      "title": "Writer",
      "tools": [
        "fs.write_file",
        "chat.post_message"
      ],
      "instructions": "Save today's snapshot and the digest to files. Post a single line to the on-call channel linking the digest. Keep the Slack message to one sentence."
    },
    "summarizer": {
      "model": {
        "hint": "reasoning"
      },
      "title": "Summarizer",
      "tools": [
        "fs.read_file"
      ],
      "instructions": "Read yesterday's snapshot file if it exists. Compare it to today's issues and alerts. Report only what is new, what got worse (more events, higher severity), and what resolved. If nothing changed, say so in one line."
    },
    "alertsAgent": {
      "model": {
        "hint": "fast"
      },
      "title": "Alerts",
      "tools": [
        "metrics.list_alerts"
      ],
      "instructions": "List the currently firing Grafana alerts with their severity and how long each has been firing. Return them as a list. No commentary."
    },
    "issuesAgent": {
      "model": {
        "hint": "fast"
      },
      "title": "Issues",
      "tools": [
        "errors.list_issues"
      ],
      "instructions": "List the open Sentry issues in the window with their event counts. Return them as a list. No commentary."
    }
  },
  "estate": {
    "delta": {
      "type": "object",
      "description": "The change since the last saved snapshot: new, worse, resolved."
    },
    "alerts": {
      "type": "array",
      "description": "Firing Grafana alerts in the window."
    },
    "issues": {
      "type": "array",
      "description": "Open Sentry issues in the window."
    }
  },
  "$schema": "socketcat.dev/blueprint/v0",
  "authors": [
    {
      "url": "https://socketcat.com",
      "name": "SocketCat"
    }
  ],
  "license": "MIT",
  "servers": [
    {
      "ref": "io.github.getsentry/sentry-mcp",
      "alias": "errors"
    },
    {
      "ref": "io.github.grafana/grafana-mcp",
      "alias": "metrics"
    },
    {
      "ref": "io.github.modelcontextprotocol/filesystem",
      "alias": "fs"
    },
    {
      "ref": "io.github.slack/slack-mcp",
      "alias": "chat"
    }
  ],
  "summary": "A scheduled digest of open errors and firing alerts that reports only what changed since yesterday.",
  "targets": [
    "langgraph",
    "*"
  ],
  "version": "1.0.0",
  "interface": {
    "inputs": {
      "window": {
        "type": "string",
        "description": "The look-back window, e.g. '24h'. Defaults to since the last run."
      }
    },
    "outputs": {
      "digest": {
        "type": "string",
        "description": "The digest of what is new or worse, also saved to disk and linked in Slack."
      }
    }
  },
  "extensions": {
    "dev.langgraph": {
      "checkpointer": "memory"
    }
  },
  "description": "A scheduled workflow. One agent lists the open Sentry issues, another lists the firing Grafana alerts, in parallel. A summarizer reads yesterday's saved snapshot from disk and reports only what is new or getting worse, so a quiet night produces a short digest. A writer saves the new snapshot and today's digest to files and posts a one-line pointer to Slack. Run it from cron or your scheduler of choice."
}