Case study

Five Hours on the Wrong Layer: Retiring a OneNote MCP

I spent five hours over two nights getting an MCP server for OneNote to start and sign in. It turned out the tools had been stubs all along, and the bigger problem was that OneNote didn't fit the workflow I wanted Claude to help run.

I wanted Claude to help me run the pipeline behind my two YouTube channels. The pipeline lived in a OneNote notebook, so the plan was simple: connect Claude to OneNote and let it read and update the video cards.

That didn’t happen. I got a server running and signed in, and it could list my notebooks. That was all it could ever do. This is the story of why, and of why the most useful thing I learned had nothing to do with the code.

What I was trying to support

The pipeline lives in a OneNote notebook, and it runs on video cards. Every video idea gets a card, a page with a permanent home in its channel’s section and playlist, which says what the video is about. As the video gets made, the card moves through the notebook’s production sections: Next Up while it’s filled in, In Progress while it’s produced, Published for the 30 days after it goes up, while I watch how it does, and Complete once I’ve reviewed it. Those say what I’m doing with it right now. When it’s done, the card goes back to its permanent home with everything I learned attached.

There are close to 200 cards, and that number is not a to-do list. It’s an idea bank. Next Up holds three to five at a time, deliberately.

I set up this pipeline because, if it runs well (and I believe it can), it will make new videos on both channels a lot faster to produce. The problem (and it is a problem) is internal. It has a lot of moving parts, and I’m not good at keeping track of that many moving parts at once. So I wanted Claude to build an MCP that could work in the pipeline notebook with me and automate some of the process. That way I could spend more of my mental energy on actually making the videos.

Where I started

There was already a OneNote connector for claude.ai, and it didn’t work for me. So I found an open-source MCP server, danosb/onenote-mcp on GitHub, whose last commit was about fifteen months old. I installed it and asked Claude Desktop to start it.

It didn’t start.

Getting it to start

The error was the same every time: the server couldn’t find a module inside a folder called typescript-sdk. I tried three different fixes to the dependencies, and the error didn’t change once.

Four things were true at the same time, and that’s why it took so long. The way I’d installed the server skips its build steps. The “submodule” everyone kept pointing to had never been a submodule. npm reported everything “up to date” while linking to an empty folder. And the SDK’s own repository refused to build with npm at all.

What finally worked was reading the import statement. The code pointed at a hard-coded path inside the project folder, so no amount of correct installation could ever have satisfied it. Two lines of code needed changing.

When the same “module not found” error survives three different dependency fixes, stop fixing dependencies and read the import.

I got there a step late, too, because I’d acted on Claude’s instructions before reading to the end of them. Again. Claude told me no headdesking was needed. My reply is still how I’d describe the habit:

lol no, I was headdesking because (and I won’t blame this on AuDHD), I do an action as soon as I see the action needs to be done, only to fail to notice that it didn’t actually need to be done yet or notice that it says later on to do something before that or (even worse) I do the action AND HIT ENTER TO SEND before I continue reading and then you’re responding to what I sent as if I just failed to do the rest of what you said and now you’re typing out another response when I haven’t even finished the last one.

Then I ran it, and reported back:

Oh… it worked. But it wants a token. Charon wanting his coin to ferry my deceased soul across the River Styx…

Getting it to sign in

The server started, and then I opened a second chat, in the app where the server was actually registered, to see whether Claude could reach my notes. It couldn’t. That took two more hours.

Restarting the server meant deleting it in the settings, quitting the app from the taskbar, pasting its lines back into the config file, and relaunching. There was no toggle. I did that over and over that night.

Along the way, one of the server’s own tools quietly wiped my saved sign-in. It was supposed to take a token from a field that never arrived, so it saved nothing over the real one. I’d noticed something was off before Claude did: the tool’s name only appeared in the code inside text strings. When the fixes went in, I couldn’t resist: “See, I’m glad I spotted that. You should tell ChatGPT to take back all the nasty things it said about me. I’m not completely useless! 🤣🤣🤣”

