# <img width="25" height="25" alt="mcp" src="https://github.com/user-attachments/assets/80bf0bb2-d116-404a-91a0-5b4f3af2e476" /> TIBCO Flogo® MCP Stateful Server — Loan Application Wizard Sample

## Overview

This sample demonstrates how to configure a **stateful Flogo MCP server** (`statelessServer = false`) using a realistic **multi-step loan application wizard** scenario.

A stateful MCP server issues a unique `Mcp-Session-Id` during initialization. The MCP client must include this session ID in the header of every subsequent request. This allows the server to track per-client context — such as accumulated wizard state — across multiple tool calls that belong to the same logical session.

---

## Stateless vs Stateful — Quick Reference

| Feature | Stateless (`statelessServer: true`) | Stateful (`statelessServer: false`) |
|---|---|---|
| `Mcp-Session-Id` issued | ❌ No | ✅ Yes |
| Client must send session header | ❌ No | ✅ Yes (on all subsequent requests) |
| Per-client context tracked server-side | ❌ No | ✅ Yes |
| Safe for horizontal scaling / load balancers | ✅ Yes | ⚠️ Only with sticky sessions or external state store |
| Required for MCP Elicitation | ❌ No | ✅ Yes |
| Required for MCP Sampling | ❌ No | ✅ Yes |
| Required for MCP Logging streams | ❌ No | ✅ Yes |
| Best for | Read-only lookups, shared data APIs, high-volume tools | Multi-step wizards, conversation context, user-specific sessions |

---

## The `statelessServer` Setting in Flogo

In the Flogo MCP server trigger, the `statelessServer` field is set at the **server level**:

```json
"settings": {
  "serverName": "LoanApplicationMCPServer",
  "serverType": "HTTP",
  "serverPort": "9095",
  "statelessServer": false,
  "serverEndpointPath": "/mcp"
}
```

- **`statelessServer: false`** — The server issues a globally unique, cryptographically secure `Mcp-Session-Id` in the `Mcp-Session-Id` HTTP response header during initialization. The MCP client must include this header in all subsequent requests. Requests missing the session header are rejected with `HTTP 400 Bad Request`. The server can associate any server-side state (conversation context, in-progress workflow data) with this session ID.
- **`statelessServer: true`** — No session ID is issued. See the [MCP_Stateless_Server](../MCP_Stateless_Server/README.md) sample.

---

## Why Stateful for Loan Application?

A loan application wizard has properties that make stateful essential:

- **Multi-step, accumulated context** — The applicant provides personal details in step 1, financial data in step 2, and the final submission in step 4 needs all of that accumulated data. In this sample, step 2 parses the JSON created by step 1 and writes back a merged session document, so later tools can review and submit the full application state. Without session continuity, each tool call would arrive at the server with no prior context.
- **Ordered workflow** — Steps must execute in sequence: `start_loan_application` → `submit_financial_profile` → `review_application` → `submit_application`. The session guarantees that all four calls from the same agent are routed correctly and share state.
- **Per-applicant isolation** — Different applicants running concurrent applications must have completely separate session contexts. The `Mcp-Session-Id` naturally isolates each applicant's in-progress data.
- **Review-before-submit pattern** — The applicant must review accumulated state (step 3) before the server can proceed to external submission (step 4). This requires the server to have stored the earlier inputs.

### How Session State Is Stored in This Sample

The Flogo flows use **file-based state persistence** (via the Flogo Files connector `read`/`write` activities) keyed by `application_ref`. This keeps the sample self-contained and runnable without external dependencies.

For readability, the sample uses a **deterministic mock application reference** in the form `LOAN-{loan_type}-20260414`, then stores the state file as `session_{application_ref}.json`. In production, replace that mock value with a UUID or timestamp-based reference generated by your app or backend.

```
Session flow:
  start_loan_application    → writes  /tmp/loan_application_sessions/session_{ref}.json
  submit_financial_profile  → reads + updates same file
  review_application        → reads   same file (read-only)
  submit_application        → reads   same file → submits → generates tracking number
```

