{"slug": "build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp", "title": "Build a Small-Business Invoice Agent with LangChain Deep Agents and MCP", "summary": "A developer published a walkthrough for building a multi-agent invoice-triage system using LangChain's Deep Agents framework and the Model Context Protocol (MCP). The architecture pairs an orchestrator that plans and maintains a todo list with extractor and reviewer subagents, all accessing vendor lookups, invoice history, duplicate checks and file reading through a FastMCP server backed by SQLite, with path-traversal protection on file reads. The author notes the design is an extensible reference rather than a production system and advises pinning versions of deepagents, langchain-mcp-adapters and mcp.", "body_md": "Most \"AI agent\" tutorials stop at a chatbot that calls a weather API. Real small-business work looks different: a stack of PDFs in an inbox, a spreadsheet of vendors, and a person who has to decide what gets paid. In this post we'll build a small but realistic multi-agent system that triages incoming invoices, using two pieces that fit together well:\n\nThe goal isn't a production system in 1,200 words. It's a clean architecture you can extend. Library APIs move fast, so pin your versions and check the docs for `deepagents`, `langchain-mcp-adapters` and `mcp` before you copy this into anything serious.\n\nA small company receives vendor invoices by email. For each one, someone has to:\n\nNone of this needs a model to \"be creative\". It needs reliable extraction, a few lookups, and a clear audit trail. That's a good fit for agents with narrow tools.\n\n```\n            ┌──────────────────────────┐\n            │   Orchestrator (Deep     │\n            │   Agent: plans + todo)   │\n            └─────┬───────────┬────────┘\n                  │           │\n        ┌─────────▼──┐   ┌────▼─────────┐\n        │ extractor  │   │  reviewer    │\n        │ subagent   │   │  subagent    │\n        └─────┬──────┘   └────┬─────────┘\n              │  MCP tools    │\n        ┌─────▼───────────────▼────────┐\n        │ MCP server: vendors, invoice │\n        │ history, file reader         │\n        └──────────────────────────────┘\n```\n\nThe orchestrator never touches the database directly. Everything goes through MCP tools, which means the same server can later serve a different agent framework, a desktop assistant, or an internal app.\n\nInstall the dependencies:\n\n```\npip install \"mcp[cli]\" langchain-mcp-adapters deepagents langchain-openai\n```\n\nHere's a minimal MCP server using `FastMCP`. For the demo, the \"database\" is SQLite.\n\n``` python\n# invoice_server.py\nimport sqlite3\nfrom pathlib import Path\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"invoices\")\nDB = Path(\"business.db\")\n\ndef q(sql: str, params: tuple = ()):\n    with sqlite3.connect(DB) as conn:\n        conn.row_factory = sqlite3.Row\n        return [dict(r) for r in conn.execute(sql, params).fetchall()]\n\n@mcp.tool()\ndef read_invoice_text(filename: str) -> str:\n    \"\"\"Return the extracted text of an invoice file in the inbox folder.\"\"\"\n    path = Path(\"inbox\") / Path(filename).name  # prevent path traversal\n    return path.read_text()\n\n@mcp.tool()\ndef lookup_vendor(name: str) -> list[dict]:\n    \"\"\"Find known vendors whose name matches (case-insensitive).\"\"\"\n    return q(\"SELECT id, name, payment_terms FROM vendors WHERE name LIKE ?\",\n             (f\"%{name}%\",))\n\n@mcp.tool()\ndef invoice_history(vendor_id: int) -> list[dict]:\n    \"\"\"Past invoices for a vendor: number, date, total.\"\"\"\n    return q(\"SELECT number, date, total FROM invoices WHERE vendor_id = ? \"\n             \"ORDER BY date DESC LIMIT 24\", (vendor_id,))\n\n@mcp.tool()\ndef is_duplicate(vendor_id: int, invoice_number: str) -> bool:\n    \"\"\"True if this vendor already has an invoice with this number.\"\"\"\n    return bool(q(\"SELECT 1 FROM invoices WHERE vendor_id = ? AND number = ?\",\n                  (vendor_id, invoice_number)))\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n```\n\nA few deliberate choices:\n\n`Path(filename).name` stops the model (or a malicious PDF) from reading files outside the inbox.\nYou can poke at it with the MCP inspector: `mcp dev invoice_server.py`.\n\n`langchain-mcp-adapters` turns MCP tools into regular LangChain tools:\n\n``` python\nfrom langchain_mcp_adapters.client import MultiServerMCPClient\n\nclient = MultiServerMCPClient({\n    \"invoices\": {\n        \"command\": \"python\",\n        \"args\": [\"invoice_server.py\"],\n        \"transport\": \"stdio\",\n    }\n})\n\ntools = await client.get_tools()  # inside an async function\nby_name = {t.name: t for t in tools}\n```\n\nSplitting by name matters. Each subagent should only see the tools it needs.\n\nDeep Agents gives you a planning tool (a todo list), a virtual file system for scratch notes, and the ability to spawn subagents with their own prompt and toolset. Subagents keep the orchestrator's context clean: the extractor can wade through a messy invoice without dumping all of that text into the main conversation.\n\n```\nextractor = {\n    \"name\": \"extractor\",\n    \"description\": \"Reads one invoice file and returns structured JSON.\",\n    \"system_prompt\": (\n        \"You extract invoice data. Call read_invoice_text, then return ONLY JSON \"\n        \"with keys: vendor_name, invoice_number, date (YYYY-MM-DD), \"\n        \"line_items (list of {description, amount}), total. \"\n        \"If a field is missing, use null. Never guess numbers.\"\n    ),\n    \"tools\": [by_name[\"read_invoice_text\"]],\n}\n\nreviewer = {\n    \"name\": \"reviewer\",\n    \"description\": \"Checks extracted invoice JSON against vendor records.\",\n    \"system_prompt\": (\n        \"You review invoices. Given extracted JSON: look up the vendor, check for \"\n        \"duplicates, and compare the total to that vendor's history. Report \"\n        \"flags: UNKNOWN_VENDOR, DUPLICATE, TOTAL_MISMATCH (line items don't sum), \"\n        \"UNUSUAL_AMOUNT (well above typical). Explain each flag in one sentence.\"\n    ),\n    \"tools\": [by_name[\"lookup_vendor\"], by_name[\"invoice_history\"],\n              by_name[\"is_duplicate\"]],\n}\n```\n\nNote what's *not* delegated to the model: the definition of each flag. You decide the business rules. The model applies them and explains its reasoning.\n\n``` python\nimport asyncio\nfrom deepagents import create_deep_agent\n\nORCHESTRATOR_PROMPT = \"\"\"You triage vendor invoices for a small business.\nFor each file you are given:\n1. Plan the work with your todo list.\n2. Delegate extraction to the `extractor` subagent.\n3. Pass its JSON to the `reviewer` subagent.\n4. Write a summary per invoice: vendor, number, total, flags, and a\n   recommendation of APPROVE or NEEDS_HUMAN_REVIEW.\nYou never approve payments yourself.\"\"\"\n\nasync def main(files: list[str]):\n    agent = create_deep_agent(\n        model=\"openai:gpt-4.1\",      # any tool-calling model works\n        tools=[],                      # orchestrator delegates everything\n        system_prompt=ORCHESTRATOR_PROMPT,\n        subagents=[extractor, reviewer],\n    )\n    result = await agent.ainvoke({\n        \"messages\": [{\"role\": \"user\",\n                      \"content\": f\"Triage these invoices: {', '.join(files)}\"}]\n    })\n    print(result[\"messages\"][-1].content)\n\nasyncio.run(main([\"acme-1043.txt\", \"newco-0007.txt\"]))\n```\n\n(In real code, create the MCP client and subagent dicts inside `main` so `await client.get_tools()` runs in the same event loop.)\n\nThe demo works. Here's what turns it into something a business can actually rely on:\n\n**Validate structured output.** Don't trust the extractor's JSON. Parse it with Pydantic and send it back for a retry on failure:\n\n``` python\nfrom pydantic import BaseModel, model_validator\n\nclass LineItem(BaseModel):\n    description: str\n    amount: float\n\nclass Invoice(BaseModel):\n    vendor_name: str\n    invoice_number: str\n    date: str\n    line_items: list[LineItem]\n    total: float\n\n    @model_validator(mode=\"after\")\n    def totals_match(self):\n        if abs(sum(i.amount for i in self.line_items) - self.total) > 0.01:\n            raise ValueError(\"line items do not sum to total\")\n        return self\n```\n\nArithmetic checks belong in code, not in a prompt.\n\n**Keep a human in the loop.** The agent recommends, a person approves. Deep Agents supports interrupting before sensitive tool calls if you later add write tools like `mark_approved`.\n\n**Log everything.** Store every tool call and result. When the owner asks \"why was this flagged?\", you can show them.\n\n**Evaluate with real samples.** Gather 20–30 anonymized past invoices with known correct answers, and re-run them every time you change a prompt or model. That's your regression suite.\n\n**Treat documents as untrusted input.** An invoice can contain text like \"ignore previous instructions\". Read-only tools and narrow subagent permissions limit what a prompt injection can do.\n\nSwap \"invoices\" for insurance claims intake, real estate transaction documents, or permit applications, and the shape is the same: an MCP server wrapping your systems, an extractor, a reviewer that applies your rules, and an orchestrator that writes a decision-ready summary. That's the core of most [intelligent document processing](https://www.businessedgeanalytics.com/services/intelligent-document-processing.html) work. The hard parts are the business rules and the evaluation set, not the framework.\n\nIf you're deciding whether to build something like this or buy a SaaS tool, I wrote a longer comparison of [custom AI vs. off-the-shelf solutions](https://www.businessedgeanalytics.com/blog/custom-ai-vs-off-the-shelf.html). For more workflow ideas beyond invoices, see [5 AI workflow automations every business needs](https://www.businessedgeanalytics.com/blog/ai-workflow-automations.html).\n\n*Jimmy Hernandez is a senior AI engineer and founder of [Business Edge Analytics](https://www.businessedgeanalytics.com), a Louisville, KY firm that builds multi-agent systems, RAG assistants and document automation for businesses and public-sector teams. He works with LangChain Deep Agents, MCP and Python, and has a background in insurance and real estate.*", "url": "https://wpnews.pro/news/build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp", "canonical_source": "https://dev.to/businessedgeanalytics/build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp-ggp", "published_at": "2026-11-04 03:03:01+00:00", "updated_at": "2026-11-04 03:22:29.418673+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "developer-tools", "large-language-models"], "entities": ["LangChain", "Deep Agents", "Model Context Protocol", "FastMCP", "langchain-mcp-adapters", "SQLite"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp", "markdown": "https://wpnews.pro/news/build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp.md", "text": "https://wpnews.pro/news/build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp.txt", "jsonld": "https://wpnews.pro/news/build-a-small-business-invoice-agent-with-langchain-deep-agents-and-mcp.jsonld"}}