cd /news/large-language-models/how-to-combine-claudes-function-call… · home topics large-language-models article
[ARTICLE · art-111224] src=dev.to ↗ pub= topic=large-language-models verified=true sentiment=· neutral

How to Combine Claude’s Function Calling with SNS FIFO for Reliable, Ordered AI Notifications

A developer demonstrates how to combine Claude's function calling with Amazon SNS FIFO topics to create reliable, ordered AI notifications. The approach ensures that alerts generated by the LLM are processed in the exact order they were emitted, with deduplication and zero-loss guarantees for downstream Lambda consumers.

read10 min views1 publishedAug 26, 2026

LLMs can now call tools, but turning their output into a trustworthy event stream is still a puzzle. We wire Claude’s function‑calling to an SNS FIFO topic, giving you ordered, deduplicated notifications that downstream Lambda functions can consume with zero‑loss guarantees.

When an LLM decides to “publishAlert”, you usually want the alert to be processed exactly in the order it was generated. Imagine a fire‑alarm system that first warns about a smoke detector, then follows up with a sprinkler‑activation command. If those two messages arrive swapped, you could end up turning on sprinklers before the fire is even confirmed.

FIFO stands for First‑In‑First‑Out. An SNS FIFO topic guarantees that messages sharing the same MessageGroupId

are delivered to subscribers in the exact order they were published. This is different from the default “standard” SNS topics, which deliver messages quickly but without ordering guarantees.

In plain English:SNS FIFO is like a single‑lane road with a traffic light that lets cars (messages) pass one after another, never overtaking.

Term Meaning
Function calling
A feature where the LLM can invoke a pre‑defined tool (a piece of code) instead of just returning text.
FIFO topic
An SNS topic that preserves the order of messages that belong to the same logical group.
MessageGroupId
An identifier that tells SNS which messages belong together for ordering.
MessageDeduplicationId
A token that prevents the same message from being delivered twice within a 5‑minute window.
Lambda
A serverless compute service that runs code in response to events (like an SNS message).

Because the LLM can generate many alerts rapidly, using a FIFO topic means you can treat the AI as a deterministic producer rather than a chaotic chatterbox. The downstream Lambda sees the alerts in the same sequence the model emitted them.

Before you can send anything to SNS, Claude (the LLM) needs to know about the tool you’re exposing. In Claude’s terminology a tool schema describes the name, description, and the JSON shape of the arguments it can pass.

Below is a minimal TypeScript snippet that creates a tool called publishAlert

. The function body uses the AWS SDK v3 (@aws-sdk/client-sns

) to push a message onto the FIFO topic. Notice the use of the satisfies

keyword – it tells TypeScript “this object matches the shape I described, but don’t widen the type”.

// src/claudeTool.ts
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns";

// ---------------------------------------------------------------------
// 1️⃣  Prepare the SNS client – it will read credentials from the
//    environment (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, etc.).
// ---------------------------------------------------------------------
const snsClient = new SNSClient({ region: "us-east-1" });

// ---------------------------------------------------------------------
// 2️⃣  Define the shape of the arguments Claude is allowed to send.
//    This is the contract between the LLM and our code.
// ---------------------------------------------------------------------
type PublishAlertArgs = {
  /** Human‑readable title of the alert */
  title: string;
  /** Optional JSON payload that downstream systems care about */
  payload: Record<string, unknown>;
  /** Group ID to keep ordering – e.g., a device ID or tenant ID */
  groupId: string;
};

// ---------------------------------------------------------------------
// 3️⃣  The tool schema Claude will load.  The `satisfies` keyword forces
//    the object to be exactly the type we described above.
// ---------------------------------------------------------------------
export const publishAlertTool = {
  name: "publishAlert",
  description: "Publish an ordered alert to an SNS FIFO topic",
  input_schema: {
    type: "object",
    properties: {
      title: { type: "string" },
      payload: { type: "object" },
      groupId: { type: "string" },
    },
    required: ["title", "groupId"],
    additionalProperties: false,
  },
} satisfies { name: string; description: string; input_schema: object };

// ---------------------------------------------------------------------
// 4️⃣  The implementation that Claude will invoke.  It builds the SNS
//    PublishCommand with the required FIFO fields.
// ---------------------------------------------------------------------
export async function publishAlert(args: PublishAlertArgs): Promise<void> {
  const { title, payload, groupId } = args;

  // A stable deduplication ID – you could hash the payload, add a timestamp,
  // or use a UUID if you need absolute uniqueness.
  const dedupId = `${groupId}-${Date.now()}`;

  const command = new PublishCommand({
    // The ARN of the FIFO topic you created (ends with .fifo)
    TopicArn: process.env.ALERTS_FIFO_TOPIC_ARN,
    // Message body – keep it short; you can embed a JSON string if needed.
    Message: JSON.stringify({ title, payload }),
    // Guarantees ordering for all alerts that share this groupId.
    MessageGroupId: groupId,
    // Prevents the same alert from being sent twice within 5 minutes.
    MessageDeduplicationId: dedupId,
  });

  // Send the command; any error will bubble up to Claude as a tool failure.
  await snsClient.send(command);
}

