MCP

In the previous module, you deployed the backend databases, REST APIs and MCP servers. This module demonstrates how to register the MCP servers with Llama Stack, how to programmatically interact with Llama Stack and MCP tools using Python - the primary language for AI application development.

You’ll explore 2 distinct approaches to building agents that consume MCP tools:

  1. Llama Stack Client: The native Python SDK for Llama Stack, providing direct access to tool invocation

  2. LangGraph: A popular third-party agentic framework that uses Llama Stack as its inference backend

Both approaches demonstrate important patterns for agent development, from simple tool invocation to complex multi-step reasoning.

Two approaches to agent development

Llama Stack’s flexible architecture supports multiple development patterns:

Direct tool invocation (Llama Stack Client)

Best for: Orchestrating tool calls programmatically when you control the logic

  • You decide which tools to call and when

  • Useful for deterministic workflows, testing, and integration scenarios

  • Full control over tool invocation, parameter passing, and result handling

Agent-driven invocation (LangGraph + Llama Stack)

Best for: Building autonomous agents that reason about tool usage

  • The agent decides which tools to call based on user intent

  • Useful for conversational interfaces, customer support, and complex problem-solving

  • Agent handles tool selection, parameter extraction, and result synthesis

This module demonstrates both patterns so you can choose the right approach for your use case.

Setup

Make sure you are in the correct base directory

cd $HOME/fantaco-redhat-one-2026/
pwd
/home/lab-user/fantaco-redhat-one-2026

Check that you have set key env variables

export LLAMA_STACK_BASE_URL=http://llamastack-distribution-vllm-service.agentic-{user}.svc:8321
export INFERENCE_MODEL=vllm/qwen3-14b
echo "LLAMA_STACK_BASE_URL="$LLAMA_STACK_BASE_URL
echo "INFERENCE_MODEL="$INFERENCE_MODEL
LLAMA_STACK_BASE_URL=http://llamastack-distribution-vllm-service.agentic-{user}.svc:8321
INFERENCE_MODEL=vllm/qwen3-14b

If needed, create a Python virtual environment (venv)

python -m venv .venv

or you can look for an existing .venv folder

ls .venv

The following response indicates you need to create your Python venv

ls: cannot access '.venv': No such file or directory

and

echo $VIRTUAL_ENV
/home/lab-user/fantaco-redhat-one-2026/.venv

Set environment

source .venv/bin/activate

And if you use Terminal 2 you wish to make sure all the env vars are set up there as well.

The execution of the source command will change the prompt itself to look like the following:

((.venv) ) [lab-user: ~/fantaco-redhat-one-2026]

Change to the correct sub-directory

cd mcp-examples/
pwd
/home/lab-user/fantaco-redhat-one-2026/mcp-examples/

Python Setup

You should have access to python and pip

python -V
Python 3.12.11
pip -V
pip 23.2.1 from /usr/lib/python3.12/site-packages/pip (python 3.12)
which python
~/fantaco-redhat-one-2026/.venv/bin/python

Install dependencies

pip install -r requirements.txt

This installs the required Python packages:

  • llamastack-client: Official Python SDK for Llama Stack

  • langgraph: Graph-based agent framework

  • langchain-openai: OpenAI-compatible LangChain integration

  • Supporting libraries for HTTP requests and JSON processing

Register MCP Servers

Verify that you have connectivity to the MCP Servers which in turn have connectivity to the REST endpoints.

export CUSTOMER_MCP_SERVER_URL=https://$(oc get routes -l app=mcp-customer -o jsonpath="{range .items[*]}{.status.ingress[0].host}{end}")/mcp
export FINANCE_MCP_SERVER_URL=https://$(oc get routes -l app=mcp-finance -o jsonpath="{range .items[*]}{.status.ingress[0].host}{end}")/mcp
echo $CUSTOMER_MCP_SERVER_URL
echo $FINANCE_MCP_SERVER_URL
https://mcp-customer-route-default.apps.cluster-frcqw.dynamic.redhatworkshops.io/mcp
https://mcp-finance-route-default.apps.cluster-frcqw.dynamic.redhatworkshops.io/mcp

Register Customer MCP with Llama Stack

echo "LLAMA_STACK_BASE_URL: ${LLAMA_STACK_BASE_URL}"
echo "CUSTOMER_MCP_SERVER_URL: ${CUSTOMER_MCP_SERVER_URL}"

