{"id":2972,"date":"2026-02-02T23:12:18","date_gmt":"2026-02-03T04:12:18","guid":{"rendered":"https:\/\/blog2wrkdev.wpenginepowered.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/"},"modified":"2026-10-08T08:34:23","modified_gmt":"2026-10-08T12:34:23","slug":"recovering-from-partial-failures-in-enterprise-mcp-tools","status":"publish","type":"post","link":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/","title":{"rendered":"Recovering from Partial Failures in Enterprise MCP Tools"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Distributed transactions fail partway through. Payment succeeds, then Salesforce times out. The guest is charged, but three systems hold stale state.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In production, this happens constantly: a system times out, a connection drops mid-request, a user submits unexpected input. In distributed systems, these failures often mean a transaction completes in one system and fails in another\u2014requiring reconciliation to restore consistency across all systems.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What does this look like in composed MCP tools? When an LLM orchestrates multi-step workflows\u2014potentially retrying, potentially calling the same tool multiple times\u2014each tool represents a surface area for partial failure. Who enforces state reconciliation? How does that fit with the separation of concerns we&#8217;ve discussed in previous posts?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Idempotency handles retries. Error handling catches failures. Neither addresses partial success\u2014when some operations complete before the failure point. Recovery requires knowing what succeeded and how to reverse it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Previous posts covered\u00a0<a href=\"https:\/\/www.workato.com\/the-connector\/designing-skills-for-enterprise-mcp\/\" type=\"link\" id=\"https:\/\/www.workato.com\/the-connector\/designing-skills-for-enterprise-mcp\/\" target=\"_blank\" rel=\"noreferrer noopener\">composable skill design<\/a>\u00a0and\u00a0<a href=\"https:\/\/www.workato.com\/the-connector\/serverless-mcp-for-enterprise-ai-tools\/\" type=\"link\" id=\"https:\/\/www.workato.com\/the-connector\/serverless-mcp-for-enterprise-ai-tools\/\" target=\"_blank\" rel=\"noreferrer noopener\">serverless execution<\/a>\u2014how to structure tools and run them reliably. This post covers what happens when reliable execution still produces inconsistent state.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Reference Architecture: Dewy Resort<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Throughout this series, we use Dewy Resort\u2014a hotel management system\u2014as our reference implementation. The architecture spans multiple systems:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Salesforce:<\/strong>\u00a0Guest records, bookings, room inventory, sales opportunities<\/li>\n\n\n\n<li><strong>Stripe:<\/strong>\u00a0Payment processing, refunds<\/li>\n\n\n\n<li><strong>MCP Tools:<\/strong>\u00a0Orchestration layer connecting these systems via composed workflows<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A single guest action like &#8220;check out&#8221; triggers operations across both systems: charge payment in Stripe, update booking status in Salesforce, mark room for cleaning, close the sales opportunity. Each system commits on its own timeline. The orchestrator sequences operations but can&#8217;t provide atomic rollback across system boundaries.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/workato-devs\/dewy-resort\" target=\"_blank\">complete implementation<\/a>&nbsp;is open source.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">The Problem: Consistency Without Transactions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Enterprise workflows need ACID-like guarantees\u2014either everything succeeds or everything rolls back. But there&#8217;s no shared transaction boundary spanning systems. Stripe commits. Salesforce commits. Each has its own state, its own failure modes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What This Looks Like: Multi-Object State<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In Dewy Resort, a single checkout action updates three related Salesforce objects:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Booking__c\n  \u251c\u2500&gt; Hotel_Room__c (lookup)\n  \u2514\u2500&gt; Opportunity (lookup)\n\nState dependencies:\n- Booking.Status = \"Checked Out\"\n  \u2192 Room.Status__c = \"Cleaning\"\n  \u2192 Opportunity.StageName = \"Closed Won\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When Booking transitions to &#8220;Checked Out,&#8221; Room and Opportunity must also transition. If any update fails, all three objects may be in inconsistent states\u2014plus the Stripe charge has already succeeded.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/04-multi-object-state.svg\"><img decoding=\"async\" src=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/04-multi-object-state.svg\" alt=\"Infographic showing expected state changes for Salesforce object updates during guest checkout, in Dewy Resort sample app implementation.\" class=\"wp-image-42212\"\/><\/a><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">A Checkout Failure in Practice<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Workflow:<\/strong>&nbsp;<code>process_guest_checkout<\/code><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Step<\/th><th>Operation<\/th><th>Result<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td>Search guest in Salesforce<\/td><td>\u2713<\/td><\/tr><tr><td>2<\/td><td>Create Stripe customer<\/td><td>\u2713<\/td><\/tr><tr><td>3<\/td><td>Create payment intent<\/td><td>\u2713<\/td><\/tr><tr><td>4<\/td><td>Confirm payment<\/td><td>\u2713 ($250 charged)<\/td><\/tr><tr><td>5<\/td><td>Update Salesforce booking<\/td><td>\u2717 Timeout<\/td><\/tr><tr><td>6<\/td><td>Update room status<\/td><td>\u2014 Skipped<\/td><\/tr><tr><td>7<\/td><td>Update opportunity<\/td><td>\u2014 Skipped<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result:<\/strong>&nbsp;HTTP 500 returned to caller.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Actual state across systems:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>System<\/th><th>Actual<\/th><th>Expected<\/th><th>Match<\/th><\/tr><\/thead><tbody><tr><td>Stripe<\/td><td>$250 charged<\/td><td>$250 charged<\/td><td>\u2713<\/td><\/tr><tr><td>Booking<\/td><td>Checked In<\/td><td>Checked Out<\/td><td>\u2717<\/td><\/tr><tr><td>Room<\/td><td>Occupied<\/td><td>Cleaning<\/td><td>\u2717<\/td><\/tr><tr><td>Opportunity<\/td><td>Negotiation<\/td><td>Closed Won<\/td><td>\u2717<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Guest paid, checkout incomplete. Room can&#8217;t be reassigned. Sales report wrong. Manual reconciliation: 30+ minutes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/01-partial-failure-1.svg\"><img decoding=\"async\" src=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/01-partial-failure-1.svg\" alt=\"Infographic describing &quot;partial failure&quot; object states in Dewy Resort during a fictional guest checkout failure. Stripe charged $250, but all downstream Salesforce object updates failed.\" class=\"wp-image-42211\"\/><\/a><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Without a transaction boundary spanning systems, you have to build consistency yourself.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why Try\/Catch Isn&#8217;t Enough<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A catch block logs the error and returns 500. It can&#8217;t distinguish between:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Failed before payment \u2192 safe to retry<\/li>\n\n\n\n<li>Failed after payment \u2192 need refund or idempotent retry<\/li>\n\n\n\n<li>Failed during Salesforce update \u2192 need reconciliation to determine state<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Traditional error handling is binary: success or failure. Distributed workflows have partial success. Recovery requires knowing&nbsp;<em>what succeeded<\/em>&nbsp;and&nbsp;<em>how to reverse it<\/em>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">A Perspective: Decision Placement<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This series has argued for a clear separation of concerns between LLMs and backend systems. That principle applies directly to recovery logic:&nbsp;<em>who<\/em>&nbsp;should decide when to compensate, and&nbsp;<em>who<\/em>&nbsp;should execute the compensation?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This isn&#8217;t established industry practice\u2014it&#8217;s a perspective we&#8217;re advocating based on our experience building MCP tools for enterprise contexts.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Principle<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">These aren&#8217;t fully autonomous systems. They&#8217;re agents assisting humans\u2014so the real question is where human judgment belongs versus where deterministic execution belongs.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Humans + LLMs decide WHEN to act and WHICH workflow.<\/strong><br><strong>Backend workflows decide HOW to act and WHAT state transitions.<\/strong><br><strong>Financial and security decisions always go to backend.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Human + LLM Decisions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Non-deterministic, context-dependent\u2014where judgment matters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Understanding user intent (&#8220;I want to check out&#8221; \u2192 tool selection)<\/li>\n\n\n\n<li>Selecting the right workflow (checkout vs compensation)<\/li>\n\n\n\n<li>Extracting parameters from natural language<\/li>\n\n\n\n<li>Confirming actions with users before execution<\/li>\n\n\n\n<li>Explaining results to users<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Backend-Appropriate Decisions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Deterministic, rule-based\u2014where consistency matters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Payment status routing (succeeded\/requires_action\/failed)<\/li>\n\n\n\n<li>State validation (can this transition happen?)<\/li>\n\n\n\n<li>Business rule enforcement (check-in window, eligibility)<\/li>\n\n\n\n<li>ID resolution (email \u2192 Contact ID)<\/li>\n\n\n\n<li>Error categorization (retryable vs permanent)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Applied to Recovery: Who Decides to Compensate?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We built capacity for both paths:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>LLM-driven:<\/strong> Staff member receives guest complaint (&#8220;I got charged but checkout failed&#8221;). Staff-facing agent verifies the problem, calls compensation, explains outcome.<\/li>\n\n\n\n<li><strong>Automated:<\/strong> Scheduled job queries for orphaned payments, triggers compensation automation based on business rules.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Same compensation tool, different trigger mechanisms. Conversational UX for reported issues; automation catches unreported failures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>On tool exposure:<\/strong>\u00a0The compensation tool exists only on the staff MCP server\u2014there&#8217;s no guest-facing version. Some tools simply don&#8217;t make sense for certain audiences. There&#8217;s no &#8220;refund on behalf of guest&#8221; capability because allowing guests to trigger unmediated refunds isn&#8217;t sound business logic. Tool exposure is itself a layer of authorization, complementing the approach to\u00a0<a href=\"https:\/\/www.workato.com\/the-connector\/designing-skills-for-enterprise-mcp\/\" type=\"link\" id=\"https:\/\/www.workato.com\/the-connector\/designing-skills-for-enterprise-mcp\/\" target=\"_blank\" rel=\"noreferrer noopener\">building authorization into tool design<\/a>\u00a0discussed earlier in this series.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/03-decision-placement-1.svg\"><img decoding=\"async\" src=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/03-decision-placement-1.svg\" alt=\"Infographic showing process flow of &quot;guest checkout&quot;, with decision rationale or control points for Human + LLM (WHEN\/WHICH tool) stages of process vs Backend (HOW\/WHAT gets executed) tactical steps.\" class=\"wp-image-42214\"\/><\/a><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">Why This Matters<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Principle<\/th><th>Implementation<\/th><th>Rationale<\/th><\/tr><\/thead><tbody><tr><td><strong>Strategic vs tactical split<\/strong><\/td><td>LLM selects workflow; backend executes it<\/td><td>Clear separation enables independent testing<\/td><\/tr><tr><td><strong>Financial logic in backend<\/strong><\/td><td>Refund amounts, payment routing, charge validation<\/td><td>Deterministic, auditable, not subject to prompt variation<\/td><\/tr><tr><td><strong>Multiple trigger mechanisms<\/strong><\/td><td>Same tool callable by LLM or cron job<\/td><td>Flexibility without duplicating logic<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Two Patterns for Recovery<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two established patterns address partial failure recovery directly. Both are implemented in Dewy Resort.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pattern 1: Compensating Transactions (Saga Pattern)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The saga pattern treats multi-step workflows as a sequence of operations, each with a corresponding&nbsp;<strong>compensating transaction<\/strong>&nbsp;that reverses its effect.<sup><a href=\"app:\/\/obsidian.md\/index.html#fn-1-79a706d60ffa7568\" target=\"_blank\" rel=\"noreferrer noopener\">[1]<\/a><\/sup><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this pattern when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Multi-system workflows can partially succeed<\/li>\n\n\n\n<li>Financial operations are involved<\/li>\n\n\n\n<li>State consistency affects user experience<\/li>\n\n\n\n<li>Manual reconciliation cost exceeds automation cost<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/02a-saga-pairing.svg\"><img decoding=\"async\" src=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/02a-saga-pairing.svg\" alt=\"Infographic with mirrored state pairs for &quot;reverse transactions&quot; in Dewy Resort implementation saga pattern for checkout compensation orchestrator.\" class=\"wp-image-42215\"\/><\/a><\/figure>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/02b-orchestrator-flow.svg\"><img decoding=\"async\" src=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/02b-orchestrator-flow.svg\" alt=\"Execution flow for Dewy Resort compensation orchestrator. Shows initial actions check for refund availability in Stripe, then check for necessary Salesforce state changes across related objects. Design of state checks across Stripe and Salesforce ensure idempotency across multiple invocations of compensator tool.\" class=\"wp-image-42216\"\/><\/a><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">How It Works<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">When checkout fails after payment succeeds, the compensation tool:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Validates payment state (is it refundable?)<\/li>\n\n\n\n<li>Issues refund (financial operations first)<\/li>\n\n\n\n<li>Checks Salesforce state and reverts if needed<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The tool accepts what callers naturally have:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"tool\": \"compensate_checkout_failure\",\n  \"parameters\": {\n    \"payment_intent_id\": \"pi_3ABC...\",\n    \"guest_email\": \"beth.gibbs@email.com\",\n    \"idempotency_token\": \"comp-pi_3ABC...\",\n    \"reason\": \"Salesforce timeout during checkout\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Design Principles<\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Principle<\/th><th>Implementation<\/th><th>Rationale<\/th><\/tr><\/thead><tbody><tr><td><strong>Check state before reversing<\/strong><\/td><td>Query current state, only update if needed<\/td><td>Makes compensation idempotent\u2014safe to retry<\/td><\/tr><tr><td><strong>Financial operations first<\/strong><\/td><td>Issue refund before Salesforce cleanup<\/td><td>Guest harm reversed immediately; data fixable async<\/td><\/tr><tr><td><strong>Business identifiers in, system IDs hidden<\/strong><\/td><td>Accept <code>guest_email<\/code>, resolve to Contact ID internally<\/td><td>LLM has email from conversation; logs stay readable<\/td><\/tr><tr><td><strong>Idempotency at every layer<\/strong><\/td><td>Client token \u2192 Backend check \u2192 Stripe Idempotency-Key<\/td><td>Safe to automate; no double-refunds<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Pattern 2: Fail-Fast Validation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Validate assumptions before expensive operations. Preventing failures is cheaper than compensating for them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this pattern when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Operations have prerequisites that could be violated<\/li>\n\n\n\n<li>Downstream operations are non-idempotent (payments, external API calls)<\/li>\n\n\n\n<li>Clear error messages can guide callers to fix input<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Example: The Multiple-Bookings Bug<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Original code assumed one booking per guest:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>search_booking(guest_email)\nupdate_booking(bookings&#91;0].id, status: \"Checked Out\")\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Problem: What if&nbsp;<code>bookings<\/code>&nbsp;has 0 or 2+ elements?<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>length == 0<\/code>: Accesses undefined \u2192 crash<\/li>\n\n\n\n<li><code>length > 1<\/code>: Updates first booking (might be wrong one)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The fix\u2014explicit validation before array access:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bookings = search_booking(guest_email)\n\nIF bookings.length == 0:\n  \u2192 Return 404 \"No checked-in booking found for this guest today\"\n\nIF bookings.length &gt; 1:\n  \u2192 Return 400 \"Multiple bookings found. Provide room_number or booking_number to disambiguate\"\n\nIF bookings.length == 1:\n  \u2192 Proceed with checkout\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This stops execution&nbsp;<strong>before<\/strong>&nbsp;charging payment. If validation happened after payment, you&#8217;d need compensation.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/05-fail-fast.svg\"><img decoding=\"async\" src=\"https:\/\/www.workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/05-fail-fast.svg\" alt=\"Infographic showing 3 approaches to transaction execution L-R: fail mid-execution (expensive--no validation, requires rollback &amp; reverting transactions), fail during validation (cheap, returns error before expensive transaction carried out), pass validation (returns success, all required data validated before expensive transactions attempted).\" class=\"wp-image-42217\"\/><\/a><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Design Principles<\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Principle<\/th><th>Implementation<\/th><th>Rationale<\/th><\/tr><\/thead><tbody><tr><td><strong>Validate before non-idempotent operations<\/strong><\/td><td>Check prerequisites before payments, external calls<\/td><td>Failures prevented &gt; failures compensated<\/td><\/tr><tr><td><strong>Validate array lengths<\/strong><\/td><td>Check length before accessing elements<\/td><td>Prevents crashes and wrong-record updates<\/td><\/tr><tr><td><strong>Return actionable errors<\/strong><\/td><td>Specific codes (400\/404\/409) with guidance<\/td><td>Callers can fix input without guessing<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Recovery Strategy Quick Reference<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Strategy<\/th><th>When to Use<\/th><th>Example<\/th><\/tr><\/thead><tbody><tr><td><strong>Compensating transaction<\/strong><\/td><td>Financial operations, state consistency critical<\/td><td>Payment succeeded, Salesforce failed \u2192 Issue refund<\/td><\/tr><tr><td><strong>Acceptable orphan<\/strong><\/td><td>Resource has low\/zero cost, will be reused<\/td><td>Stripe customer created, checkout failed \u2192 Customer reused on retry<\/td><\/tr><tr><td><strong>Fail-fast validation<\/strong><\/td><td>Preventing failure cheaper than recovering<\/td><td>Multiple bookings found \u2192 Return 400 before payment<\/td><\/tr><tr><td><strong>Retry with idempotency<\/strong><\/td><td>Transient failure, operation is idempotent<\/td><td>Salesforce timeout \u2192 Retry with same token<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Is Your Tool Recovery-Ready?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Saga Pattern<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Compensation orchestrator exists for financial operations<\/li>\n\n\n\n<li>Compensation checks state before reversing (idempotent)<\/li>\n\n\n\n<li>Financial compensation prioritized over data cleanup<\/li>\n\n\n\n<li>All compensation actions logged for audit trail<\/li>\n\n\n\n<li>Idempotency tokens flow through entire compensation flow<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Fail-Fast Validation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Array lengths validated before element access<\/li>\n\n\n\n<li>Prerequisites checked before non-idempotent operations<\/li>\n\n\n\n<li>Error codes are specific with actionable guidance<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Decision Placement<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Strategic decisions (when, which workflow) \u2192 LLM<\/li>\n\n\n\n<li>Tactical decisions (how, what transitions) \u2192 Backend<\/li>\n\n\n\n<li>Financial\/security decisions \u2192 Backend (always)<\/li>\n\n\n\n<li>Multiple trigger mechanisms supported where needed<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MCP standardizes how LLMs discover and invoke tools. What those tools do\u2014and how they handle partial failures, state consistency, and recovery\u2014is architecture you build into the tools themselves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<strong>saga pattern<\/strong>&nbsp;provides compensating transactions when multi-system workflows fail partway through.&nbsp;<strong>Fail-fast validation<\/strong>&nbsp;prevents failures by checking assumptions before expensive operations.&nbsp;<strong>Decision placement<\/strong>\u2014where recovery logic lives\u2014determines whether your system is testable, auditable, and flexible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can see these approaches in the Dewy Resort sample application. We&#8217;ve built a checkout orchestration tool that handles Stripe and Salesforce coordination, financial operations, state consistency across Booking\/Room\/Opportunity, automatic compensation, and idempotency at every layer.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Implementation:<\/strong>&nbsp;The complete Dewy Resort Hotel example is open source:&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/workato-devs\/dewy-resort\" target=\"_blank\">github.com\/workato-devs\/dewy-resort<\/a><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><em>This post builds on\u00a0<a href=\"https:\/\/www.workato.com\/the-connector\/designing-skills-for-enterprise-mcp\/\" type=\"link\" id=\"https:\/\/www.workato.com\/the-connector\/designing-skills-for-enterprise-mcp\/\" target=\"_blank\" rel=\"noreferrer noopener\">Designing Composable Skills for MCP Tools<\/a>\u00a0and\u00a0<a href=\"https:\/\/www.workato.com\/the-connector\/serverless-mcp-for-enterprise-ai-tools\/\" type=\"link\" id=\"https:\/\/www.workato.com\/the-connector\/serverless-mcp-for-enterprise-ai-tools\/\" target=\"_blank\" rel=\"noreferrer noopener\">Serverless MCP Execution<\/a>. For more on composable architecture patterns, see the complete series.<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<ol class=\"wp-block-list\">\n<li>The saga pattern was introduced by Hector Garcia-Molina and Kenneth Salem in their 1987 paper &#8220;Sagas&#8221; <a href=\"https:\/\/dl.acm.org\/doi\/10.1145\/38713.38742#:~:text=References18-,Abstract,break%20up%20LLTs%20into%20sagas.\">ACM SIGMOD<\/a>. For a modern treatment, see Chris Richardson&#8217;s <a href=\"https:\/\/microservices.io\/patterns\/data\/saga.html\">Saga pattern documentation<\/a>.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Distributed transactions fail partway through. Payment succeeds, then Salesforce times out. The guest is charged, but three systems hold stale state. In production, this happens constantly: a system times out, a connection drops mid-request, a user submits unexpected input. In distributed systems, these failures often mean a transaction completes in one system and fails in [&hellip;]<\/p>\n","protected":false},"author":126,"featured_media":16370,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[31,62],"tags":[],"class_list":["post-2972","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-automation-integration","category-enterprise-mcp"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Recovering from Partial Failures in Enterprise MCP Tools - Workato<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Recovering from Partial Failures in Enterprise MCP Tools - Workato\" \/>\n<meta property=\"og:description\" content=\"Distributed transactions fail partway through. Payment succeeds, then Salesforce times out. The guest is charged, but three systems hold stale state. In production, this happens constantly: a system times out, a connection drops mid-request, a user submits unexpected input. In distributed systems, these failures often mean a transaction completes in one system and fails in [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/\" \/>\n<meta property=\"og:site_name\" content=\"Workato\" \/>\n<meta property=\"article:published_time\" content=\"2026-02-03T04:12:18+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-08T12:34:23+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/blog-post3-B-copy-6.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2049\" \/>\n\t<meta property=\"og:image:height\" content=\"897\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Zayne Turner\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Zayne Turner\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/\"},\"author\":{\"name\":\"Zayne Turner\",\"@id\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/#\\\/schema\\\/person\\\/5774274ce937d6b9171322a903e2c7a7\"},\"headline\":\"Recovering from Partial Failures in Enterprise MCP Tools\",\"datePublished\":\"2026-02-03T04:12:18+00:00\",\"dateModified\":\"2026-10-08T12:34:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/\"},\"wordCount\":1567,\"image\":{\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/blog-post3-B-copy-6.png\",\"articleSection\":[\"Automation &amp; Integration\",\"Enterprise MCP\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/\",\"url\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/\",\"name\":\"Recovering from Partial Failures in Enterprise MCP Tools - Workato\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/blog-post3-B-copy-6.png\",\"datePublished\":\"2026-02-03T04:12:18+00:00\",\"dateModified\":\"2026-10-08T12:34:23+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/#\\\/schema\\\/person\\\/5774274ce937d6b9171322a903e2c7a7\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#primaryimage\",\"url\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/blog-post3-B-copy-6.png\",\"contentUrl\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/blog-post3-B-copy-6.png\",\"width\":2049,\"height\":897},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/recovering-from-partial-failures-in-enterprise-mcp-tools\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Recovering from Partial Failures in Enterprise MCP Tools\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/#website\",\"url\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/\",\"name\":\"Workato\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.workato.com\\\/the-connector\\\/#\\\/schema\\\/person\\\/5774274ce937d6b9171322a903e2c7a7\",\"name\":\"Zayne Turner\",\"sameAs\":[\"https:\\\/\\\/github.com\\\/zaynelt\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/zayne-turner\"],\"url\":\"https:\\\/\\\/blog2workato.wpengine.com\\\/author\\\/zayne-turner\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Recovering from Partial Failures in Enterprise MCP Tools - Workato","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/","og_locale":"en_US","og_type":"article","og_title":"Recovering from Partial Failures in Enterprise MCP Tools - Workato","og_description":"Distributed transactions fail partway through. Payment succeeds, then Salesforce times out. The guest is charged, but three systems hold stale state. In production, this happens constantly: a system times out, a connection drops mid-request, a user submits unexpected input. In distributed systems, these failures often mean a transaction completes in one system and fails in [&hellip;]","og_url":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/","og_site_name":"Workato","article_published_time":"2026-02-03T04:12:18+00:00","article_modified_time":"2026-10-08T12:34:23+00:00","og_image":[{"width":2049,"height":897,"url":"https:\/\/workato.com\/the-connector\/wp-content\/uploads\/2026\/10\/blog-post3-B-copy-6.png","type":"image\/png"}],"author":"Zayne Turner","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Zayne Turner","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#article","isPartOf":{"@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/"},"author":{"name":"Zayne Turner","@id":"https:\/\/www.workato.com\/the-connector\/#\/schema\/person\/5774274ce937d6b9171322a903e2c7a7"},"headline":"Recovering from Partial Failures in Enterprise MCP Tools","datePublished":"2026-02-03T04:12:18+00:00","dateModified":"2026-10-08T12:34:23+00:00","mainEntityOfPage":{"@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/"},"wordCount":1567,"image":{"@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#primaryimage"},"thumbnailUrl":"https:\/\/blog2workato.wpengine.com\/wp-content\/uploads\/2026\/10\/blog-post3-B-copy-6.png","articleSection":["Automation &amp; Integration","Enterprise MCP"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/","url":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/","name":"Recovering from Partial Failures in Enterprise MCP Tools - Workato","isPartOf":{"@id":"https:\/\/www.workato.com\/the-connector\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#primaryimage"},"image":{"@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#primaryimage"},"thumbnailUrl":"https:\/\/blog2workato.wpengine.com\/wp-content\/uploads\/2026\/10\/blog-post3-B-copy-6.png","datePublished":"2026-02-03T04:12:18+00:00","dateModified":"2026-10-08T12:34:23+00:00","author":{"@id":"https:\/\/www.workato.com\/the-connector\/#\/schema\/person\/5774274ce937d6b9171322a903e2c7a7"},"breadcrumb":{"@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#primaryimage","url":"https:\/\/blog2workato.wpengine.com\/wp-content\/uploads\/2026\/10\/blog-post3-B-copy-6.png","contentUrl":"https:\/\/blog2workato.wpengine.com\/wp-content\/uploads\/2026\/10\/blog-post3-B-copy-6.png","width":2049,"height":897},{"@type":"BreadcrumbList","@id":"https:\/\/blog2workato.wpengine.com\/recovering-from-partial-failures-in-enterprise-mcp-tools\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog2workato.wpengine.com\/"},{"@type":"ListItem","position":2,"name":"Recovering from Partial Failures in Enterprise MCP Tools"}]},{"@type":"WebSite","@id":"https:\/\/www.workato.com\/the-connector\/#website","url":"https:\/\/www.workato.com\/the-connector\/","name":"Workato","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.workato.com\/the-connector\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.workato.com\/the-connector\/#\/schema\/person\/5774274ce937d6b9171322a903e2c7a7","name":"Zayne Turner","sameAs":["https:\/\/github.com\/zaynelt","https:\/\/www.linkedin.com\/in\/zayne-turner"],"url":"https:\/\/blog2workato.wpengine.com\/author\/zayne-turner\/"}]}},"_links":{"self":[{"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/posts\/2972","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/users\/126"}],"replies":[{"embeddable":true,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/comments?post=2972"}],"version-history":[{"count":4,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/posts\/2972\/revisions"}],"predecessor-version":[{"id":16369,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/posts\/2972\/revisions\/16369"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/media\/16370"}],"wp:attachment":[{"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/media?parent=2972"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/categories?post=2972"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog2workato.wpengine.com\/wp-json\/wp\/v2\/tags?post=2972"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}