Tip:Keep theMessageDeduplicationId

deterministic (e.g., a hash of the payload) if you ever needexactly‑oncesemantics across retries.

The LLM will call publishAlert

whenever it decides an alert should be raised. Your application simply needs to expose the publishAlertTool

description to Claude and bind the publishAlert

implementation to the tool handler.

Creating a FIFO topic is a one‑time operation, but there are a few hidden rules that bite many engineers:

Below is a small script that creates a FIFO topic, sets the required attributes, and adds a Lambda subscription. The code uses the same SDK (@aws-sdk/client-sns

) and demonstrates the gotchas.

// scripts/createFifoTopic.ts
import {
  SNSClient,
  CreateTopicCommand,
  SubscribeCommand,
  SetTopicAttributesCommand,
} from "@aws-sdk/client-sns";

// ---------------------------------------------------------------------
// 1️⃣  Initialize the client (same region as your Lambda)
// ---------------------------------------------------------------------
const sns = new SNSClient({ region: "us-east-1" });

async function main() {
  // -----------------------------------------------------------------
  // 2️⃣  Create the FIFO topic.  The name MUST end with ".fifo".
  // -----------------------------------------------------------------
  const createResp = await sns.send(
    new CreateTopicCommand({
      Name: "ai-alerts.fifo",
      Attributes: {
        // FIFO topics need these two flags.
        FifoTopic: "true",
        // Optional: set a default message group to avoid errors if you forget.
        // We'll enforce explicit group IDs later.
        ContentBasedDeduplication: "false",
      },
    })
  );

  const topicArn = createResp.TopicArn!;
  console.log("✅ FIFO topic created:", topicArn);

  // -----------------------------------------------------------------
  // 3️⃣  Attach a Lambda subscriber (replace with your function ARN).
  // -----------------------------------------------------------------
  const lambdaArn = process.env.ALERTS_LAMBDA_ARN!;
  await sns.send(
    new SubscribeCommand({
      Protocol: "lambda",
      TopicArn: topicArn,
      Endpoint: lambdaArn,
    })
  );
  console.log("✅ Lambda subscribed:", lambdaArn);

  // -----------------------------------------------------------------
  // 4️⃣  (Optional) Add a dead‑letter queue (DLQ) via a subscription
  //     attribute – note that SNS FIFO does NOT create a DLQ automatically.
  // -----------------------------------------------------------------
  await sns.send(
    new SetTopicAttributesCommand({
      TopicArn: topicArn,
      AttributeName: "RedrivePolicy",
      AttributeValue: JSON.stringify({
        deadLetterTargetArn: process.env.ALERTS_DLQ_ARN,
      }),
    })
  );
  console.log("✅ DLQ attached (if provided).");
}

main().catch((err) => {
  console.error("❌ Error creating topic:", err);
  process.exit(1);
});

Key takeaway:A FIFO topic is only as reliable as its subscribers. Make sure the Lambda you attach is ready to handle retries, and consider wiring a dead‑letter queue manually because SNS does not add one by default.

Deduplication window – SNS remembers each MessageDeduplicationId

for 5 minutes. If you reuse the same ID within that window, the second message disappears without any error. To avoid silent drops, generate a fresh ID for each publish (as shown) or enable ContentBasedDeduplication

and let SNS hash the Message

body.

Ordering across groups – SNS only guarantees order inside a single MessageGroupId. If you publish alerts for two different devices (

groupId = "deviceA"

and "deviceB"

), their relative order is undefined. Design your downstream logic to treat each group independently, or funnel everything through a single group if true global order is required (at the cost of throughput). Now that alerts are flowing into SNS, we need a Lambda that respects the ordering and logs the payload. The Lambda runtime we’ll target is Node.js 22, the latest LTS version. Be aware of two Lambda‑specific gotchas:

require(esm)

in Node 22 can break existing Lambda layers silently – always use native ESM (import …

) or stay with CommonJS. Below is a straightforward handler that extracts the SNS message, parses the JSON payload, and logs the alert. It also explicitly acknowledges the message by returning successfully; any uncaught error will cause SNS to retry the delivery.

