Skip to main content
Glama

Creative Graph

I needed one write path for a UI and an agent. The React console and the MCP server both post to /graphql. Neither opens SQLite on its own. Creatives are CTV, video, audio, or DOOH. Budgets are integer cents.

I made this for a developer-ecosystem interview. Bad writes fail closed. Deploy is a Dockerfile, not a slide.

Why one schema

GraphiQL, the React console, and MCP tools all send createCreative, creatives, and creative. If a field is wrong, every client is wrong the same way. The MCP server does not open SQLite. It posts to /graphql. An agent with a raw database handle would skip the cents checks, the typed NOT_FOUND errors, and the SQL CHECK constraints. I did not give it that option.

Related MCP server: Google Ads MCP Unofficial

Why cents

$1,200 is twelve hundred dollars, 120000 cents. GraphQL Int will not take $1,200 or 12.5. The console and MCP convert dollars first. The resolver still checks Number.isInteger. A float, extra fraction digits, or a junk string throws. A missing creative id on updateCreative returns extensions.code: NOT_FOUND and data: null. The field is Creative!, so the write cannot succeed as a silent null.

5-minute demo

Needs Node 22+. I checked this repo on Node 25. node:sqlite is built in, so there is no native addon.

cp .env.example .env
npm install
npm test
npm run dev
  1. Open http://127.0.0.1:5173. If that port is already taken, Vite prints the next one. You should see the seeded Spring CTV / audio pack. A 15s CTV bumper at 50000 cents, and a 30s podcast read in DRAFT at 25000 cents.

  2. Leave the budget as $1,200. The helper text should read Sends budgetCents: 120000. Click Create. A new DRAFT creative appears.

  3. The console has two buttons for the failure cases, because walking someone through GraphiQL mid-interview is a bad demo. Click Update missing ID. The banner should show Creative cre_does_not_exist was not found [NOT_FOUND].

  4. Click Send "$1,200" as GraphQL Int. GraphQL rejects the string before a row is written.

  5. Open http://127.0.0.1:4000/graphql and run:

query {
  campaign(id: "cmp_demo") {
    name
    creatives { id name format budgetCents }
  }
}

The schema file is schema.graphql. Tables live in src/db/migrations/001_init.sql.

How to run

What

Command

Where

API + React

npm run dev

GraphQL at :4000/graphql. UI at :5173, which proxies /graphql

API only

npm run dev:api

:4000

Built UI served by the API

npm run build && npm start

UI and GraphQL both on :4000

Tests

npm test

schema, cents, bad-id writes, MCP input

MCP stdio

npm run mcp

talks to GRAPHQL_URL

MCP tool list

npm run mcp:list

prints list_creatives, get_creative, create_creative

The API must be up before MCP tools do anything useful. I left it that way so MCP cannot bypass GraphQL.

MCP in Cursor or Claude

npm run mcp:list should print three tools. Then add the server.

Cursor. Put this in ~/.cursor/mcp.json or the project .cursor/mcp.json.

{
  "mcpServers": {
    "creative-graph": {
      "command": "npx",
      "args": ["tsx", "src/mcp/index.ts"],
      "cwd": "/ABS/PATH/TO/creative-graph",
      "env": {
        "GRAPHQL_URL": "http://127.0.0.1:4000/graphql"
      }
    }
  }
}

Claude Desktop uses the same command, args, cwd, and env under mcpServers in claude_desktop_config.json.

Tool inputs are Zod-checked before the HTTP call.

  • list_creatives optional campaignId, format

  • get_creative requires id

  • create_creative requires campaignId, name, format, durationMs, and exactly one of budgetCents or budget. "$1,200" is valid for budget.

Deploy

This repo is not claiming a live URL. There is no Railway, Fly, or Wrangler CLI on the machine that built it.

Railway, one service, Dockerfile.

  1. Push the repo to GitHub.

  2. New Railway project from that repo. Builder reads Dockerfile / railway.toml.

  3. Set PORT=4000 if the platform does not inject it. DATABASE_PATH=/app/data/creative-graph.sqlite is already in the image.

  4. Health check is GET /health.

  5. After deploy, GraphQL is https://<host>/graphql and the console is https://<host>/.

Fly.io uses the same image. Run fly launch then fly deploy. SQLite in the container is ephemeral unless you mount a volume on /app/data.

No secrets are required for the demo. Copy .env.example. Do not commit .env.

What this is not

I skipped Go and Rails. The console is one form and a table, so I did not add Redux. I used TypeScript so GraphQL Yoga and the MCP SDK live in the same repo I run on a laptop. Multi-region ad serving and frequency caps are not here. I did not model how bids get priced. Those are honest gaps against a StackAdapt Developer Ecosystem posting. What that posting actually wants is a public API as the contract. Agents use the same write path as the UI.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables management of OpenAI Ads campaigns, ad groups, ads, and insights through MCP, with approval-gated live mutations for safe write operations.
    54
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables management of Google Ads accounts via MCP, providing read and write tools for campaigns, ad groups, keywords, assets, and more, with support for reporting and mutations.
    -
  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that enables safe, audited mutation of Google Ads campaigns—creating ads, ad groups, keywords, and assets or adjusting budgets and statuses—with a dry-run default and an optional guarded remove operation, plus read-only Keyword Planner ideas.
    22
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JohnCarl-30/creative-graph'

If you have feedback or need assistance with the MCP directory API, please join our Discord server