What finally cracked it was cutting the server out entirely. I sent the saved token straight to Microsoft from PowerShell, and it failed there too. That one test proved the problem wasn’t in any code we’d been editing. The real cause was permissions written for work accounts. My account is a personal Microsoft account that happens to use a school email address, so the sign-in looked like it succeeded and gave me a token that couldn’t read anything. With the personal-account permissions, my three notebooks came back.

When you can’t tell whether the bug is in your code or in what your code is handed, take your code out of the loop and test what it’s handed.

A week later

Six days later it stopped working. The error said the sign-in token wasn’t a well-formed JWT, and every Claude session I asked took that at face value. None of them had registered that mine is a personal Microsoft account that happens to use a school email address, so they kept treating it like a work account until I spelled it out.

That’s the first half of the story, and it’s a lesson about token types. The token was never the problem. Personal Microsoft accounts get tokens that start with “EwB” and look nothing like a JWT, and Microsoft accepts them anyway. The real problem was where I was sending it: the sign-in code pointed at the endpoint for organizations, when my account needed the one for personal accounts. (The first attempt at that change went into a copy of the code that never ran.)

The second half is smaller and more embarrassing. Switching endpoints also moved the sign-in page to a new address, and I didn’t notice. Each time it rejected my code I went back to the old page from memory, and each time it failed again. As I put it at the time: “I never noticed the change of URLs and so I kept going back to login.microsoft.com/device. (facepalm)” Once I used the address on the screen, it worked. Three minutes later, another chat listed my notebooks.

“Not a JWT” doesn’t mean “not a valid token.” And the address you remember isn’t always the one on the screen.

“It works”

Twice, a Claude session told me I was done. The first time, the server had only started. The second time, it had listed my notebooks.

Both times, that’s all it had done. A chat that tried to go further found that listing notebooks worked, listing sections hung for four minutes, and none of the tools accepted any input. That report came from the hosted connector, not my server, but my server turned out to have the same shape.

What five hours of debugging had missed

When I finally read the tool code, the answer was waiting there. Only one of the nine tools was real. The others had been written as stubs upstream: they always returned the first notebook, the first section, or the first page, whatever you asked for. They couldn’t receive what you asked for in the first place.

The clue had been on screen since the first night. The tool that wiped my sign-in failed because it took no input. We read that as one broken tool, fixed around it, and moved on.

“It can only list notebooks” was never a bug to find. Every hour had gone into making the plumbing work. The tools at the other end of it had never been built.

Reading the code also showed a second problem that I haven’t seen happen, only predicted: the server saved a token that lasts about an hour and had no way to renew it.

The handoff that changed the question

Before committing to a rewrite, I had ChatGPT write a handoff describing the pipeline from my earlier conversations about it. It marked every claim as one of three things: something I’d decided, its own interpretation, or a suggestion. That labeling is the reason I could trust it enough to act on.

Reading it with the server in mind, I saw the problem. My whole pipeline depends on where a page is. A card lives in its playlist, gets copied into Next Up, moves through the stages, and comes home. A OneNote page can only be in one place, and Microsoft’s API for OneNote can’t move a page at all. A move would be a copy followed by a delete, and that gives the page a new identity, which breaks every link pointing to it.

Much of the handoff’s complexity existed only because of that: rules for reconciling the original card with its working copy, for deciding which copy is current, for matching the status board to the right card. Six of its fifteen open questions were about it.

The decision

I’m retiring the server and moving the pipeline to Notion. In Notion, one record can appear in many views: playlist is a property, stage is a property, and the status board is a view. With no copies, there’s nothing to reconcile.