curl -w "\nHTTP Status: %{http_code}\n" -X POST "${LLAMA_STACK_BASE_URL}/v1/toolgroups" \
  -H "Content-Type: application/json" \
  -d '{
    "toolgroup_id": "customer_mcp",
    "provider_id": "model-context-protocol",
    "mcp_endpoint": { "uri": "'"${CUSTOMER_MCP_SERVER_URL}"'" }
  }'
LLAMA_STACK_BASE_URL: http://localhost:8321
CUSTOMER_MCP_SERVER_URL: https://mcp-customer-route-default.apps.cluster-frcqw.dynamic.redhatworkshops.io/mcp
null
HTTP Status: 200

Note: If you feel that your registration failed, perhaps the wrong URL, then there Python and shell scripts to help you unregister the tool.

See that the new toolgroup is registered

curl -sS -H "Content-Type: application/json" $LLAMA_STACK_BASE_URL/v1/toolgroups | jq
{
  "data": [
    {
      "identifier": "builtin::rag",
      "provider_resource_id": "builtin::rag",
      "provider_id": "rag-runtime",
      "type": "tool_group",
      "mcp_endpoint": null,
      "args": null
    },
    {
      "identifier": "builtin::websearch",
      "provider_resource_id": "builtin::websearch",
      "provider_id": "tavily-search",
      "type": "tool_group",
      "mcp_endpoint": null,
      "args": null
    },
    {
      "identifier": "customer_mcp",
      "provider_resource_id": "customer_mcp",
      "provider_id": "model-context-protocol",
      "type": "tool_group",
      "mcp_endpoint": {
        "uri": "https://mcp-customer-route-agentic-user1.apps.cluster-b8h97.dynamic.redhatworkshops.io/mcp"
      },
      "args": null
    }
  ]
}

Register Finance MCP with Llama Stack

echo "LLAMA_STACK_BASE_URL: ${LLAMA_STACK_BASE_URL}"
echo "FINANCE_MCP_SERVER_URL: ${FINANCE_MCP_SERVER_URL}"

curl -w "\nHTTP Status: %{http_code}\n" -X POST "${LLAMA_STACK_BASE_URL}/v1/toolgroups" \
  -H "Content-Type: application/json" \
  -d '{
    "toolgroup_id": "finance_mcp",
    "provider_id": "model-context-protocol",
    "mcp_endpoint": { "uri": "'"${FINANCE_MCP_SERVER_URL}"'" }
  }'
LLAMA_STACK_BASE_URL: http://localhost:8321
FINANCE_MCP_SERVER_URL: https://mcp-finance-route-default.apps.cluster-frcqw.dynamic.redhatworkshops.io/mcp
null
HTTP Status: 200

See if both the Customer and Finance MCP servers are registered

curl -sS -H "Content-Type: application/json" \
  "$LLAMA_STACK_BASE_URL/v1/toolgroups" \
| jq -r '.data[] | select(.provider_id == "model-context-protocol") | .identifier'
customer_mcp
finance_mcp

What just happened? You registered two MCP servers as tool groups with Llama Stack. Llama Stack now knows where to find them and will query them for available tools at runtime. Notice the provider_id is model-context-protocol — this tells Llama Stack to use the MCP protocol for tool discovery and invocation, as opposed to built-in tools like builtin::rag.

List Customer Tools

curl -sS -L -H "Content-Type: application/json" \
  "$LLAMA_STACK_BASE_URL/v1/tool-runtime/list-tools?tool_group_id=customer_mcp" \
| jq -r '.data[] | "Tool: \(.name)\nDescription: \(.description | split("\n")[0])\n"'
Tool: search_customers
Description: Search for customers by various fields with partial matching

Tool: get_customer
Description: Get customer by ID

List Finance Tools

curl -sS -L -H "Content-Type: application/json" \
  "$LLAMA_STACK_BASE_URL/v1/tool-runtime/list-tools?tool_group_id=finance_mcp" \
| jq -r '.data[] | "Tool: \(.name)\nDescription: \(.description | split("\n")[0])\n"'
Tool: fetch_order_history
Description: Get order history for a customer.

Tool: fetch_invoice_history
Description: Get invoice history for a customer.

Tool discovery and inspection

