Building a Legal Monitoring Agent

Guides  ·  AI Agents

A legal monitoring agent combines Claude's ability to reason over unstructured legal text with DocketLayer's real-time docket data. The agent checks a list of cases for new activity, interprets what was filed, and surfaces the information that matters — deadlines, motions, adverse filings — without requiring manual review of every update.

What This Builds

The core pattern is simple: give an AI model access to docketlayer_case_query or GET /v2/case, a list of cases to monitor, and a prompt that defines what to look for. The model queries each case, reads the docket activity, and produces a structured summary. Run it on a schedule and you have a monitoring agent.

This pattern works for tracking active litigation, monitoring counterparties for new cases, reviewing discovery activity, or flagging approaching deadlines across a portfolio. The model handles the interpretation; DocketLayer handles the data retrieval.

Using the MCP Server

If you're running the agent interactively in Claude Desktop, configure the MCP server and prompt Claude directly. Set DOCKETLAYER_DEFAULT_CONTEXT to full to get complete docket text in the response — useful when you want Claude to reason over the substance of filings, not just headers.

{
  "mcpServers": {
    "docketlayer": {
      "command": "npx",
      "args": ["-y", "@docketlayer/mcp-server"],
      "env": {
        "DOCKETLAYER_WALLET_PRIVATE_KEY": "your_base58_private_key",
        "DOCKETLAYER_DEFAULT_CONTEXT": "full"
      }
    }
  }
}

With the MCP server configured, ask Claude to check your cases directly:

You are a legal monitoring assistant. Check each of the following cases for
new docket activity since the date shown. For any case that has new filings,
summarize what was filed, who filed it, and any deadlines or hearings set.

Cases to check:
- Court: nysd, Case: 1:24-cv-09822, Last checked: 2025-02-01T00:00:00-05:00
- Court: cacd, Case: 2:23-cv-04501, Last checked: 2025-02-01T08:00:00-08:00
- Court: ilnd, Case: 1:24-cv-01123, Last checked: 2025-02-01T00:00:00-06:00

Use docketlayer_case_query with last_checked set to the date shown for each case.
If a case shows no new activity, note that briefly and move on.

Claude will call docketlayer_case_query for each case, read the results, and produce a summary. Payment is handled automatically by the MCP server. For a three-case check like the example above, total cost is $2.97.

Using the REST API

For automated monitoring — scheduled runs, background jobs, or agents that don't live in a chat interface — use the Anthropic API directly with tool use. The call_docketlayer function uses the standard x402 payment flow. Define the query tool yourself and implement the agentic loop.

import anthropic, httpx, json
from datetime import datetime, timezone

client = anthropic.Anthropic()

# Tools definition for the Anthropic API
tools = [
    {
        "name": "query_case",
        "description": "Query a court case for current context and recent docket activity.",
        "input_schema": {
            "type": "object",
            "properties": {
                "court_code": {"type": "string"},
                "case_id":    {"type": "string"},
                "last_checked": {"type": "string", "description": "ISO-8601 timestamp"},
            },
            "required": ["court_code", "case_id", "last_checked"],
        },
    },
]

def handle_tool_call(tool_name: str, tool_input: dict) -> str:
    """Execute a tool call and return the result as a string."""
    if tool_name == "query_case":
        result = call_docketlayer(
            tool_input["court_code"],
            tool_input["case_id"],
            tool_input.get("last_checked"),
        )
        return json.dumps(result)
    raise ValueError(f"Unknown tool: {tool_name}")

def call_docketlayer(court_code: str, case_id: str, last_checked: str = None) -> dict:
    """Call GET /v2/case with x402 payment."""
    # build_payment_tx from "How x402 Works" guide
    params = {"court_code": court_code, "case_id": case_id}
    if last_checked:
        params["last_checked"] = last_checked

    probe = httpx.get("https://api.docketlayer.ai/v2/case", params=params)
    requirements = json.loads(probe.headers["X-Payment-Requirements"])
    encoded = build_payment_tx(solana_client, payer, requirements["payTo"],
                               int(requirements["maxAmountRequired"]) / 1e6)
    resp = httpx.get("https://api.docketlayer.ai/v2/case", params=params,
                     headers={"x402-payment": encoded})
    resp.raise_for_status()
    return resp.json()

def run_monitoring_agent(cases: list[dict], system_prompt: str) -> str:
    """Run an agentic loop that monitors the given cases."""
    user_message = "Check the following cases for new activity: " + json.dumps(cases)

    messages = [{"role": "user", "content": user_message}]

    while True:
        response = client.messages.create(
            model="claude-opus-4-6",
            max_tokens=4096,
            system=system_prompt,
            tools=tools,
            messages=messages,
        )

        if response.stop_reason == "end_turn":
            # Extract the final text response
            return next(b.text for b in response.content if b.type == "text")

        # Process tool calls
        messages.append({"role": "assistant", "content": response.content})
        tool_results = []

        for block in response.content:
            if block.type == "tool_use":
                result = handle_tool_call(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })

        messages.append({"role": "user", "content": tool_results})

The agentic loop runs until stop_reason is end_turn, meaning the model has finished its reasoning and produced a final text response. Tool calls are handled in the loop — the model decides which cases to query and in what order.

The model drives the query order

When you give the model a list of cases and let it decide when to call each tool, it may query them in a different order than you specified, or call a case more than once if the first result prompts a follow-up. This is normal agentic behavior. If you need deterministic query ordering, call the API directly in your own loop rather than delegating to the model.

Incremental Queries

Pass last_checked with each query — see Efficient Polling for the full pattern — to limit results to activity since your last check. This keeps the model's context focused on what's new and prevents re-summarizing the same filings on every run.

Store the last_checked timestamp per case after each successful monitoring run. On the next run, pass the stored timestamp and the model will only see the delta. For most cases in a large portfolio on any given day, the delta will be empty — the model notes this and moves on without burning context on stale data.