Why I’m pivoting from “custom actions” to MCP servers + workflows

What that means for my QuickBooks custom action

The ecosystem is standardizing on the Model Context Protocol (MCP) to connect AI to real apps and data. Instead of wiring bespoke “custom actions” (like our QuickBooks action), we can expose a QuickBooks MCP server and plug it into any MCP-aware agent or workflow tool. That gives us portability, better tooling, and less glue code—while still letting us add guardrails where it matters. Our old custom QuickBooks action isn’t wrong, just increasingly redundant.

What changed
  • MCP became the “USB-C of AI apps.” It’s an open standard that lets AI assistants connect to tools, files, APIs, and workflows through a consistent interface. Multiple vendors (Anthropic, Microsoft, etc.) are rallying around it, so integrations we build once can be reused across agents and platforms. Model Context Protocol
  • Transport ≠ protocol. SSE is just one way to stream model output. MCP sits a layer above (JSON-RPC semantics, tools/resources/prompts), and can ride over SSE or other transports. So the “shift” isn’t really SSE → MCP; it’s custom, one-off integrations → standardized MCP servers. ni18 Blog
What this means for our QuickBooks integration
  • There are now off-the-shelf QuickBooks MCP servers and connectors (e.g., n8n, Knit, Zapier MCP) that expose full QBO operations as MCP tools. That means less custom code for auth, schema mapping, and error handling—and we can invoke the same tools from Claude, ChatGPT (where supported), desktop agents, or workflow engines.
  • Because MCP is portable, our integration isn’t tied to a single GPT/action definition. It’s easier to compose workflows (validation, approvals, posting, notifications) around QuickBooks and swap the front-end agent without rewiring everything.
So… is our custom QuickBooks action obsolete?
openapi: 3.1.0
info:
  title: QuickBooks Reports API
  description: Access live Profit and Loss and Transaction List data from your QuickBooks Online company using a custom GPT.
  version: 1.2.0
servers:
  - url: https://quickbooks.api.intuit.com/v3/company/xxxxxxxxxxxxxxxxx
paths:
  /reports/ProfitAndLoss:
    get:
      operationId: getProfitAndLoss
      summary: Retrieve a Profit and Loss (Income Statement) report.
      parameters:
        - name: start_date
          in: query
          required: false
          description: Start of reporting period (YYYY-MM-DD). Defaults to start of current week if not provided.
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          required: false
          description: End of reporting period (YYYY-MM-DD). Defaults to end of current week if not provided.
          schema:
            type: string
            format: date
        - name: accounting_method
          in: query
          required: false
          description: Choose 'Cash' or 'Accrual' method. Optional.
          schema:
            type: string
            enum: [Accrual, Cash]
      security:
        - oauth2: []
      responses:
        '200':
          description: Profit and Loss report returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProfitAndLossResponse'

  /reports/TransactionList:
    get:
      operationId: getTransactionList
      summary: Retrieve a list of transactions, including payees, for a given date range.
      parameters:
        - name: start_date
          in: query
          required: false
          description: Start of reporting period (YYYY-MM-DD). Defaults to start of current week if not provided.
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          required: false
          description: End of reporting period (YYYY-MM-DD). Defaults to end of current week if not provided.
          schema:
            type: string
            format: date
        - name: accounting_method
          in: query
          required: false
          description: Choose 'Cash' or 'Accrual' method. Optional.
          schema:
            type: string
            enum: [Accrual, Cash]
      security:
        - oauth2: []
      responses:
        '200':
          description: Transaction list returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionListResponse'

components:
  securitySchemes:
    oauth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://appcenter.intuit.com/connect/oauth2
          tokenUrl: https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer
          scopes:
            com.intuit.quickbooks.accounting: Access accounting data
  schemas:
    ProfitAndLossResponse:
      type: object
      properties:
        Header:
          type: object
          properties:
            ReportName:
              type: string
            StartPeriod:
              type: string
            EndPeriod:
              type: string
        Rows:
          type: object
          properties:
            Row:
              type: array
              items:
                type: object
                properties:
                  Header:
                    type: object
                    properties:
                      ColData:
                        type: array
                        items:
                          type: object
                          properties:
                            value:
                              type: string
                  Summary:
                    type: object
                    properties:
                      ColData:
                        type: array
                        items:
                          type: object
                          properties:
                            value:
                              type: string

    TransactionListResponse:
      type: object
      properties:
        Header:
          type: object
          properties:
            ReportName:
              type: string
            StartPeriod:
              type: string
            EndPeriod:
              type: string
        Rows:
          type: object
          properties:
            Row:
              type: array
              items:
                type: object
                properties:
                  ColData:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        value:
                          type: string

Not entirely, but it’s less strategic now:

  • Keep it if you need very specific business logic, custom validations, or you’re stuck on a non-MCP platform.
  • Otherwise, prefer an MCP server + workflow: you’ll gain reuse, observability, and easier maintenance—even if we still wrap certain calls with policy checks.
Share this post:
subscribe
Join our community and get updated every week We have a lot more just for you!