Before building agents, you need to understand what tools are available. Llama Stack provides APIs for discovering registered tool groups and inspecting individual tools. This dynamic discovery is a key feature of MCP - agents can adapt to available capabilities without hardcoded dependencies.

The sample code includes Python scripts for: - Listing all registered tool groups - Inspecting tools in each MCP server - Invoking tools directly (Llama Stack Client approach) - Building autonomous agents (LangGraph approach)

Available Tools

python 2_list_tools.py
==================================================
Registered Toolgroups
==================================================

Toolgroup ID:  builtin::rag
Provider ID:   rag-runtime
--------------------------------------------------

Toolgroup ID:  builtin::websearch
Provider ID:   tavily-search
--------------------------------------------------

Toolgroup ID:  customer_mcp
Provider ID:   model-context-protocol
MCP Endpoint:  https://mcp-customer-route-agentic-user1.apps.cluster-b8h97.dynamic.redhatworkshops.io/mcp
--------------------------------------------------

Toolgroup ID:  finance_mcp
Provider ID:   model-context-protocol
MCP Endpoint:  https://mcp-finance-route-agentic-user1.apps.cluster-b8h97.dynamic.redhatworkshops.io/mcp
--------------------------------------------------

Total toolgroups: 4
==================================================

Customer MCP Tools

python 3_list_customer_tools.py
==================================================
Customer MCP Server Tools
==================================================
MCP Server URL: https://mcp-customer-route-showroom-27qxd-1-user1.apps.cluster-27qxd.dynamic.redhatworkshops.io/mcp

Tool Name:    search_customers
Description:  Search for customers by various fields with partial matching

Args:
    company_name: Filter by company name (partial matching, optional)
    contact_name: Filter by contact person name (partial matching, optional)
    contact_email: Filter by contact email address (partial matching, optional)
    phone: Filter by phone number (partial matching, optional)

Returns:
    List of customers matching the search criteria
--------------------------------------------------
Tool Name:    get_customer
Description:  Get customer by ID

Retrieves a single customer record by its unique identifier

Args:
    customer_id: The unique 5-character identifier of the customer

Returns:
    Customer details including customerId, companyName, contactName, contactTitle,
    address, city, region, postalCode, country, phone, fax, contactEmail,
    createdAt, and updatedAt
--------------------------------------------------

Total tools: 2
==================================================

Finance MCP tools

python 3_list_finance_tools.py
==================================================
Finance MCP Server Tools
==================================================
MCP Server URL: https://mcp-finance-route-showroom-27qxd-1-user1.apps.cluster-27qxd.dynamic.redhatworkshops.io/mcp

Tool Name:    fetch_order_history
Description:  Get order history for a customer.

Retrieves the order history for a specific customer with optional date filtering and pagination.

Args:
    customer_id: Unique identifier for the customer (e.g., "CUST-12345")
    start_date: Start date for filtering orders in ISO 8601 format (e.g., "2024-01-15T10:30:00")
    end_date: End date for filtering orders in ISO 8601 format (e.g., "2024-01-31T23:59:59")
    limit: Maximum number of orders to return (default: 50)

Returns:
    Dictionary containing:
    - success: Boolean indicating if the request was successful
    - message: Description of the result
    - data: List of order objects with details (id, orderNumber, customerId, totalAmount, status, orderDate, etc.)
    - count: Number of orders returned
--------------------------------------------------
Tool Name:    fetch_invoice_history
Description:  Get invoice history for a customer.

Retrieves the invoice history for a specific customer with optional date filtering and pagination.

Args:
    customer_id: Unique identifier for the customer (e.g., "CUST-12345")
    start_date: Start date for filtering invoices in ISO 8601 format (e.g., "2024-01-15T10:30:00")
    end_date: End date for filtering invoices in ISO 8601 format (e.g., "2024-01-31T23:59:59")
    limit: Maximum number of invoices to return (default: 50)

Returns:
    Dictionary containing:
    - success: Boolean indicating if the request was successful
    - message: Description of the result
    - data: List of invoice objects with details (id, invoiceNumber, orderId, customerId, amount, status, invoiceDate, dueDate, paidDate, etc.)
    - count: Number of invoices returned
--------------------------------------------------


Total tools: 2
==================================================

Notice the rich tool descriptions returned by the MCP servers. Each tool includes: - Name: Unique identifier for the tool - Description: Explains what the tool does and when to use it - Args: Detailed parameter specifications with types and requirements - Returns: Description of the expected result structure

