{"slug": "i-gave-an-ai-agent-the-keys-to-a-live-production-app-here-s-the-mcp-setup", "title": "I gave an AI agent the keys to a live production app: here's the MCP setup", "summary": "GoodBarber, an app platform, has deployed a hosted remote MCP server that lets AI agents operate live production apps, including scheduling push notifications and publishing articles. The setup uses OAuth for authentication and returns policy blocks on every write to enforce verification, with the server card documenting the full tool inventory. The engineer behind the project shared the hands-on configuration for clients like Claude Code, ChatGPT, and Codex.", "body_md": "AI agents are good at writing code. What they mostly cannot do is operate the software you already run in production: publish the article, schedule the push, fulfill the order. Not because the models are incapable, but because most production apps expose no structured surface an agent can act on.\n\nMCP fixes exactly that. This post is the hands-on setup I use to let an agent operate a live mobile app: real endpoint, real tool calls, real payloads, and the gotchas I hit.\n\nDisclosure up front: I run engineering at GoodBarber, an app platform, so the production app in this post runs on our MCP server. The patterns transfer to any remote MCP server you point an agent at.\n\nThe server is a hosted remote MCP server. No `npx`\n\n, nothing local to run:\n\n```\nhttps://mcp.goodbarber.dev/mcp/sse\n```\n\nThe `/sse`\n\npath is historical; the server answers both SSE and Streamable HTTP, so every current client works.\n\n**Claude Code:**\n\n```\nclaude mcp add --transport sse goodbarber https://mcp.goodbarber.dev/mcp/sse\n```\n\nor in `.mcp.json`\n\n:\n\n```\n{\n  \"mcpServers\": {\n    \"goodbarber\": {\n      \"type\": \"sse\",\n      \"url\": \"https://mcp.goodbarber.dev/mcp/sse\"\n    }\n  }\n}\n```\n\n**Claude (claude.ai):** Settings, then Connectors, then add a custom connector with that URL.\n\n**ChatGPT:** Settings, then Apps & Connectors, enable Developer mode, create a connector with the same URL. Write actions worked on a free account when I tested it; OpenAI's docs gate some of this by plan, so verify on yours.\n\n**Codex:** add a custom MCP server, transport Streamable HTTP, leave the bearer token field empty. Saving opens the OAuth flow.\n\nThe first tool use triggers OAuth in the browser: you sign in with the app's account, and the session is scoped to that single app. No API key to paste anywhere. That scoping does a lot of security work later.\n\nAfter OAuth, the client pulls the tool list. What the agent sees is not \"the API\": it is an operations menu, namespaced by domain. The `cms_`\n\ntools cover articles, events, and media. The `shop_`\n\ntools cover products, variants, orders, and promo codes. The `classic_`\n\ntools cover push, analytics, and memberships. The full inventory is public in the server card, which is the file to read before writing any client code:\n\n```\nhttps://mcp.goodbarber.dev/.well-known/mcp/server-card.json\n```\n\nPush is the scariest operation to hand an agent (a sent push has no undo), which makes it the best test of a server's design.\n\nMe, in the chat:\n\nSchedule a push for 6 PM tonight: \"Doors open at 7. First 50 people get the poster.\"\n\nThe agent calls `classic_create_push_broadcast`\n\n:\n\n```\n{\n  \"message\": \"Doors open at 7. First 50 people get the poster.\",\n  \"send\": \"at\",\n  \"send_at\": \"2026-07-29T18:00+02:00\"\n}\n```\n\nThree details in this schema are worth noticing:\n\n`message`\n\nis capped at 255 characters server-side, so the agent gets a hard error instead of a silently truncated push.`send`\n\nis an enum, `now`\n\nor `at`\n\n; scheduling requires the timezone-aware `send_at`\n\n, and the tool handles the UTC conversion. The agent does no date math.`action_type`\n\nis one of `open_app`\n\n, `external_link`\n\n, or `section`\n\n. \"Open the tickets section\" resolves to a real section id, not a guessed deeplink.The result comes back confirming the scheduled send, along with something rarer: a policy block. Every write on this server returns `_mcp_policy.verification_required: true`\n\n. The server's own guidance tells the agent to read back what it just wrote before declaring success. There is even a meta tool for this: `meta_get_tool_plan`\n\ntakes a tool name and returns the recommended discover, call, verify sequence plus the failure policy. Agents follow instructions embedded in tool results remarkably well; putting that discipline server-side beats hoping every client prompt remembers it.\n\n**Publish an article (CMS).** `cms_create_article`\n\nwants a title and category ids, and category ids come from `cms_list_cms_sections`\n\nfirst. Discover, then write:\n\n```\n{\n  \"title\": \"Matchday guide: what to know before Saturday\",\n  \"categories\": [4821],\n  \"status\": \"stock\",\n  \"publishedDate\": \"2026-08-01T08:00:00+02:00\"\n}\n```\n\n`status`\n\nis `published`\n\n, `draft`\n\n, or `stock`\n\n; a future `publishedDate`\n\nrequires `stock`\n\n, which is the scheduled state. Body content is its own resource: `cms_create_article_paragraph`\n\n, one call per paragraph, with `cms_reorder_article_paragraphs`\n\nwhen the agent restructures. There is also an `accessTier`\n\nfield (`free`\n\nor `premium`\n\n) that hooks straight into the app's paywall.\n\n**Add a product and a variant (shop).** `shop_create_product`\n\nfirst:\n\n```\n{\n  \"title\": \"Home Kit Hoodie 2026\",\n  \"status\": \"DRAFT\",\n  \"collections\": [312]\n}\n```\n\nthen `shop_create_variant`\n\n:\n\n```\n{\n  \"product_id\": 88410,\n  \"price\": \"49.00000\",\n  \"stock\": 120,\n  \"sku\": \"HK26-M\",\n  \"option_values\": [{ \"option_id\": 17, \"value\": \"M\" }]\n}\n```\n\nTwo things I like here. `price`\n\nis a decimal string, not a float: whoever wrote this schema has met floating-point money. And the variant model is strict: all variants of a product must share the exact same set of `option_id`\n\ns, so introducing a Size option on one variant forces you to define it on all of them. That is a real invariant of the commerce domain, enforced at the tool layer. A raw database connection would let your agent violate it silently.\n\n**Look up and update an order (fulfillment).** `shop_list_orders`\n\nfilters by status and date range. `shop_update_order_shipping`\n\nmoves an order along a one-way state machine, PENDING → FULFILLED → DELIVERED, with optional tracking:\n\n```\n{\n  \"order_id\": 55231,\n  \"status\": \"FULFILLED\",\n  \"shipping_tracking_num\": \"6A0301234567\",\n  \"shipping_tracking_url\": \"https://tracking.example.com/6A0301234567\"\n}\n```\n\nThe `status`\n\nenum only contains `FULFILLED`\n\nand `DELIVERED`\n\n. You cannot un-deliver an order through this surface, however confused the agent gets.\n\n**Pull the numbers (analytics).** `classic_list_downloads`\n\nand `classic_list_page_views`\n\ntake plain ISO date ranges and return aggregates. The stats family is read-only by construction; there is nothing to break. My standing Monday ask is one sentence: last week's downloads and page views versus the previous week, flag anything odd.\n\nThis is what I would look at before connecting an agent to anything in production:\n\n`shop_`\n\nnamespace is simply absent; push not configured, no push tools. The agent cannot call what it cannot see. Corollary if you write client code: never hardcode the tool list, read it at connect time or from the server card.`_mcp_policy`\n\nblock rides on every write result.On top of the server, there is an open-source repo of 44 Skills: markdown recipes in the Claude Skills format, one per workflow (create a product with variants, schedule a push campaign, process the morning's orders, and so on). They encode the discover-then-write sequences above so the agent does not rediscover them every session:\n\n```\nhttps://github.com/goodbarber/goodbarber-skills\n```\n\nThe terms allow rebranding and redistribution; they were written for resellers. The server itself is proprietary. The recipes are the open part.\n\nThings I would want to know before recommending this to another engineer:\n\n`shop_create_product`\n\ncalls. Bulk import stays a back-office job; the agent shines on the daily delta, not the migration.`cms_list_cms_sections`\n\n, `shop_list_products`\n\n); `meta_get_tool_plan`\n\nreturns exactly that sequence per tool.`send: \"at\"`\n\nrather than `now`\n\n, so there is always a review window between the ask and the broadcast.Everyone is racing to make agents build software. The quieter and, I think, more useful shift is agents operating the software you already have. Building is a one-time event. Operating is every day.\n\nIf you want to poke at a live implementation: the [server card](https://mcp.goodbarber.dev/.well-known/mcp/server-card.json) is public, the [Skills repo](https://github.com/goodbarber/goodbarber-skills) is open, and the non-dev version of this story lives at [goodbarber.com/mcp](https://www.goodbarber.com/mcp/).", "url": "https://wpnews.pro/news/i-gave-an-ai-agent-the-keys-to-a-live-production-app-here-s-the-mcp-setup", "canonical_source": "https://dev.to/goodbarber/i-gave-an-ai-agent-the-keys-to-a-live-production-app-heres-the-mcp-setup-27e", "published_at": "2026-07-31 13:21:47+00:00", "updated_at": "2026-07-31 13:34:36.442925+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-infrastructure"], "entities": ["GoodBarber", "MCP", "Claude Code", "ChatGPT", "Codex"], "alternates": {"html": "https://wpnews.pro/news/i-gave-an-ai-agent-the-keys-to-a-live-production-app-here-s-the-mcp-setup", "markdown": "https://wpnews.pro/news/i-gave-an-ai-agent-the-keys-to-a-live-production-app-here-s-the-mcp-setup.md", "text": "https://wpnews.pro/news/i-gave-an-ai-agent-the-keys-to-a-live-production-app-here-s-the-mcp-setup.txt", "jsonld": "https://wpnews.pro/news/i-gave-an-ai-agent-the-keys-to-a-live-production-app-here-s-the-mcp-setup.jsonld"}}