Outcome
By the end of this lesson, you will be able to connect ZopNight MCP to Cursor and to OpenAI Codex, secure PATs via environment variables (not config files), and verify the connection with three tests.
| Tier | Engineer |
| JTBD | ”Wire cost data into the AI tool I already use for code.” |
| Personas | Platform Engineer · Backend / Frontend Engineer |
| Prerequisites | M6.1 · M6.2.L1 |
| Time | 9 minutes |
| Bloom verb | Connect (Apply), Secure (Apply), Verify (Apply) |
1. Concept
Cursor is a code editor built around an assistant. Codex is OpenAI’s equivalent, close to what GitHub Copilot does in its agent mode.
Both speak MCP. Setting them up works the same way as Claude Desktop in M6.2.L1; only the location of the configuration file differs.
PREREQUISITES: ZopNight account with MCP enabled (covered in M6.3) PAT (reads by default; writes gated by the org tier) Cursor or Codex installed Engineer wants cost data inline in their dev workflowCursor setup
1. Open Cursor → Customize → MCPs → New MCP Server2. Paste this config and save: { "mcpServers": { "zopnight": { "url": "https://<your-zopnight-mcp-endpoint>/mcp", "headers": { "Authorization": "Bearer <your-PAT>" } } } }3. Restart Cursor (Cmd+Q / File→Quit, then relaunch)4. Test in Cursor chat: "list my idle resources" → Cursor calls ZopNight MCP; returns top resourcesCodex setup
1. Open Codex → Settings → Plugins → MCPs → Add2. Configure: Type: Streamable HTTP Name: zopnight URL: https://<your-zopnight-mcp-endpoint>/mcp Headers: add Key "Authorization", Value "Bearer <your-PAT>" (use the Headers section, not the "Bearer token env var" field)3. Save4. Restart Codex5. Test in Codex chatGrok build setup
Grok build (xAI’s coding agent) has native remote-MCP support, so it registers the endpoint directly from the terminal, no mcp-remote bridge:
1. Open a terminal2. Register the server: grok mcp add --transport http zopnight https://<your-zopnight-mcp-endpoint>/mcp \ --header "Authorization: Bearer <your-PAT>"3. Restart Grok build4. Test in Grok build chat: "list my idle resources" → Grok build calls ZopNight MCP; returns top resourcesConfiguration via file (recommended for teams)
For consistency across team members, use a versioned config file:
~/.cursor/mcp.json (Cursor)~/.codex/mcp.json (Codex)
{ "mcpServers": { "zopnight": { "command": "npx", "args": ["-y", "mcp-remote", "https://<your-zopnight-mcp-endpoint>/mcp"], "env": { "ZN_PAT": "${env:ZN_PAT}", "ZN_ORG": "${env:ZN_ORG}" } } }}The ${env:VAR} syntax reads from your shell environment. Set them in your shell profile:
# ~/.zshrc or ~/.bashrcexport ZN_PAT="zn_pat_xxxxxxxxxxxxxx"export ZN_ORG="org_acme123"Restart shell, then restart Cursor/Codex.
Security: never commit PATs
DO: Use ${env:ZN_PAT} in config files Store PAT in env var (.bashrc / .zshrc) or secrets manager Add .env to .gitignore
DON'T: Hardcode PAT in mcp.json Commit mcp.json with PAT value Share PAT via Slack/email Use the same PAT for multiple machines (rotate per device)Verification: three tests
After setup, run these three:
TEST 1: Server registered: In chat: "What MCP servers are connected?" Expected: list includes zopnight
TEST 2: Tool call works: In chat: "List my idle resources" Expected: JSON-formatted resources from ZopNight backend
TEST 3: Chained query: In chat: "What's my total monthly spend on EC2 across all accounts?" Expected: Chains multiple MCP calls; synthesizes a totalIf all three pass, you’re connected. If any fails, see common setup failures below.
Common setup failures
PROBLEM: "MCP server not found" CAUSE: npm package not installed; npx couldn't fetch FIX: npx -y mcp-remote https://<your-zopnight-mcp-endpoint>/mcp installs on every invocation; verify network and Node 18+
PROBLEM: "Unauthorized" CAUSE: PAT invalid, expired, or wrong env var name FIX: echo $ZN_PAT in shell to verify the env var is set; regenerate PAT in ZopNight settings if needed
PROBLEM: "No tools available" CAUSE: PAT scope incomplete OR org has MCP toggled off FIX: Check ZopNight admin settings (M6.3); verify PAT has full read scopes
PROBLEM: "Tool calls timeout" CAUSE: Org has many resources for default page size FIX: Increase MCP server timeout in config: args: ["-y", "mcp-remote", "https://<your-zopnight-mcp-endpoint>/mcp", "--timeout=60000"]
PROBLEM: "Permission denied" (file system) CAUSE: Config file location not writable FIX: Check directory permissions; create parent dir if missingPermission scope (PAT)
PAT SCOPES (inherited from the user; reads on a default org): org:read (basic identity) resources:read (resource inventory) costs:read (cost data) schedules:read (schedule inspection) recommendations:read (rec listing) audit:read (audit log access) notifications:read (notification history)ZopNight’s PAT model lets the user scope per-token. For MCP, all read scopes are typically included. M6.3 covers detailed PAT management.
Multi-org setup
Engineers working across multiple ZopNight orgs (e.g., one per customer in MSP / consulting roles):
{ "mcpServers": { "zopnight-customer-a": { "command": "npx", "args": ["-y", "mcp-remote", "https://<your-zopnight-mcp-endpoint>/mcp"], "env": { "ZN_PAT": "${env:ZN_PAT_A}", "ZN_ORG": "org_a" } }, "zopnight-customer-b": { "command": "npx", "args": ["-y", "mcp-remote", "https://<your-zopnight-mcp-endpoint>/mcp"], "env": { "ZN_PAT": "${env:ZN_PAT_B}", "ZN_ORG": "org_b" } } }}The agent sees both orgs. Queries specify: “List idle resources for zopnight-customer-a” or “Compare A and B spend last month.”
Team quick-start template
For teams adopting MCP, publish a starter template:
# In team's internal docs / wiki
REPO PATH: ~/.cursor/mcp.json (or ~/.codex/mcp.json)
CONTENT:{ "mcpServers": { "zopnight": { "command": "npx", "args": ["-y", "mcp-remote", "https://<your-zopnight-mcp-endpoint>/mcp"], "env": { "ZN_PAT": "${env:ZN_PAT}", "ZN_ORG": "${env:ZN_ORG}" } } }}
ENVIRONMENT (in ~/.zshrc):export ZN_PAT="zn_pat_yourPAThere"export ZN_ORG="org_acmeOrgId"
GITIGNORE:.env*_secret*Document team-shared config in the repo; the actual PAT goes in each engineer’s environment.
2. Demo
ENGINEER OPENS Cursor for the first time after setup:
Step 1: Cursor reads ~/.cursor/mcp.json on startupStep 2: Spawns the MCP server process with PAT from env varStep 3: Server authenticates to ZopNight backendStep 4: Cursor receives the capability list the gateway filtered for this caller
ENGINEER queries: "list my top 5 idle resources"
Cursor (via MCP): Calls list_resources(filter=idle, sort=days, limit=5) Returns 5 resources with details
ENGINEER follow-up: "schedule the top one for nights and weekends"
Cursor (via MCP): Recognizes mutation request Responds: "I do not have a schedule-creation tool available. To schedule i-0xyz123, open ZopNight: https://zop.dev/zopnight/app/resources/i-0xyz123/schedule Want me to draft the schedule config you'd paste?"
ENGINEER: "Yes, draft the schedule."
Cursor drafts cron-style schedule + resource group assignment.ENGINEER copies, opens ZopNight UI, pastes, saves.
Engineer's time: 60 secondsWithout MCP: would require switching to ZopNight UI to find the resource + 5 minutes of dashboard navigationThe contract surfaces appropriately: an ungranted tool is never advertised, so the agent recognises it cannot act and redirects to the UI.
3. Hands-on (5 min)
Set up MCP in your preferred AI tool:
TOOL: Cursor / Codex / both
□ STEP 1: Generated PAT in ZopNight PAT stored: env var / secrets manager / 1Password
□ STEP 2: Config file created Location: __________ Uses ${env:ZN_PAT}? Yes / No
□ STEP 3: Env var set in shell profile Shell: zsh / bash / fish File: __________ (e.g., ~/.zshrc)
□ STEP 4: Restarted tool
□ STEP 5: Verification tests Test 1 (server registered): pass / fail Test 2 (tool call): pass / fail Test 3 (chained query): pass / fail
ANY FAILURES: Symptom: __________ Likely cause (from common failures): __________ Fix: __________If you cannot get all three tests to pass, M6.2.L4 covers deeper verification.
4. Knowledge check
Q1
PAT for MCP should be stored:
A. Hardcoded directly in the mcp.json config file
B. Shared in the team chat channel so everyone can use it
C. Loaded from environment variable via ${env:ZN_PAT} syntax
D. Committed to the repository alongside the config
Show answer
Correct: C. The config file goes in version control (or template repo); the PAT value goes in the shell environment or a secrets manager. Never commit PATs to source control. Env variable. Config in repo; PAT in shell.
Q2
After MCP setup, verify with:
A. Both: server registered AND a test tool call returns real data
B. Just checking that the MCP server shows up as registered
C. Skipping verification and simply using it directly
D. Checking the advertised tool count matches the docs
Show answer
Correct: A. Without the tool call, you don’t know if the PAT is valid. Run all three tests (server registered, tool call, chained query) before relying on the connection. Test a tool call. Server-registered alone is necessary but not sufficient.
Q3
PAT scope for MCP:
A. Admin scope, so that every single tool is reachable
B. Owner scope across the whole of the organisation
C. Read-only; org:read, resources:read, costs:read, schedules:read, recommendations:read, audit:read, notifications:read
D. Whatever scope the organisation default happens to be set to at the moment the token is minted for you
Show answer
Correct: C. Minimum needed; full read scopes typical. Write scopes don’t exist for MCP (architectural; see M6.1.L2). Read-only scopes. The PAT model enforces this.
5. Apply
Configure MCP for your team’s chosen AI tool. Document the team setup in your wiki. M6.3 covers PAT management at scale.
For team rollout: publish a template config + env var setup; each engineer generates their own PAT.
Related lessons
- L1: Claude Desktop setup
- L3: Claude Code (terminal) setup (next)
- L4: Verify the connection
- M6.3: PAT management
Glossary terms touched
Cursor MCP · Codex MCP · Env-var loading · Multi-org config