· #servicenow#ai#mcp#sndevio#cloudflare#project

Proxying a live ServiceNow instance through an MCP server

Static specs teach agents the API surface. A live proxy lets them actually use it. How I built /live on sndev.io without storing anyone's credentials.

The first time I watched an agent discover a schema and then actually use it on a real instance, I made a noise my dog didn’t appreciate.

It was late. I’d been staring at the terminal for probably an hour, asking Claude to poke around a dev instance through sndev.io. It called discover_schema on incident. It read the field list back. Then, without being told, it composed a query_table call with the right encoded query, the right display-value flag, and a sensible field selection — and it got back ten rows of actual incidents.

I’d been testing this thing for days. That was the run where it clicked.

The gap static specs leave behind

sndev.io started as a reference. Fourteen MCP tools that let an agent search the Zurich release — scripting classes, REST endpoints, table schemas, script includes, encoded query syntax, patterns. All static. All bundled into a Cloudflare Worker. All very fast.

It works. Agents that couldn’t tell you what gs.getUser() returned six months ago will now cite the method signature correctly on the first try.

But there’s a ceiling. Specs tell you what the API is. They don’t tell you what’s in the instance in front of you. An agent reading sys_dictionary entries from a bundled export is not the same as an agent reading the dictionary of your instance, with your custom fields, on your release. At some point you stop wanting a reference and start wanting a hand on the wheel.

I wanted that hand. I also didn’t want to build a second product to get it.

What /live actually is

/live is three tools bolted onto the same worker. Nothing more.

  • query_table — Table API, any table, any encoded query
  • execute_script — server-side JavaScript, the kind you’d paste into a background script
  • discover_schema — walks sys_dictionary for a given table on the live instance

That’s it. No CRUD helpers. No “create an incident” convenience wrapper. No abstractions that pretend the Table API is something it isn’t. I genuinely considered adding more and then didn’t, because the minute you start hand-rolling tools for every verb you’re building a second ServiceNow SDK, and the world already has several of those.

Three tools is enough for an agent to look around, try something, and verify the result. That’s the whole loop. Everything else is decoration.

The part I actually care about: credentials

Here’s the bit I want to be loud about. The worker doesn’t store your credentials. It doesn’t want to. It never will.

Every /live request carries its own auth headers — X-SN-Instance plus either X-SN-Token or a username/password pair. The worker reads those headers, forwards the call to your instance, and then those headers fall out of scope with the request. No KV write. No D1 row. No “let me just cache this for convenience”. Nothing.

I went out of my way to make this boring. There’s no login flow. There’s no session. There’s no “connect your instance” button. If you want the proxy to do something, you hand it credentials for that one call, and then you do it again next time.

The posture I want users to have is: I am not trusting sndev.io with my instance. I am letting sndev.io forward a single request that I already authorized, and I’d be fine if I had to re-authorize the next one. That’s the whole design goal. If someone ever breaches the worker, the worst thing they find is analytics counters.

(The flip side — yes, you do have to pipe credentials into your MCP client. I’d rather that than the alternative.)

When this matters, and when it doesn’t

It does not matter if you’re asking a question the static spec already answers. “What fields does cmdb_ci_server have in Zurich?” doesn’t need a live instance. search_schema handles that for free.

It matters the moment the question is about your data. “Does our incident table have that custom field the last admin added?” “How many P1s opened this week?” “Can this script actually run in our scope?” Those are live questions. You can’t answer them from a spec, no matter how good the spec is.

I use it almost every day now, mostly for the “is my thing actually working” part of the loop — after I push a script include through the sn CLI, I ask an agent to run it against the instance and tell me what came back. That used to be three tabs and a copy-paste. Now it’s a sentence.

The part I didn’t expect

The unexpected thing was how much better the static tools got once the live ones existed. Agents stopped hallucinating as often, because they could check themselves. If they weren’t sure whether a field existed, they’d run discover_schema instead of guessing. If they weren’t sure a script was valid, they’d throw it at execute_script and read the error.

I didn’t plan that. I thought I was building a convenience. Turns out I was building a feedback loop.

Anyway. That’s /live. Three tools, no storage, one late-night moment where the dog got startled.

-D