OneNote: a page lives in one place A video card sits in its playlist section. To appear in Next Up as well, it has to be copied there, which leaves two pages and the question of which one is current. Moving a page instead means copying it and deleting the original, which creates a new page and breaks the links to the old one. OneNote: a page lives in one place Playlist section Video card Dashboard › Next Up Copy of the card Which one is current? copied Moving it means copy, then delete: a new page, and broken links. Notion: one record, many views A single video card record holds its playlist and its stage as properties. Arrows run from the card down to a playlist view and a stage board, which both read that same record. Changing the card's stage moves it between columns on the board, but the card itself never moves and is never copied. Notion: one record, many views Video card Playlist: its playlist Stage: Next Up Playlist view Stage board Change its stage and the board updates. The card never moves or gets copied.
The same card in each tool. In OneNote, being in two places means two pages. In Notion, it's one record, and the views read from it.

The reasons, in order of weight: the workflow is built on location, and OneNote can’t represent it without copies; Claude couldn’t move cards even with a working server; the server was further from working than it looked; and YouTube is on the back burner anyway.

What I accepted to get there: migrating about 200 cards, giving up years of habit in OneNote, and going against the handoff’s own warning about adding new infrastructure. That warning was written for a OneNote session, though.

I haven’t started the move yet. When I do, the first step is deliberately small: one Notion database, one real card, and a board view grouped by stage. If I can see that card on the board without having copied it anywhere, that one observation is the whole case for the move.

It was a question of fit, not code

Looking back, code quality didn’t decide this. Fit did, at three levels:

  • Auth fit. A personal Microsoft account on a school address, which the default settings didn’t expect.
  • API fit. The API can’t move a page, and moving pages is the pipeline’s core motion.
  • Workflow fit. A OneNote page can only be in one place, and a card needs to be in two.

The Runbox MCP (runbox-mcp.kwpledger.com) worked, and not because Runbox made it easy. Runbox has no mail API at all. It fit on the same three counts where this one didn’t:

  • Auth fit. One scoped app password, revocable, kept as a secret on the server and never in a config file.
  • API fit. Mail speaks IMAP and SMTP, which are old and boring and standard, so the server could talk to the mailbox directly.
  • Workflow fit. Sorting mail is moving messages between folders and marking them, which is exactly what IMAP does. The rules live in my own server, so I never needed Runbox to support them.

How AI was used, and where it wasn’t trusted

All of this happened in ordinary claude.ai chats. I hadn’t really used Claude Code yet, so I was hashing everything out in Chat. Those sessions did most of the diagnosis, and I ran every command myself. A few rules came out of that, and they’re the part I’d keep:

  • Establish which system produced an error before acting on it. Two different “OneNote” MCPs shared a name and nothing else. An error report from one chat wasn’t evidence about my server unless it came from the app where my server was running.
  • Ask; don’t infer. The sign-in fix depended on what kind of account I had. The domain suggested one answer, and the true answer was the other one. Only I could say.
  • Label what’s confirmed. The stub tools were confirmed by reading the code. The token expiration was predicted and never observed. The docs say which is which, and so does this piece.
  • “Done” is a claim, so test it. Sessions declared victory when the server started and again when it listed notebooks. Neither had tried a second tool.
  • Confident isn’t correct. Several fixes were guesses delivered as diagnoses: a submodule that didn’t exist, a build script that wasn’t there, and, once, a history of an earlier attempt that I don’t recognize. The test that cut through it was the one with no AI reasoning in it at all, sending the token straight to Microsoft.
  • Write it down as you go. The architecture doc, the debugging record, and the decision note exist because each session recorded its reasoning before it ended. Without them, this case study would be recollection.

What I’d tell someone starting the same thing

  1. Check the fit before the code. Ask whether the tool can represent your workflow at all, and whether its API can do the one motion your workflow depends on.
  2. Read the import before blaming the dependencies.
  3. A token that isn’t a JWT can still be valid.
  4. Make sure the URL is what you think it is before you type anything into it.
  5. Test the tools, not just the connection. “It connected” and “it works” are different claims.

Where it stands

The repository is archived and private. The OneNote notebook stays the source of truth until the move to Notion is done. The handoff describing the pipeline carries over nearly intact, because most of it was about the pipeline and not about OneNote.

It didn’t succeed. I don’t count it as a failure, though, because I learned a lot in the attempt. It produced a decision, a written record, and a better question to ask at the start of the next one.

← All case studies