This metadata enables agents to understand how to use tools without prior knowledge of the backend systems. The agent can read these descriptions and determine which tool to call for a given user request.

Approach 1: Llama Stack Client (direct tool invocation)

The Llama Stack Client SDK provides a programmatic interface to all Llama Stack capabilities. When you use this approach, your Python code explicitly decides which tools to invoke and when - you’re orchestrating the tool calls rather than letting an agent reason about them.

This approach is useful when: - You have deterministic workflows with known tool sequences - You’re testing tool connectivity and responses - You’re building integrations where tool selection is rule-based - You need fine-grained control over error handling and retries

Llama Stack Client: Customer

The key elements of code to pay attention to in the Llama Stack Client for Customer are creation of the Client

client = Client(
    base_url=BASE_URL,
    api_key=API_KEY
)

And the invocation of the tool.

result = client.tool_runtime.invoke_tool(
    tool_name="search_customers",
    kwargs={"contact_email": email}
)

Run the script

python 4_llamastack_client_customer.py
Base URL: http://llamastack-distribution-vllm-service:8321
Model:    vllm/qwen3-14b

==================================================
Searching for customer: thomashardy@example.com
==================================================

==================================================
CUSTOMER SEARCH RESULT
==================================================
{
  "results": [
    {
      "customerId": "AROUT",
      "companyName": "Around the Horn",
      "contactName": "Thomas Hardy",
      "contactTitle": "Sales Representative",
      "address": "120 Hanover Sq.",
      "city": "London",
      "region": null,
      "postalCode": "WA1 1DP",
      "country": "UK",
      "phone": "(171) 555-7788",
      "fax": "(171) 555-6750",
      "contactEmail": "thomashardy@example.com",
      "createdAt": "2026-01-02T18:21:35.131298",
      "updatedAt": "2026-01-02T18:21:35.131298"
    }
  ]
}
==================================================

Direct invocation = no LLM involved. This approach calls the MCP tool directly through client.tool_runtime.invoke_tool(). The LLM never sees the request — your code decides which tool to call and with what parameters. This is useful for testing, scripted workflows, and cases where tool selection is deterministic.

Llama Stack Client: Finance

Also run the Llama Stack Client for Finance

python 4_llamastack_client_finance.py
Base URL: http://llamastack-distribution-vllm-service:8321
Model:    vllm/qwen3-14b

==================================================
Fetching order history for customer: AROUT
==================================================

==================================================
ORDER HISTORY FOR CUSTOMER: AROUT
==================================================

Order #1:
  Order ID:     8
  Order Number: ORD-008
  Order Date:   2024-01-30T15:20:00
  Status:       PENDING
  Total Amount: $59.99

Order #2:
  Order ID:     3
  Order Number: ORD-003
  Order Date:   2024-01-25T09:45:00
  Status:       PENDING
  Total Amount: $89.99

Order #3:
  Order ID:     4
  Order Number: ORD-004
  Order Date:   2024-01-10T16:20:00
  Status:       DELIVERED
  Total Amount: $199.99

==================================================
Total Orders Found: 3
==================================================

The Llama Stack Client approach demonstrates direct tool invocation where you control the workflow. Notice that: - Your code explicitly calls client.tool_runtime.invoke_tool() - You specify the exact tool name and parameters - You receive structured ToolInvocationResult objects - No LLM reasoning is involved - this is pure tool execution

This pattern is efficient for known workflows but doesn’t leverage the agent’s ability to reason about which tools to use based on natural language input.

Approach 2: LangGraph with Llama Stack (agent-driven invocation)

LangGraph is a framework for building stateful, multi-agent applications using a graph-based execution model. When integrated with Llama Stack, LangGraph uses Llama Stack’s Response API (or ChatCompletion API) for inference while adding sophisticated agent orchestration capabilities.

Why use LangGraph with Llama Stack?

LangGraph adds agent capabilities
  • Autonomous tool selection: Agent decides which tools to call based on user intent

  • Natural language understanding: Extract tool parameters from conversational input

  • Multi-step reasoning: Chain multiple tool calls together to answer complex questions

  • State management: Maintain conversation context across turns