> **Production guidance:** Replace the file activities with a distributed cache (Redis), database (PostgreSQL, MySQL), or your organisation's state management service. In a load-balanced deployment, external state storage is required so any server instance can serve any request within the same session.

---

## The Four-Step Loan Application Wizard

### Step-by-Step Flow

```
AI Agent calls start_loan_application
        │  (applicant name, email, loan type, amount, term)
        │  → session state file created
        ▼
  [start_loan_application]    returns application_ref: LOAN-HOME_LOAN-20260414
        │
        ▼
AI Agent calls submit_financial_profile
        │  (application_ref, income, employment, expenses, debt, credit_score)
        │  → session state file updated with financial profile
        ▼
  [submit_financial_profile]  returns confirmation + next-step prompt
        │
        ▼
AI Agent calls review_application
        │  (application_ref)
        │  → reads full accumulated session state
        ▼
  [review_application]        returns complete application summary for applicant confirmation
        │
        ▼
AI Agent calls submit_application
        │  (application_ref, applicant_consent=true)
        │  → reads session state → dispatches to external loan origination API
        ▼
  [submit_application]        returns unique tracking number TRK-...
```

---

## Tool Summary

| Tool | Step | readOnly | destructive | idempotent | openWorld | Description |
|---|:---:|:---:|:---:|:---:|:---:|---|
| `start_loan_application` | 1 | ❌ | ❌ | ❌ | ❌ | Creates a new application session with applicant details |
| `submit_financial_profile` | 2 | ❌ | ❌ | ✅ | ❌ | Adds/updates financial data in the session (safe to retry) |
| `review_application` | 3 | ✅ | ❌ | — | ❌ | Read-only review of all accumulated session state |
| `submit_application` | 4 | ❌ | ❌ | ❌ | ✅ | Final submission to external loan origination API |

### Why `submit_financial_profile` is Idempotent

If the AI agent retries this call (e.g. due to a network timeout) with the same financial data, the session state file is overwritten with identical content — the resulting state is the same as if it had been called once. No duplicate records are created.

### Why `submit_application` is NOT Idempotent

Each call dispatches a new submission to the external loan origination API, which generates a **new unique tracking number**. Calling it twice would create two separate underwriting cases. The agent should NOT retry this tool without user confirmation.

---

## Getting Started

### Prerequisites

- TIBCO Flogo® **2.26.3** or later
- An MCP-capable client (e.g. GitHub Copilot in VS Code, Claude Desktop)

### Import the App

Import `LoanApplicationMCPServer.flogo` into VS Code using the Flogo extension.

### Configure App Properties

| Property | Default | Description |
|---|---|---|
| `FlogoMcpServer.PORT` | `9095` | HTTP port the MCP server listens on |
| `session_state_dir` | `/tmp/loan_application_sessions` | Directory for session state files. Update this for your environment. On Windows, use a path like `C:/Temp/loan_sessions`. |

### Run the App

Run `LoanApplicationMCPServer.flogo` from VS Code. The server starts at:

```
http://localhost:9095/mcp
```

### Configure Your MCP Client

**VS Code (`mcp.json`):**
```json
{
  "servers": {
    "LoanApplicationMCPServer": {
      "type": "http",
      "url": "http://localhost:9095/mcp"
    }
  }
}
```

**Claude Desktop (`claude_desktop_config.json`):**
```json
{
  "mcpServers": {
    "LoanApplicationMCPServer": {
      "command": "npx",
      "args": ["mcp-remote", "http://localhost:9095/mcp"]
    }
  }
}
```

### Try It — Example Conversation

Ask your AI agent to guide you through the loan application:

```
"I'd like to apply for a home loan of $450,000 over 30 years"
```
→ Agent calls `start_loan_application`, receives `application_ref: LOAN-HOME_LOAN-20260414`

```
"My name is Jane Smith, email jane@example.com. Please use those details."
```
→ (If prompted, agent re-calls `start_loan_application` with full details)

