In the last two posts I built LDBD bots with Claude Desktop and a local Gemma model. Both of them ultimately required my laptop to be on.
This time I went the other way: ChatGPT would submit predictions to LDBD straight from OpenAI's cloud, without my laptop in the loop.
With Claude Desktop, registering npx mcp-ldbd was enough — it runs locally, so stdio MCP works without much ceremony. ChatGPT is different. It can't see my laptop's localhost.
So I added a public HTTP MCP endpoint to LDBD — https://ldbd.app/mcp. I thought adding one route would be the whole job. The actual code change was small too. But getting that route to play nicely with ChatGPT took a full day.
The i18n middleware swallowed the route, ChatGPT classified even read-only tools like stats as “destructive,” and then Vercel function timeouts and cache behavior joined the party. This post is the debugging log from that day.
One caveat up front — what I actually verified here is that ChatGPT can read from and write to LDBD tools. I haven't separately confirmed running this on a daily schedule yet.
You need two things:
- A ChatGPT plan that lets you register a custom MCP connector
- An LDBD account
Availability can depend on your plan, region, and when you're reading this, so the fastest sanity check is to look for “Developer Mode” in your settings first. Add a day of debugging patience to that and you're set.
Comparing the three approaches in this series
| Approach | LLM location | MCP type | Laptop needed? |
|---|---|---|---|
| Gemma + Ollama/MLX | Local | None (direct API) | Yes (24/7) |
| Claude Desktop + mcp-ldbd | Local | stdio | Yes (only at run time) |
| ChatGPT + remote MCP | Cloud | HTTP | No |
The appeal of the third row is obvious: ChatGPT can call LDBD on a schedule while I'm asleep. But to get there, LDBD needed an HTTP route ready first.
Step 1. (operator side) Building the HTTP MCP route
This section is for someone bolting a ChatGPT connector onto their own service. If you're using LDBD, the route is already deployed, so jumping to Step 2 is fine.
Before writing anything, two decisions had to be made.
- New domain vs. existing route — I considered spinning up an
mcp.ldbd.appsubdomain or splitting it into a separate npm package. In the end I just added a Next.js route atldbd.app/mcp. It was the fastest path because it required no extra deployment infrastructure. - MCP SDK dependency vs. handwritten JSON-RPC — with five tools and a handful of JSON-RPC methods, writing it by hand was simpler than pulling in an SDK, both in code and in mental overhead.
app/mcp/route.tsended up around 110 lines andlib/mcp/tools.tsaround 150, with zero new dependencies.
The JSON-RPC methods the route handles are initialize, tools/list, tools/call, ping, and notifications/*. The tool definitions expose the same five tools as the stdio version (the mcp-ldbd npm package) — search, get_asset, get_my_stats, list_open, submit_prediction. The user's LDBD API key arrives in the Authorization: Bearer ... header and is forwarded straight through to LDBD's internal API.
Step 2. (user side) Registering LDBD in the ChatGPT Connector
This part is a step-by-step guide and runs a bit long. If you're more interested in the debugging pitfalls, you can jump straight to Step 3.
First, in /settings on LDBD, create an identity with type=AI Bot and issue an API key — same drill as in the Claude Desktop and Gemma bot posts. The actual key string is shown exactly once, so copy it somewhere safe right away. Once that's ready, switch over to ChatGPT.
2-1. Turn on Developer Mode and create an app
In ChatGPT, open Settings → Apps and switch on Developer Mode. As soon as the toggle flips, a “Create app” button shows up on the same screen — that's the entry point.
2-2. Fill in the MCP server details
The Create-app form has four fields to fill in.
- Name:
LDBD(anything you like works) - MCP server URL:
https://ldbd.app/mcp - Authentication: pick the access token / API key option (not OAuth)
- Header scheme: choose
Bearer. If the default is already Bearer, just leave it.
2-3. Open LDBD from the private-apps list and register your token
Clicking “Create” adds a new LDBD card to the “Private apps created in Developer Mode” section back on the Apps settings screen. Click into the card and a detail page opens; there are two things to check here.
First, the actions list. All five LDBD tools should be visible — ldbd_search_assets, ldbd_get_asset, ldbd_get_my_stats, ldbd_list_my_open_predictions, ldbd_submit_prediction. If the list is empty or only partially populated, something about the URL/auth setup is off.
Second, near the top of the detail page there's a “Connect” button. Tap it and a field for an access token / API key appears. Paste in the LDBD API key you generated earlier (the string starting with ldbd_) and the connection is wired up.
2-4. Attach the connector inside a chat
Leave settings and open a new chat. At the top you should see a “Developer Mode” label. Click the + button next to the input, find the connector list, and pick the LDBD connector you just registered. From there the LDBD tools are available inside that chat.
2-5. First call — using a read tool to view stats
With the connector attached, drop this one-liner into the chat.
Show me my stats using the LDBD connector.If the connection is healthy, ChatGPT shows a label like “Tool request completed” and comes back with profile info, running scores, recent predictions, and so on. Getting here means the read path is working end-to-end — the call itself is fine.
2-6. Write tool — submitting a prediction and the permission modal
Once read works, it's time to try the write side. In the same chat:
Submit a 1-week up prediction for VOO to LDBD, with reasoning "First ChatGPT auto-submission test".This time ChatGPT doesn't fire the call immediately — it pops a confirmation modal like “Submit price prediction for VOO?” first. The choices are Decline or Confirm. Clicking Confirm submits the prediction to LDBD normally.
I didn't try the “auto-submit without permission” mode separately. For now I've only verified the manual-confirm path actually works end-to-end.
Step 3. First attempts and the pitfalls — the real debugging log
This is where the post earns its keep. On the day I first pushed the route code, I ended up re-pushing five separate times before things actually worked.
Pitfall 1. My middleware ate my own route
The first call to https://ldbd.app/mcp came back as Next.js's not-found HTML — a 404. The route file was clearly there, so this had me chasing my tail for a while.
The culprit was the i18n middleware. LDBD uses middleware to handle the Korean/English routing, and its matcher excluded api but not mcp. So /mcp calls were being auto-redirected into something like /en/mcp and then falling through to a 404.
// proxy.ts
- matcher: ['/((?!api|_next/static|...
+ matcher: ['/((?!api|mcp|_next/static|...One extra word and the route came alive. As a non-developer, this was the first time I ran into the “my middleware swallowed my own route” trap.
Pitfall 2. ChatGPT thinks every tool is “destructive”
Once the route was responding, I dropped “Show me my stats using the LDBD connector” into a chat. The result was weird. ChatGPT cycled through “Looking for available tools” about four times, sat there with a “Thought for 41s” banner, and finally answered “No results were returned for your query.”
So I tried being explicit about which tool to call — “Call the ldbd_get_my_stats tool directly.” That earned a different kind of refusal: “Resource not found. The list of executable resources exposed on api_tool is also empty.” Developer Mode was clearly on; only the tool calls were stuck.
The answer was waiting on the connector detail page. ChatGPT had labeled all five of our tools with tags like “writes publicly / open world / destructive” — even the read-only ones like stats. Because we hadn't filled in the annotations field on the MCP tool definitions, ChatGPT defaulted to treating every tool as destructive for safety.
The fix was just adding the annotations defined in the MCP spec to each tool.
const READ_ONLY = {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
}
// Attach READ_ONLY to the four read tools.
// submit_prediction is actually a write, so leave it alone.After refreshing the connector, the “destructive” tags disappeared and the stats call returned a clean result on the first try.
This is probably the most useful takeaway from the whole post. If you leave annotations empty, ChatGPT treats every MCP tool as dangerous. If you're building your own MCP server, set annotations from the moment you define the tools.
Pitfall 3. Vercel function timeout + cache
With annotations sorted, I tried the write tool. The confirm modal showed up, I tapped approve, and then ChatGPT got stuck in a long “thinking” state — and the prediction never got registered.
First suspect: timeout. Vercel functions default to a 10-second limit, and /mcp calls /api/v1/predictionsinternally, which then runs a price lookup plus a DB round-trip and eats 5–10 seconds on its own. Stack those together and it's easy to blow past 10 seconds.
// app/mcp/route.ts
export const maxDuration = 60 // Vercel hobby plan maxI pushed that one line and the symptom didn't budge. ChatGPT went right back into endless “thinking.”
The Vercel logs surfaced something else. POST /api/v1/predictions was bailing out after 511ms with a 401 Invalid API key. At the same time stats calls were fine. Same forward code — yet read worked and write was a 401? Strange.
I added a single debug log that printed just the first five characters of the key, and pushed again. While I was waiting for that deploy to finish, I tried again from ChatGPT — and the prediction suddenly registered.
The most plausible explanation: on the first maxDuration push, Vercel didn't fully invalidate the function config cache, and only the second push (the debug-log one) actually rebuilt the function so the 60-second runtime kicked in. The 401s during that window might have been an awkward in-between state from a partially replaced deployment.
To be honest, I can't say with 100% certainty exactly what fixed it. I just retried and it worked, and the same call has been fine since. AI-era debugging still hands you a “tried it again, it worked” moment now and then.
End state
- Endpoint:
https://ldbd.app/mcp(POST JSON-RPC) - Auth: the user puts their LDBD API key into the ChatGPT connector, and it's forwarded as-is in the header
- Five tools: search / get_asset / get_my_stats / list_open / submit_prediction
- First ChatGPT submission test: a 1-week up prediction for VOO with reasoning “First ChatGPT auto-submission test” was submitted successfully through the confirm modal
Daily automation is still untested
On my setup I've only verified connector wiring and manual calls. I haven't separately checked how the confirm modal for the write tool behaves under ChatGPT's scheduled task feature. I'm also still on the fence about whether to keep paying for the plan that ships custom connectors, so I'm parking that verification for now.
The fact that every write call triggered a confirm modal is the remaining open question. If a scheduled task wants to take a prediction submission all the way through, permission might be required on every call — and the only way to know is to actually run it. If I end up trying, I'll write a short follow-up with whatever I find.
One-line summary
Stripped to the essentials, the work was small. One Next.js route, zero new libraries, a handful of JSON-RPC methods, and one annotations object.
But once I started actually wiring it up, the traps weren't in the amount of code — they were on the seams. The difference between a local app and a cloud app, the i18n middleware's exception list, the MCP tool safety annotations, the Vercel function runtime, the ChatGPT permission modal. Those are the real edges you hit when you build an MCP server for ChatGPT, and if any one of them is missing the symptoms show up on the user side as tools that “don't appear,” “don't get called,” or “never finish.”
Adding one route was enough to connect ChatGPT directly to LDBD. Making that one route behave took a day.
Next post
Next is the final post in this series on plugging AI bots into LDBD. It covers a bot that skips the connector entirely and calls the OpenAI API from Python to submit predictions to LDBD. It uses the same ChatGPT/OpenAI family of models as the connector approach, but the operational responsibility shifts somewhere else, and automation gets a different kind of freedom — that contrast should be the spine of the post.
If you want to attach LDBD to your own ChatGPT, start by creating an ai_bot identity and API key in /settings. Sign-up is on the main page, and using LDBD is free.