Llama Stack provides the inference layer
  • Model serving: vLLM handles the heavy lifting of model inference

  • Tool runtime: MCP servers are invoked through Llama Stack’s tool execution

  • Unified API: LangGraph uses standard OpenAI-compatible endpoints

Together, they enable production agentic applications
  • LangGraph orchestrates the agent workflow (what to do)

  • Llama Stack executes the operations (how to do it)

  • MCP servers connect to business systems (where to get data)

sequenceDiagram participant User participant LG as LangGraph Agent participant LLS as Llama Stack
Response API participant Model as vLLM Model participant MCP as MCP Server User->>LG: "Find customer with email
thomashardy@example.com" LG->>LLS: POST /v1/responses
(user message) LLS->>Model: Inference request Model->>Model: Analyze intent
Generate tool call Model-->>LLS: "Call search_customers
with email parameter" LLS->>MCP: Invoke search_customers MCP-->>LLS: Customer data LLS-->>LG: Tool result LG->>LLS: POST /v1/responses
(with tool result) LLS->>Model: Generate response Model-->>LLS: Natural language answer LLS-->>LG: Final response LG-->>User: "Found customer:
Thomas Hardy at
Around the Horn..." Note over User,MCP: Agent reasons about tools,
Llama Stack executes them
Figure 1. LangGraph agent workflow with Llama Stack

LangGraph integration with Llama Stack

LangGraph connects to Llama Stack through OpenAI-compatible APIs, making it easy to swap between different inference providers. The key integration points are:

OpenAI-compatible endpoints

Llama Stack exposes /v1/chat/completions and /v1/responses endpoints that follow OpenAI’s API specification. LangGraph uses these endpoints as if it were talking to OpenAI’s GPT models.

Response API usage

The code uses use_responses_api=True to leverage Llama Stack’s native Response API instead of ChatCompletion. This provides better integration with Llama Stack’s tool runtime.

MCP tool binding

Tools are bound to the LLM using MCP server references. LangGraph tells Llama Stack which MCP servers to make available to the agent.

Since Llama Stack’s OpenAI-compatible endpoints don’t require real authentication (when using local vLLM), you can set a placeholder API key.

Setup

Many frameworks assume that you need an OpenAI API key but in the case of using vLLM via MaaS and/or via Llama Stack, we simply need to set a "not-applicable" value.

export API_KEY="not-applicable"

LangGraph Client: Customer

The key elements in the code include:

llm = ChatOpenAI(
    model=INFERENCE_MODEL,
    openai_api_key=API_KEY,
    base_url=f"{BASE_URL}/v1",
    use_responses_api=True
)
# MCP tool binding using OpenAI Responses API format
llm_with_tools = llm.bind(
    tools=[
        {
            "type": "mcp",
            "server_label": "customer_mcp",
            "server_url": os.getenv("CUSTOMER_MCP_SERVER_URL"),
            "require_approval": "never",
        },
    ])
response = graph.invoke(
    {"messages": [{"role": "user", "content": "Search for customer with email thomashardy@example.com"}]})

Execute it

python 5_langgraph_client_customer.py
Base URL: http://localhost:8321
Model:    vllm/qwen3-14b
Testing LLM connectivity...
LLM connectivity OK

==================================================
Searching for customer: thomashardy@example.com
==================================================

==================================================
CUSTOMER SEARCH RESULTS
==================================================

Customer ID:   AROUT
Company Name:  Around the Horn
Contact Name:  Thomas Hardy
Contact Email: thomashardy@example.com
==================================================


Assistant:

Here is the customer information matching the email **thomashardy@example.com**:

**Customer ID:** AROUT
**Company:** Around the Horn
**Contact Name:** Thomas Hardy
**Title:** Sales Representative
**Address:** 120 Hanover Sq.
**City:** London
**Postal Code:** WA1 1DP
**Country:** UK
**Phone:** (171) 555-7788
**Fax:** (171) 555-6750
**Email:** thomashardy@example.com
**Created At:** 2026-01-02 18:21:35
**Updated At:** 2026-01-02 18:21:35

Let me know if you'd like to view more details or perform any actions for this customer!

LangGraph Client: Finance

python 5_langgraph_client_finance.py
Base URL: http://localhost:8321
Model:    vllm/qwen3-14b
Testing LLM connectivity...
LLM connectivity OK

==================================================
Fetching order history for customer: AROUT
==================================================

