# I gave an AI agent the keys to a live production app: here's the MCP setup

> Source: <https://dev.to/goodbarber/i-gave-an-ai-agent-the-keys-to-a-live-production-app-heres-the-mcp-setup-27e>
> Published: 2026-07-31 13:21:47+00:00

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.

MCP 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.

Disclosure 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.

The server is a hosted remote MCP server. No `npx`

, nothing local to run:

```
https://mcp.goodbarber.dev/mcp/sse
```

The `/sse`

path is historical; the server answers both SSE and Streamable HTTP, so every current client works.

**Claude Code:**

```
claude mcp add --transport sse goodbarber https://mcp.goodbarber.dev/mcp/sse
```

or in `.mcp.json`

:

```
{
  "mcpServers": {
    "goodbarber": {
      "type": "sse",
      "url": "https://mcp.goodbarber.dev/mcp/sse"
    }
  }
}
```

**Claude (claude.ai):** Settings, then Connectors, then add a custom connector with that URL.

**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.

**Codex:** add a custom MCP server, transport Streamable HTTP, leave the bearer token field empty. Saving opens the OAuth flow.

The 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.

After 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_`

tools cover articles, events, and media. The `shop_`

tools cover products, variants, orders, and promo codes. The `classic_`

tools 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:

```
https://mcp.goodbarber.dev/.well-known/mcp/server-card.json
```

Push 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.

Me, in the chat:

Schedule a push for 6 PM tonight: "Doors open at 7. First 50 people get the poster."

The agent calls `classic_create_push_broadcast`

:

```
{
  "message": "Doors open at 7. First 50 people get the poster.",
  "send": "at",
  "send_at": "2026-07-29T18:00+02:00"
}
```

Three details in this schema are worth noticing:

`message`

is capped at 255 characters server-side, so the agent gets a hard error instead of a silently truncated push.`send`

is an enum, `now`

or `at`

; scheduling requires the timezone-aware `send_at`

, and the tool handles the UTC conversion. The agent does no date math.`action_type`

is one of `open_app`

, `external_link`

, or `section`

. "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`

. 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`

takes 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.

**Publish an article (CMS).** `cms_create_article`

wants a title and category ids, and category ids come from `cms_list_cms_sections`

first. Discover, then write:

```
{
  "title": "Matchday guide: what to know before Saturday",
  "categories": [4821],
  "status": "stock",
  "publishedDate": "2026-08-01T08:00:00+02:00"
}
```

`status`

is `published`

, `draft`

, or `stock`

; a future `publishedDate`

requires `stock`

, which is the scheduled state. Body content is its own resource: `cms_create_article_paragraph`

, one call per paragraph, with `cms_reorder_article_paragraphs`

when the agent restructures. There is also an `accessTier`

field (`free`

or `premium`

) that hooks straight into the app's paywall.

**Add a product and a variant (shop).** `shop_create_product`

first:

```
{
  "title": "Home Kit Hoodie 2026",
  "status": "DRAFT",
  "collections": [312]
}
```

then `shop_create_variant`

:

```
{
  "product_id": 88410,
  "price": "49.00000",
  "stock": 120,
  "sku": "HK26-M",
  "option_values": [{ "option_id": 17, "value": "M" }]
}
```

Two things I like here. `price`

is 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`

s, 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.

**Look up and update an order (fulfillment).** `shop_list_orders`

filters by status and date range. `shop_update_order_shipping`

moves an order along a one-way state machine, PENDING → FULFILLED → DELIVERED, with optional tracking:

```
{
  "order_id": 55231,
  "status": "FULFILLED",
  "shipping_tracking_num": "6A0301234567",
  "shipping_tracking_url": "https://tracking.example.com/6A0301234567"
}
```

The `status`

enum only contains `FULFILLED`

and `DELIVERED`

. You cannot un-deliver an order through this surface, however confused the agent gets.

**Pull the numbers (analytics).** `classic_list_downloads`

and `classic_list_page_views`

take 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.

This is what I would look at before connecting an agent to anything in production:

`shop_`

namespace 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`

block 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:

```
https://github.com/goodbarber/goodbarber-skills
```

The terms allow rebranding and redistribution; they were written for resellers. The server itself is proprietary. The recipes are the open part.

Things I would want to know before recommending this to another engineer:

`shop_create_product`

calls. Bulk import stays a back-office job; the agent shines on the daily delta, not the migration.`cms_list_cms_sections`

, `shop_list_products`

); `meta_get_tool_plan`

returns exactly that sequence per tool.`send: "at"`

rather than `now`

, 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.

If 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/).