```
"My annual income is $95,000, I'm employed at Acme Corp, monthly expenses are $3,800, 
 I have $12,000 existing debt, and my credit score is 740"
```
→ Agent calls `submit_financial_profile` with the application_ref from step 1

```
"Can you show me everything I've entered so far before I submit?"
```
→ Agent calls `review_application` — reads all accumulated session state from steps 1 and 2

```
"That all looks correct, please submit my application"
```
→ Agent calls `submit_application` with `applicant_consent: true`  
→ Returns: `TRK-LOAN-HOME_LOAN-20260414-20260414-093042`

Notice that the server **accumulates** context across all four calls using the session state file. This is only possible because `statelessServer: false` ensures the session is tracked from initialization through to final submission.

---

## Session Management — MCP Protocol Detail

When `statelessServer: false`, the Flogo MCP server participates in the full MCP session management lifecycle:

```
Client                                    Server (statelessServer: false)
  │── POST /mcp (Initialize) ──────────▶ │
  │◀─ InitializeResult ─────────────────│  Mcp-Session-Id: a9f3c8e1... issued
  │
  │── POST /mcp (tools/call start_loan_application)
  │   Mcp-Session-Id: a9f3c8e1... ──────▶ │  session identified, state written
  │◀─ tool result ────────────────────────│
  │
  │── POST /mcp (tools/call submit_financial_profile)
  │   Mcp-Session-Id: a9f3c8e1... ──────▶ │  same session, state updated
  │◀─ tool result ────────────────────────│
  │
  │── POST /mcp (tools/call review_application)
  │   Mcp-Session-Id: a9f3c8e1... ──────▶ │  same session, full state read
  │◀─ tool result ────────────────────────│
  │
  │── POST /mcp (tools/call submit_application)
  │   Mcp-Session-Id: a9f3c8e1... ──────▶ │  same session, state submitted externally
  │◀─ tool result (tracking number) ──────│
  │
  │── DELETE /mcp                          │  session terminated
  │   Mcp-Session-Id: a9f3c8e1... ──────▶ │
  │◀─ 200 OK ─────────────────────────────│
```

If the client sends a request with an **unrecognised or expired** session ID, the server responds with `HTTP 404 Not Found`, and the client must start a new session with a fresh `InitializeRequest`.

---

## Adapting for Production

The flows use file activities for state storage. For production:

| Component | Replace with |
|---|---|
| Session state (file write/read) | Redis / Memcached / PostgreSQL / your enterprise data store — keyed by `application_ref` |
| External submission (mock return) | REST Invoke → external loan origination API (e.g. Encompass, nCino, or your LOS) |
| Load balancing | Sticky sessions (session-affinity) OR move state storage to external distributed cache so any instance can handle any session request |

---

## When to Use Stateful — Decision Guide

Use `statelessServer: false` when **any** of the following are true:

- ✅ The workflow has multiple steps that must share accumulated context
- ✅ Different users must have isolated, per-session data
- ✅ You need MCP Elicitation (server sends interactive forms to client mid-flow)
- ✅ You need MCP Sampling (server asks client's LLM for a completion mid-flow)
- ✅ You need persistent server-to-client SSE notification streams (MCP Logging)
- ✅ The tool sequence must be ordered and the server must enforce it

Use `statelessServer: true` (see [MCP_Stateless_Server](../MCP_Stateless_Server/README.md)) when each tool call is self-contained and requires no context from previous calls.

---

## Related Resources

- [MCP Specification — Transports (Session Management)](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management)
- [MCP Stateless Server Sample](../MCP_Stateless_Server/README.md) — Product catalog with `statelessServer: true`
- [Smart Incident Response Assistant](../Smart_Incident_Response_Assistant/README.md) — MCP Elicitation, Logging, and Sampling (all require stateful server)
- [Customer360 MCP Sample](../Customer360/README.md) — Basic Flogo MCP server
- [MCP Tool Annotations Sample](../MCP_Tool_Annotations/README.md) — All four tool annotation hints demonstrated