==================================================
ORDER HISTORY RESULTS
==================================================

Order #1:
  Order ID:     8
  Order Number: ORD-008
  Order Date:   2024-01-30T15:20:00
  Status:       PENDING
  Total Amount: $59.99

Order #2:
  Order ID:     3
  Order Number: ORD-003
  Order Date:   2024-01-25T09:45:00
  Status:       PENDING
  Total Amount: $89.99

Order #3:
  Order ID:     4
  Order Number: ORD-004
  Order Date:   2024-01-10T16:20:00
  Status:       DELIVERED
  Total Amount: $199.99

==================================================
Total Orders: 3
==================================================


Assistant:

Here is the order history for customer AROUT:

**Order History (3 orders total):**
1. **ORD-008**
   - Total: $59.99
   - Status: PENDING
   - Date: January 30, 2024, 3:20 PM

2. **ORD-003**
   - Total: $89.99
   - Status: PENDING
   - Date: January 25, 2024, 9:45 AM

3. **ORD-004**
   - Total: $199.99
   - Status: DELIVERED
   - Date: January 10, 2024, 4:20 PM

Let me know if you'd like to investigate any specific order or need further assistance!

LangGraph Client: Customer + Finance

The real power is seen when combining a natural language query that works across BOTH the customer and finance tools.

Find all orders for franwilson@example.com

Where the use of the contact email address first requires a search of the customers to find the correct customer id which is then used to retrieve the orders from the finance tool.

python 6_langgraph_client_list_orders_for_franwilson.py
Base URL: http://llamastack-distribution-vllm-service:8321
Model:    vllm/qwen3-14b
Testing LLM connectivity...
LLM connectivity OK

==================================================
Finding orders for: franwilson@example.com
==================================================

==================================================
CUSTOMER INFORMATION
==================================================

Customer ID:   LONEP
Company Name:  Lonesome Pine Restaurant
Contact Name:  Fran Wilson
Contact Email: franwilson@example.com
==================================================

==================================================
ORDER HISTORY
==================================================

Order #1:
  Order ID:     2
  Order Number: ORD-002
  Order Date:   2024-01-20T14:15:00
  Status:       SHIPPED
  Total Amount: $149.5

Order #2:
  Order ID:     1
  Order Number: ORD-001
  Order Date:   2024-01-15T10:30:00
  Status:       DELIVERED
  Total Amount: $299.99

Order #3:
  Order ID:     6
  Order Number: ORD-006
  Order Date:   2024-01-05T08:30:00
  Status:       DELIVERED
  Total Amount: $399.99

==================================================
Total Orders: 3
==================================================


Assistant:

Here are all the orders for Fran Wilson (LONEP):

1. **Order #ORD-001**
   - Date: January 15, 2024
   - Status: DELIVERED
   - Total: $299.99

2. **Order #ORD-006**
   - Date: January 5, 2024
   - Status: DELIVERED
   - Total: $399.99

3. **Order #ORD-002**
   - Date: January 20, 2024
   - Status: SHIPPED
   - Total: $149.50

Let me know if you need details about a specific order!

Multi-step reasoning in action: The agent received a single request ("Find all orders for franwilson@example.com") and autonomously executed two tool calls: first search_customers to resolve the email to a customer ID (LONEP), then fetch_order_history with that ID. The agent planned this sequence on its own — you didn’t have to code the workflow.

python 7_langgraph_client_list_orders_any_customer.py liuwong@example.com
Base URL: http://llamastack-distribution-vllm-service:8321
Model:    vllm/qwen3-14b
Testing LLM connectivity...
LLM connectivity OK

==================================================
Finding orders for: liuwong@example.com
==================================================

==================================================
CUSTOMER INFORMATION
==================================================

Customer ID:   THECR
Company Name:  The Cracker Box
Contact Name:  Liu Wong
Contact Email: liuwong@example.com
==================================================

==================================================
ORDER HISTORY
==================================================

Order #1:
  Order ID:     7
  Order Number: ORD-007
  Order Date:   2024-01-28T13:45:00
  Status:       SHIPPED
  Total Amount: $129.99

Order #2:
  Order ID:     5
  Order Number: ORD-005
  Order Date:   2024-01-22T11:10:00
  Status:       CANCELLED
  Total Amount: $79.99

==================================================
Total Orders: 2
==================================================


Assistant:

Here are the orders for liuwong@example.com (customer ID THECR):

1. **Order #ORD-007**
   - Date: January 28, 2024
   - Status: ✅ SHIPPED
   - Total: $129.99

2. **Order #ORD-005**
   - Date: January 22, 2024
   - Status: ❌ CANCELLED
   - Total: $79.99

Let me know if you need details about a specific order!
python 8_langgraph_client_list_invoices_any_customer.py liuwong@example.com
Base URL: http://llamastack-distribution-vllm-service:8321
Model:    vllm/qwen3-14b
Testing LLM connectivity...
LLM connectivity OK

==================================================
Finding invoices for: liuwong@example.com
==================================================

==================================================
CUSTOMER INFORMATION
==================================================

Customer ID:   THECR
Company Name:  The Cracker Box
Contact Name:  Liu Wong
Contact Email: liuwong@example.com
==================================================

==================================================
INVOICE HISTORY
==================================================

Invoice #1:
  Invoice ID:     7
  Invoice Number: INV-007
  Invoice Date:   2024-01-28T13:50:00
  Status:         SENT
  Total Amount:   $129.99

Invoice #2:
  Invoice ID:     5
  Invoice Number: INV-005
  Invoice Date:   2024-01-22T11:15:00
  Status:         CANCELLED
  Total Amount:   $79.99

==================================================
Total Invoices: 2
==================================================


Assistant:

Here are the invoices for Liu Wong (The Cracker Box):

**Invoice History for THECR (The Cracker Box):**
1. **Invoice #INV-007**
   - Status: SENT
   - Amount: $129.99
   - Invoice Date: Jan 28, 2024
   - Due Date: Feb 28, 2024
   - No payment date recorded

2. **Invoice #INV-005**
   - Status: CANCELLED
   - Amount: $79.99
   - Invoice Date: Jan 22, 2024
   - Due Date: Feb 22, 2024
   - No payment date recorded

Would you like me to check details about the cancelled invoice or look up any other information?

The data comes from the Postgres databases behind the REST APIs behind the MCP Servers. If you would like to know which customers have orders or invoices you can go look in the following file:

ls $HOME/fantaco-redhat-one-2026/fantaco-finance-main/src/main/resources/data.sql

The LangGraph examples demonstrate agentic behavior and offers a different approach to building more complex, stateful agent systems with explicit control flow via graph-based orchestration.

Notice the key differences from direct tool invocation:

Agent reasoning

The agent analyzes the user’s request, determines which tools to use, plans the response format, and synthesizes a natural language answer.

Natural language interface

You provide conversational input like "Search for customer with email thomashardy@example.com" rather than specifying tool names and parameters programmatically.

Tool selection

The agent autonomously decides to call search_customers with the appropriate email parameter extracted from the user’s message.

Response synthesis

After receiving tool results, the agent generates a human-friendly response, formatting the data in a readable way and offering follow-up assistance.

This is the power of agentic AI - the LLM acts as an intelligent coordinator that understands user intent, selects appropriate tools, and presents results conversationally.

Summary

In this module, you explored 2 approaches to interacting with MCP tools:

Approach 1: Llama Stack Client (direct invocation)
  • Programmatic tool invocation where your code controls the workflow

  • Useful for deterministic workflows, testing, and rule-based integrations

  • Direct access to client.tool_runtime.invoke_tool()

  • No LLM reasoning - pure tool execution

Approach 2: LangGraph with Llama Stack (agent-driven)
  • Autonomous agent that reasons about tool selection

  • Natural language interface for user input

  • Agent extracts parameters, chains tools, and synthesizes responses

Key concepts demonstrated
  • Tool discovery: Agents can query available tools and their descriptions dynamically

  • MCP abstraction: Same tools work with both approaches - the MCP interface is consistent

  • Llama Stack flexibility: Support for native client SDK and third-party frameworks like LangGraph

  • Response API integration: Both approaches leverage Llama Stack’s Response API for streamlined inference

What you built
  • Python scripts that discover and inspect MCP tools

  • Direct tool invocation using Llama Stack Client

  • Agents using LangGraph that reason about customer and finance queries

You’ve now demonstrated complete end-to-end connectivity from natural language input through agent reasoning, tool invocation, MCP servers, backend APIs, and databases.

In the next module, you will dive into Llama Stack’s native agent capabilities.