// src/alertProcessor.ts
import { SQSEvent, SNSEvent, Context } from "aws-lambda";

/**
 * Lambda entry point – SNS will invoke this function for each batch
 * of messages that share the same MessageGroupId.
 */
export async function handler(event: SNSEvent, _ctx: Context): Promise<void> {
  // SNS may deliver multiple records in one invocation.
  for (const record of event.Records) {
    // -----------------------------------------------------------------
    // 1️⃣  The raw message body is a string; we expect JSON.
    // -----------------------------------------------------------------
    const raw = record.Sns.Message;
    let parsed: { title: string; payload?: Record<string, unknown> };

    try {
      parsed = JSON.parse(raw);
    } catch (e) {
      // If parsing fails, we *must* let the error bubble up so SNS retries.
      console.error("❌ Failed to parse SNS message:", raw);
      throw e;
    }

    // -----------------------------------------------------------------
    // 2️⃣  Log the alert – in a real system you would forward it to a DB
    //     or another service.
    // -----------------------------------------------------------------
    console.log(
      `🔔 Alert [${record.Sns.MessageGroupId}]: ${parsed.title}`,
      parsed.payload ?? {}
    );
  }

  // Returning without error tells SNS the batch was processed.
}

To wire this function to the SNS topic, you can use the AWS Console or the CDK/CloudFormation. The critical configuration bits are:

Setting Value Why it matters
Runtime
nodejs22.x
Supports the latest language features and the SDK v3.
Memory
128 MiB (or higher if payloads are large) Affects max concurrent invocations; keep low for cost.
Timeout
30 seconds (default) Should be enough for simple logging; increase if you do heavy work.
Dead‑letter queue
Optional, but recommended SNS retries three times; after that the message is lost unless a DLQ captures it.

Tip:Enable CloudWatch Logs for the Lambda and set an alarm onInvocationErrors

. Because SNS retries are per‑subscriber, a silent Lambda failure could leave you with undelivered alerts.

A reliable system is only as good as the tests you run against it. The following steps let you validate ordering, deduplication, and error handling without deploying to production.

Create a tiny script that calls publishAlert

a few times with the same groupId

. Use a short setTimeout

between calls to mimic rapid LLM output.

// scripts/simulateClaude.ts
import { publishAlert } from "../src/claudeTool";

async function main() {
  const groupId = "device-123";

  // Fire three alerts in quick succession.
  await publishAlert({
    title: "Temperature high",
    payload: { temp: 78 },
    groupId,
  });
  await publishAlert({
    title: "Temperature critical",
    payload: { temp: 92 },
    groupId,
  });
  await publishAlert({
    title: "Shutdown initiated",
    payload: { reason: "overheat" },
    groupId,
  });

  console.log("✅ All alerts sent.");
}

main().catch((e) => {
  console.error("❌ Simulation failed:", e);
});

Run ts-node scripts/simulateClaude.ts

. Then check the Lambda logs – you should see the three alerts appear in the same order.

Modify the script to reuse the same MessageDeduplicationId

(by passing a constant dedupId

into publishAlert

). You’ll see only the first message appear in Lambda logs; the others are dropped silently. This demonstrates the 5‑minute window rule.

Add a line that throws an exception for a particular alert (e.g., when title

contains “critical”). Deploy the Lambda, run the simulation again, and watch CloudWatch. You’ll see the failed invocation retried three times, then disappear unless you have a DLQ attached.

In plain English:If the Lambda crashes, SNS will try three more times, then give up. Without a dead‑letter queue, that alert is lost forever.

Run a query like the following in CloudWatch Logs Insights:

fields @timestamp, @message
| filter @message like /Alert/
| sort @timestamp asc
| limit 20

The sort asc

will show you the exact arrival order. If you see out‑of‑order entries for the same groupId

, double‑check that you used a FIFO topic and that the MessageGroupId

is identical across the batch.

What you now have:a pattern that turns Claude’s tool calls into areliable, ordered event streamusing only AWS‑managed services.

MessageGroupId

arrives at the subscriber in the exact sequence it was published. With these pieces in place, you can let Claude act as the brain of your system while SNS FIFO and Lambda act as the nervous system that reliably carries the signals in order, without loss. Happy coding!

Transparency noticeThis article was written with the help of an AI system —

[Groq](GPT OSS 120B).

Published:2026-08-26 ·Primary focus:SNSAll code blocks are intended to be correct and runnable, but please verify them

against the official docs for the tools mentioned before using in production.

Find an error? Drop a comment — corrections are always welcome.

── more in #large-language-models 4 stories · sorted by recency
── more on @claude 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-to-combine-claud…] indexed:0 read:10min 2026-08-26 ·