{"slug": "how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai", "title": "How to Combine Claude’s Function Calling with SNS FIFO for Reliable, Ordered AI Notifications", "summary": "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.", "body_md": "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.\n\nWhen 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.\n\n**FIFO** stands for *First‑In‑First‑Out*. An SNS FIFO topic guarantees that messages sharing the same `MessageGroupId`\n\nare 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.\n\nIn plain English:SNS FIFO is like a single‑lane road with a traffic light that lets cars (messages) pass one after another, never overtaking.\n\n| Term | Meaning |\n|---|---|\nFunction calling |\nA feature where the LLM can invoke a pre‑defined tool (a piece of code) instead of just returning text. |\nFIFO topic |\nAn SNS topic that preserves the order of messages that belong to the same logical group. |\nMessageGroupId |\nAn identifier that tells SNS which messages belong together for ordering. |\nMessageDeduplicationId |\nA token that prevents the same message from being delivered twice within a 5‑minute window. |\nLambda |\nA serverless compute service that runs code in response to events (like an SNS message). |\n\nBecause 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.\n\nBefore 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.\n\nBelow is a minimal TypeScript snippet that creates a tool called `publishAlert`\n\n. The function body uses the AWS SDK v3 (`@aws-sdk/client-sns`\n\n) to push a message onto the FIFO topic. Notice the use of the `satisfies`\n\nkeyword – it tells TypeScript “this object matches the shape I described, but don’t widen the type”.\n\n``` js\n// src/claudeTool.ts\nimport { SNSClient, PublishCommand } from \"@aws-sdk/client-sns\";\n\n// ---------------------------------------------------------------------\n// 1️⃣  Prepare the SNS client – it will read credentials from the\n//    environment (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, etc.).\n// ---------------------------------------------------------------------\nconst snsClient = new SNSClient({ region: \"us-east-1\" });\n\n// ---------------------------------------------------------------------\n// 2️⃣  Define the shape of the arguments Claude is allowed to send.\n//    This is the contract between the LLM and our code.\n// ---------------------------------------------------------------------\ntype PublishAlertArgs = {\n  /** Human‑readable title of the alert */\n  title: string;\n  /** Optional JSON payload that downstream systems care about */\n  payload: Record<string, unknown>;\n  /** Group ID to keep ordering – e.g., a device ID or tenant ID */\n  groupId: string;\n};\n\n// ---------------------------------------------------------------------\n// 3️⃣  The tool schema Claude will load.  The `satisfies` keyword forces\n//    the object to be exactly the type we described above.\n// ---------------------------------------------------------------------\nexport const publishAlertTool = {\n  name: \"publishAlert\",\n  description: \"Publish an ordered alert to an SNS FIFO topic\",\n  input_schema: {\n    type: \"object\",\n    properties: {\n      title: { type: \"string\" },\n      payload: { type: \"object\" },\n      groupId: { type: \"string\" },\n    },\n    required: [\"title\", \"groupId\"],\n    additionalProperties: false,\n  },\n} satisfies { name: string; description: string; input_schema: object };\n\n// ---------------------------------------------------------------------\n// 4️⃣  The implementation that Claude will invoke.  It builds the SNS\n//    PublishCommand with the required FIFO fields.\n// ---------------------------------------------------------------------\nexport async function publishAlert(args: PublishAlertArgs): Promise<void> {\n  const { title, payload, groupId } = args;\n\n  // A stable deduplication ID – you could hash the payload, add a timestamp,\n  // or use a UUID if you need absolute uniqueness.\n  const dedupId = `${groupId}-${Date.now()}`;\n\n  const command = new PublishCommand({\n    // The ARN of the FIFO topic you created (ends with .fifo)\n    TopicArn: process.env.ALERTS_FIFO_TOPIC_ARN,\n    // Message body – keep it short; you can embed a JSON string if needed.\n    Message: JSON.stringify({ title, payload }),\n    // Guarantees ordering for all alerts that share this groupId.\n    MessageGroupId: groupId,\n    // Prevents the same alert from being sent twice within 5 minutes.\n    MessageDeduplicationId: dedupId,\n  });\n\n  // Send the command; any error will bubble up to Claude as a tool failure.\n  await snsClient.send(command);\n}\n```\n\nTip:Keep the`MessageDeduplicationId`\n\ndeterministic (e.g., a hash of the payload) if you ever needexactly‑oncesemantics across retries.\n\nThe LLM will call `publishAlert`\n\nwhenever it decides an alert should be raised. Your application simply needs to expose the `publishAlertTool`\n\ndescription to Claude and bind the `publishAlert`\n\nimplementation to the tool handler.\n\nCreating a FIFO topic is a one‑time operation, but there are a few hidden rules that bite many engineers:\n\nBelow 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`\n\n) and demonstrates the gotchas.\n\n``` js\n// scripts/createFifoTopic.ts\nimport {\n  SNSClient,\n  CreateTopicCommand,\n  SubscribeCommand,\n  SetTopicAttributesCommand,\n} from \"@aws-sdk/client-sns\";\n\n// ---------------------------------------------------------------------\n// 1️⃣  Initialize the client (same region as your Lambda)\n// ---------------------------------------------------------------------\nconst sns = new SNSClient({ region: \"us-east-1\" });\n\nasync function main() {\n  // -----------------------------------------------------------------\n  // 2️⃣  Create the FIFO topic.  The name MUST end with \".fifo\".\n  // -----------------------------------------------------------------\n  const createResp = await sns.send(\n    new CreateTopicCommand({\n      Name: \"ai-alerts.fifo\",\n      Attributes: {\n        // FIFO topics need these two flags.\n        FifoTopic: \"true\",\n        // Optional: set a default message group to avoid errors if you forget.\n        // We'll enforce explicit group IDs later.\n        ContentBasedDeduplication: \"false\",\n      },\n    })\n  );\n\n  const topicArn = createResp.TopicArn!;\n  console.log(\"✅ FIFO topic created:\", topicArn);\n\n  // -----------------------------------------------------------------\n  // 3️⃣  Attach a Lambda subscriber (replace with your function ARN).\n  // -----------------------------------------------------------------\n  const lambdaArn = process.env.ALERTS_LAMBDA_ARN!;\n  await sns.send(\n    new SubscribeCommand({\n      Protocol: \"lambda\",\n      TopicArn: topicArn,\n      Endpoint: lambdaArn,\n    })\n  );\n  console.log(\"✅ Lambda subscribed:\", lambdaArn);\n\n  // -----------------------------------------------------------------\n  // 4️⃣  (Optional) Add a dead‑letter queue (DLQ) via a subscription\n  //     attribute – note that SNS FIFO does NOT create a DLQ automatically.\n  // -----------------------------------------------------------------\n  await sns.send(\n    new SetTopicAttributesCommand({\n      TopicArn: topicArn,\n      AttributeName: \"RedrivePolicy\",\n      AttributeValue: JSON.stringify({\n        deadLetterTargetArn: process.env.ALERTS_DLQ_ARN,\n      }),\n    })\n  );\n  console.log(\"✅ DLQ attached (if provided).\");\n}\n\nmain().catch((err) => {\n  console.error(\"❌ Error creating topic:\", err);\n  process.exit(1);\n});\n```\n\nKey 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.\n\n**Deduplication window** – SNS remembers each `MessageDeduplicationId`\n\nfor **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`\n\nand let SNS hash the `Message`\n\nbody.\n\n**Ordering across groups** – SNS only guarantees order **inside a single MessageGroupId**. If you publish alerts for two different devices (\n\n`groupId = \"deviceA\"`\n\nand `\"deviceB\"`\n\n), 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:\n\n`require(esm)`\n\nin Node 22 can break existing Lambda layers silently – always use native ESM (`import …`\n\n) or stay with CommonJS.\nBelow 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.\n\n``` js\n// src/alertProcessor.ts\nimport { SQSEvent, SNSEvent, Context } from \"aws-lambda\";\n\n/**\n * Lambda entry point – SNS will invoke this function for each batch\n * of messages that share the same MessageGroupId.\n */\nexport async function handler(event: SNSEvent, _ctx: Context): Promise<void> {\n  // SNS may deliver multiple records in one invocation.\n  for (const record of event.Records) {\n    // -----------------------------------------------------------------\n    // 1️⃣  The raw message body is a string; we expect JSON.\n    // -----------------------------------------------------------------\n    const raw = record.Sns.Message;\n    let parsed: { title: string; payload?: Record<string, unknown> };\n\n    try {\n      parsed = JSON.parse(raw);\n    } catch (e) {\n      // If parsing fails, we *must* let the error bubble up so SNS retries.\n      console.error(\"❌ Failed to parse SNS message:\", raw);\n      throw e;\n    }\n\n    // -----------------------------------------------------------------\n    // 2️⃣  Log the alert – in a real system you would forward it to a DB\n    //     or another service.\n    // -----------------------------------------------------------------\n    console.log(\n      `🔔 Alert [${record.Sns.MessageGroupId}]: ${parsed.title}`,\n      parsed.payload ?? {}\n    );\n  }\n\n  // Returning without error tells SNS the batch was processed.\n}\n```\n\nTo wire this function to the SNS topic, you can use the AWS Console or the CDK/CloudFormation. The critical configuration bits are:\n\n| Setting | Value | Why it matters |\n|---|---|---|\nRuntime |\n`nodejs22.x` |\nSupports the latest language features and the SDK v3. |\nMemory |\n128 MiB (or higher if payloads are large) | Affects max concurrent invocations; keep low for cost. |\nTimeout |\n30 seconds (default) | Should be enough for simple logging; increase if you do heavy work. |\nDead‑letter queue |\nOptional, but recommended | SNS retries three times; after that the message is lost unless a DLQ captures it. |\n\nTip:Enable CloudWatch Logs for the Lambda and set an alarm on`InvocationErrors`\n\n. Because SNS retries are per‑subscriber, a silent Lambda failure could leave you with undelivered alerts.\n\nA 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.\n\nCreate a tiny script that calls `publishAlert`\n\na few times with the same `groupId`\n\n. Use a short `setTimeout`\n\nbetween calls to mimic rapid LLM output.\n\n``` js\n// scripts/simulateClaude.ts\nimport { publishAlert } from \"../src/claudeTool\";\n\nasync function main() {\n  const groupId = \"device-123\";\n\n  // Fire three alerts in quick succession.\n  await publishAlert({\n    title: \"Temperature high\",\n    payload: { temp: 78 },\n    groupId,\n  });\n  await publishAlert({\n    title: \"Temperature critical\",\n    payload: { temp: 92 },\n    groupId,\n  });\n  await publishAlert({\n    title: \"Shutdown initiated\",\n    payload: { reason: \"overheat\" },\n    groupId,\n  });\n\n  console.log(\"✅ All alerts sent.\");\n}\n\nmain().catch((e) => {\n  console.error(\"❌ Simulation failed:\", e);\n});\n```\n\nRun `ts-node scripts/simulateClaude.ts`\n\n. Then check the Lambda logs – you should see the three alerts appear **in the same order**.\n\nModify the script to reuse the same `MessageDeduplicationId`\n\n(by passing a constant `dedupId`\n\ninto `publishAlert`\n\n). You’ll see only the first message appear in Lambda logs; the others are dropped silently. This demonstrates the 5‑minute window rule.\n\nAdd a line that throws an exception for a particular alert (e.g., when `title`\n\ncontains “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.\n\nIn 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.\n\nRun a query like the following in CloudWatch Logs Insights:\n\n```\nfields @timestamp, @message\n| filter @message like /Alert/\n| sort @timestamp asc\n| limit 20\n```\n\nThe `sort asc`\n\nwill show you the exact arrival order. If you see out‑of‑order entries for the same `groupId`\n\n, double‑check that you used a FIFO topic and that the `MessageGroupId`\n\nis identical across the batch.\n\nWhat you now have:a pattern that turns Claude’s tool calls into areliable, ordered event streamusing only AWS‑managed services.\n\n`MessageGroupId`\n\narrives at the subscriber in the exact sequence it was published.\nWith 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!\n\nTransparency noticeThis article was written with the help of an AI system —\n\n[Groq](GPT OSS 120B).\n\nPublished:2026-08-26 ·Primary focus:SNSAll code blocks are intended to be correct and runnable, but please verify them\n\nagainst the official docs for the tools mentioned before using in production.\n\nFind an error? Drop a comment — corrections are always welcome.", "url": "https://wpnews.pro/news/how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai", "canonical_source": "https://dev.to/dineshgowtham/how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai-notifications-5b6n", "published_at": "2026-08-26 03:34:54+00:00", "updated_at": "2026-08-26 04:14:40.702783+00:00", "lang": "en", "topics": ["large-language-models", "ai-agents", "ai-infrastructure"], "entities": ["Claude", "Amazon SNS", "AWS Lambda", "AWS SDK v3"], "alternates": {"html": "https://wpnews.pro/news/how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai", "markdown": "https://wpnews.pro/news/how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai.md", "text": "https://wpnews.pro/news/how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai.txt", "jsonld": "https://wpnews.pro/news/how-to-combine-claudes-function-calling-with-sns-fifo-for-reliable-ordered-ai.jsonld"}}