Skip to main content
Glama
README.md
# sn-mcp

A [Model Context Protocol](https://modelcontextprotocol.io) server that lets an LLM talk to a ServiceNow instance — query records, and (only when you turn it on) write them — without handing the model the keys to the kingdom.

Built for a healthcare context. The bar is: a CISO can read this page and know what the model can and cannot touch.

---

## The problem

LLMs are useful against ServiceNow. They are also one bad prompt away from reading `sys_user`, dumping a table, or following instructions hidden in a work note.

Most MCP wrappers solve “can the model call the API?” They skip “what happens when it tries something it shouldn’t?”

## Why this exists

The day job is regulated healthcare. I needed an agent that could look at a live instance while I was building — and I would not point a raw API token at that instance.

So the product is not “nine ServiceNow tools.” The product is the **safety kernel those tools cannot bypass**.

---

## How a call works

```
LLM
  │  stdio (no network, no ports)
  ▼
sn-mcp
  │  1. Zod validation
  │  2. Table + field allowlist      ← default deny
  │  3. Query denylist               ← no identity enumeration
  │  4. HTTPS to ServiceNow (10s cap)
  │  5. Error sanitization           ← LLM never sees stack traces
  │  6. PHI redaction
  │  7. Response wrapper             ← retrieved data is untrusted input
  │  8. Audit log + rate limit
  ▼
ServiceNow
```

Read tools always go through that path. Write tools add one more gate: **`BUILDER_MODE` is off unless you set it.** Idle sessions cannot create, update, delete, or run scripts.

---

## Design decisions

These are the ones a stranger needs. The rest, including what was rejected, live in [`DECISIONS.md`](./DECISIONS.md).

| Decision | Choice | Why |
|---|---|---|
| Default deny | Only listed tables and fields come back | A miss is a closed door, not an open one |
| Writes are opt-in | `BUILDER_MODE=true` or the write tools refuse | An idle chat cannot change the instance |
| Errors fail safe | Unknown error category = no hint to the model | Better a confused model than a leaked schema |
| PHI regex is not the primary control | Allowlist is primary; redaction is defense in depth | Regex will miss things. We say that out loud. |
| Retrieved data is untrusted | Every SN payload is wrapped and tagged | Stops a work note from becoming an instruction |
| Identity fields are denied everywhere | One `SYSTEM_IDENTITY_FIELDS` list, every table | Closing `caller_id` and leaving `sys_created_by` is not a lock |

What this is **not**: a production connector for a hospital instance. It is built and tested against a developer instance, with Basic auth. OAuth is the gating requirement before it ever sees real PHI.

---

## What it can do

**Always on** (still subject to the kernel):

| Tool | Purpose |
|---|---|
| `query_table` | Query an allowlisted table |
| `get_record` | Fetch one record by `sys_id` |
| `search_kb` | Search published knowledge articles |
| `count_table` | Return a count — no rows cross the boundary |
| `health_check` | Is ServiceNow up, and can we write the audit log? |

**Off until you opt in:**

| Tool | Purpose |
|---|---|
| `create_record` / `update_record` / `delete_record` | Write to the instance |
| `execute_script` | Run server-side JavaScript. Treat it as admin. |

Default allowlist: `incident`, `change_request`, `problem`, `sc_request`, `sc_task`, `kb_knowledge`, `alm_hardware`, `wm_order`. Clinical tables, users, scripts, attachments, and email are denied by name.

---

## Run it

```bash
git clone https://github.com/dadshorts/sn-mcp.git
cd sn-mcp
npm install
cp .env.example .env   # set SN_INSTANCE, SN_USERNAME, SN_PASSWORD
npm test               # regression suite against the safety kernel
node src/index.mjs     # listens on stdio
```

Point your MCP client at `node src/index.mjs`. Credentials stay in `.env` (gitignored). Leave `BUILDER_MODE` unset unless you are sitting down to write.

---

## What's verified

- Safety kernel + write gate exercised on a live developer instance
- Regression suite for allowlist, denylist, wrapping, PHI false-positives, and builder-mode refusals
- Audit log written for every tool call, including refusals
- Months of local operation while building ServiceNow work

This repo is the server. It does not include instance credentials, audit logs, or production data.

TDQS

A4.2/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: querying records, fetching by sys_id, counting records, creating/updating/deleting, searching the KB, running scripts, and checking health. Even query_table and get_record are distinct (filtered search vs. direct ID lookup), so there is no ambiguity.

Naming Consistency4/5

Most tools follow a consistent verb_noun snake_case pattern (query_table, create_record, delete_record, etc.). The only minor deviation is 'health_check' which uses a noun-verb compound rather than verb_noun, but it remains readable and consistent in casing.

Tool Count5/5

With 9 tools, the server is well-scoped for its purpose. It covers CRUD, querying, counting, KB search, script execution, and health monitoring without unnecessary bloat or missing essentials.

Completeness5/5

The tool set provides full record lifecycle coverage (create, read, update, delete), flexible searching with query_table and count_table, knowledge base access, arbitrary script execution, and server health checks. There are no obvious gaps for typical ServiceNow automation workflows.

Maintenance

ActivitySlowing
ResponsivenessNo issues