idnow-mcp-server
by DevMohith
README.md
# IDnow Trust Platform MCP Server (Assignment)
## Introduction
An MCP server that lets an LLM agents operate the IDnow Trust Platform sandbox - list the
verification flows on the account, kick off a new identity-verification session, check on
existing sessions, and pull full detail on one specific session. IDnow's customers are banks
and fintechs that need to verify their own customers remotely: a "flow" is a verification strategy
(video KYC, document scan, etc.), a "session" is one verification attempt for one person, and
the `playerUrl` a session returns is the link that person opens to actually go through verification.
This server is a small version of how those customers' own agents would eventually orchestrate that flow.
Everything here runs against the real IDnow sandbox (staging environment) - nothing is mocked.
## Setup & Run
```terminal
git clone https://github.com/DevMohith/idnow-mcp-server.git
npm install
```
- That's it - no `.env`, no config step. Credentials are hardcoded in `src/config.js`, which is
exactly what the assignment asked for ("fine to put it directly in the code to keep things simple").
- In a real production integration these would live in a secrets manager and get injected at *runtime, never committed - that's a scope choice for this assignment, not an oversight.
## for running it, and pointing an LLM client at it
There are three ways i designed to actually talk to this server, depending on what you're trying to check.
Before jumping I want to let you know -
This server uses #stdio transport pipes, so it only works when the client and server are on the same machine :
- the client spawn the server for you using the path in its config,
- no manual "run the server" step needed.
- A cloud-based client would need the server hosted separately over HTTP (Cloudflare Tunnel or VPS server)
### Option A - Claude Desktop (the real end-to-end demo) with prettier print
Edit your Claude Desktop config:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"idnow": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/idnow-mcp-server/src/server.js"]
}
}
}
```
For e.g. - Absolute Path looks like below
("C:\\Users\\mohithtummala\\Desktop\\idnow-mcp-server\\src\\server.js")
Restart Claude Desktop fully (not just the window - check it's not still running in the
background tasks or tray), and the 4 tools show up under [+ icon -> connectors -> manage connectors -> idnow] .
This is the setup `transcript.md` was captured against.
### Option B - Claude Code (CLI)
If you've got the `claude` CLI installed, this is the fastest way to try it:
```bash
cd idnow-mcp-server
claude mcp add idnow -- node src/server.js
claude
```
Then just talk to it naturally - "which verification flows can I use?" and so on.
### Option C - MCP Inspector (protocol-level, no LLM involved, Developer tool)
Useful for checking the raw tool schemas and responses without an LLM in the loop at all:
```bash
npm run mcp-inspector
```
opens a browser UI automatically, or for scripting/CI-style checks, the CLI mode works well:
This is how the tool schemas and error responses in this project were actually verified during
development, independent of any particular chat client.
### Other Options (e.g. Cursor, Cline)
Use mcp config provided in option A, to connect to the local mcp clients uses STDIN, STDOUT pipelines.
## Architecture
Four files, each with one job:
`src/config.js` - The sandbox credentials and URLs, in one place <br>
`src/auth.js` - OAuth2 client-credentials token manager — in-memory cache, 60s early-refresh buffer, in-flight request de-duplication, `forceRefresh` for 401 recovery <br>
`src/api.js` - 4 endpoint functions, a shared `request()` helper with one-time 401 retry, and `toCleanError()` centralizing all error message formatting <br>
`src/server.js` - FastMCP tool registration - 4 tools with Zod schemas and descriptions written for chaining, `UserError` wrapping so failures surface as clean tool errors <br>
Transport is stdio, schemas are Zod, HTTP is axios - this is the stack I followed,
and there's no database or Docker involved; state lives in-memory only, which is
explicitly stated fine in Assignment.
## The 4 tools in server.js
| Tool | Purpose | Endpoint |
`list_flows` - List available verification flows (`GET /api/v1/flows/staging`) <br>
`create_session` - Start a new session for a flow (`POST /api/v1/flows/{flowId}/staging/sessions`) <br>
`list_sessions` - List sessions for a flow (defaults to the first flow if none given) (`GET /api/v1/flows/{flowId}/staging/sessions`) <br>
`get_session` - Full detail on one session (`GET /api/v1/staging/sessions/{sessionId}`)
## Results / Verification
See `transcript.md` for verification of the three prompts working against the sandbox
- all four tools working end-to-end, including tool chaining and error handling tests.
## Testing
```bash
npm test # tests/auth.test.js - proves getToken() fetches and caches it rather than re-authenticatrin on every call.
npm run smoke # scripts/smoke.js - This is where i started testing, walks all 4 endpoints against the live sandbox, prints results
```
The two are kept separate on purpose: `npm test` should be fast, deterministic, and side-effect
free, while the smoke script deliberately creates a real session every time it runs, which isn't
something you want happening on every `npm test`.
## Design choices & trade-offs
- **Stack:** Node.js + official MCP SDK + FastMCP SDK + Zod Input Validation + axios + Node test + assert runtime-checks
- **Transport:** stdio, MCP's default - kept simple; an HTTP/SSE transport would remove the
local-config step for a real deployment, I would extend in production.
- **MCP SDK:** used FastMCP abstraction of official MCP SDK, which the brief explicitly
allows either, mainly for the Zod-schema-to-tool-schema generation.
- **Client secret:** hardcoded in `config.js`, per the assignment's own instruction ("fine to put
it directly in the code to keep things simple") - not an oversight, i would use secret manager and .env in prod.
- **Real sandbox only:** every response shape documented above, and everything in
`transcript.md`, came from actually hitting `idnow.sx` - nothing here is mocked or guessed.
- **`list_sessions` status filter runs client-side, not server-side:** Verified
directly against the live sandbox — and cross-checked IDnow's own docs - that
the sessions-listing endpoint has no working status filter parameter; So added
filter expression on `listSessions()` fetches the full list first and filters by
`sessionStatus` in JavaScript instead.
## A note on AI usage
This was built by contesting on AI-generated code rather than completely accepting everything it given - for example,
the initial auth module re-authenticated on every call; I added expiry tracking and in-flight deduplication.TDQS
A4.4/5.0
Scored across 4 tools
Disambiguation5/5
Each tool targets a distinct resource and action: listing flows, listing sessions, creating a session, and retrieving a single session. There is no overlap or ambiguity between them.
Naming Consistency5/5
All tools follow a consistent verb_noun pattern (list_flows, list_sessions, create_session, get_session) with no mixed conventions or deviations.
Tool Count5/5
Four tools is well-scoped for an identity verification server, covering discovery, creation, and status retrieval without unnecessary bloat.
Completeness4/5
The core workflow is fully covered: discover flows, create sessions, list and retrieve session details. However, an explicit cancel or abort session operation is missing, which might be a minor gap given the session statuses include ABORTED.
Maintenance
ActivitySlowing
ResponsivenessNo issues