MCP Claude AI Agents .NET 10 PostgreSQL

Teaching Claude to Query My Payment Database

AL
Azim Litanga
· April 8, 2025 · ⏱ 10 min read
The PayCore Series — Article 2 of 4 · logicdynamics.net

I asked Claude: "Which provider is performing best and why?"

It didn't guess. It didn't hallucinate. It called a tool, queried my live PostgreSQL database, and returned this:

Provider Performance Ranking: 🥇 Airtel — 21 events, 11 successes, 2 failures → ~69% success rate 🥈 M-PESA — 25 events, 15 successes, 3 failures → ~60% success rate 🥉 Stripe — 20 events, 11 successes, 3 failures → ~55% success rate 4️⃣ PayPal — 14 events, 7 successes, 2 failures → ~50% success rate Winner: Airtel Reason: Lowest failure count. Highest success ratio. Fewest stuck payments in 'processing' state. Watch-outs: PayPal has 4 payments in 'processing' — highest relative to size. Could signal timeout or webhook issues.

That's not a chatbot. That's an analyst with direct database access.

What is MCP?

Model Context Protocol (MCP) is an open standard that lets AI agents call external tools in a structured way. Instead of embedding data in a prompt, you expose tools — functions with descriptions — that the AI can call when it needs information.

The key insight: Claude doesn't know what's in your database. But if you give it a tool called GetPaymentSummary with a description that says "Get volume and status breakdown of recent payments", Claude knows when to call it — and what the result means.

"The description attribute is architecture. How you describe a tool determines when Claude calls it, what arguments it passes, and how it interprets the result."

Building PayCore.Mcp in .NET 10

The MCP server is a standard ASP.NET Core project with one NuGet package:

dotnet add package ModelContextProtocol.AspNetCore
// Program.cs — the entire setup
builder.Services.AddMcpServer()
    .WithHttpTransport()
    .WithTools<PayCoreTools>();

app.MapMcp("/mcp");

Three lines of setup. The rest is your tool implementations.

[McpServerToolType]
public class PayCoreTools
{
    [McpServerTool]
    [Description("Get a summary of payment volume and status breakdown. "
                + "Use this to answer: 'What is the total payment volume?', "
                + "'How many payments succeeded vs failed?'")]
    public async Task<string> GetPaymentSummary(
        [Description("Number of recent payments to include (1-500). Default 100.")]
        int count = 100)
    {
        var payments = await _intents.GetRecentAsync(count, null, null);
        var summary = new {
            TotalPayments    = payments.Count,
            ByStatus         = payments.GroupBy(p => p.Status)
                                       .ToDictionary(g => g.Key, g => g.Count()),
            VolumeByCurrency = payments
                .Where(p => p.Status == "captured")
                .GroupBy(p => p.Currency)
                .ToDictionary(g => g.Key, g => g.Sum(p => p.Amount))
        };
        return JsonSerializer.Serialize(summary, _jsonOptions);
    }
}

The four tools

ToolClaude can answer...
get_payment_summaryWhat is the total volume? How many payments failed vs succeeded?
get_provider_statsWhich provider is performing best? Compare Stripe vs M-PESA.
get_recent_eventsShow me the last 10 events. Which provider had failures today?
get_recent_paymentsShow all M-PESA transactions. Which payments are still processing?

Connecting Claude.ai: the ngrok journey

Claude.ai is a cloud service. Your development machine is not. Connecting them required ngrok — a tunneling tool that creates a public URL pointing at your local server.

The journey had three obstacles:

# Obstacle 1: Wrong URL
app.MapMcp()    # registers at '/' — Claude can't find it
app.MapMcp("/mcp")  # explicit path — works ✅

# Obstacle 2: CORS
# Claude.ai is a different origin — blocked by default
app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());

# Obstacle 3: ngrok version too old
ngrok update
ngrok http https://localhost:7070 --host-header="localhost:7070"
# → https://contusion-affirm-steadily.ngrok-free.dev/mcp ✅

What Claude found

Once connected, the first query was: "What is the total payment volume in PayCore?"

Total Volume by Currency (120 transactions): USD: $805,455 across 33 transactions KES: 747,832 across 26 transactions UGX: 578,653 across 19 transactions Status Breakdown: ✅ Captured: 78 (65% success rate) ⏳ Processing: 19 ❌ Failed: 23 Claude's follow-up: "The 23% failure rate is worth investigating. Want me to pull the failed transactions per provider?"

That last line is what separates an AI tool from an AI analyst. Claude didn't just answer the question — it identified the next question worth asking.

The bigger implication

Every .NET developer building a business system now has a straightforward path to making that system conversational. Add ModelContextProtocol.AspNetCore, write a few tool methods with good descriptions, expose an /mcp endpoint, and any Claude-connected interface can query your system in natural language.

This isn't the future of software. It's available today, in .NET 10, with a NuGet package and three lines of Program.cs.

← Previous
How I Built a Multi-Provider Payment Engine in .NET 10
Next →
From Zero to Docker: Containerizing a .NET Payment Platform
AL
Azim Litanga

Founder of LogicDynamics — .NET engineer, builder, and creator. Passionate about Blazor, Web APIs, AI, and shipping real software. Based in Oklahoma City.