Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
da52ea3
Fix API conformance, security hardening, and expand MCP toolset
May 15, 2026
015ee6c
Separate close, resolution, and status into distinct MCP tools
May 15, 2026
f00e471
Fix environment variable loading and correct .env.example key names
May 15, 2026
0f551fb
Fix resolution on closed tickets and add startup connection test
May 15, 2026
c5a7063
Revert "Fix resolution on closed tickets and add startup connection t…
May 15, 2026
dcbde16
Add SDP API connection test at server startup
May 15, 2026
ab39156
Fix missing instanceName causing 403 errors on all API calls
May 15, 2026
ded7bb7
Consolidate instanceName into portalName, drop SDP_INSTANCE_NAME
May 15, 2026
52799fa
Sanitize hardcoded credentials and untrack token cache file
May 15, 2026
22fbf8d
Restore instanceName, match original auth/URL logic, add env diagnost…
May 15, 2026
60a5920
Fix infinite 401 retry loop and add auth diagnostics
May 15, 2026
cbce0da
Fix API URL — drop /app/{portalName} segment from baseURL
May 15, 2026
4355366
Change testConnection to verify OAuth token only, not API endpoint
May 15, 2026
0d24625
Update README with session fixes and add SDP-MCP_Usage.md
May 19, 2026
fcca193
Add MCP Resources and get_usage_guide tool for Copilot Studio
May 19, 2026
9a226d2
Update CHANGELOG with 2026-05-19 session changes
May 19, 2026
66552eb
Modularise server — split tools, add prompts, input validation, riche…
May 19, 2026
81f1ea1
Update CHANGELOG with session 2 modularisation changes
May 19, 2026
457aef2
Add display_id lookup support to get_request
May 19, 2026
5236758
Update CHANGELOG with display_id lookup feature
May 19, 2026
6df536c
Fix advancedSearchRequests criteria normalisation and pagination
May 19, 2026
3c07d7c
Update CHANGELOG with advancedSearchRequests fixes
May 19, 2026
6562b86
Add response logging to diagnose empty advanced search results
May 19, 2026
129dbc6
Add technician_email and requester_email filters to list_requests
May 19, 2026
0eaa4b2
Improve tool descriptions to steer AI toward list_requests
May 19, 2026
4d4ab8d
Fix listRequests multi-criteria format to use children nesting
May 19, 2026
d2c40f7
Revert list_requests and listRequests to pre-session state
May 19, 2026
af6188f
Revert closeRequest to PUT and fix resolution field mismatch
May 20, 2026
8e004c2
Drop closure_code from PUT payload; derive status from it instead
May 20, 2026
7f5be6f
Fix getRequestConversation wrong response key and missing list_info
May 20, 2026
03cb68b
Fix response interceptor false 'Empty result' log for notes endpoint
May 20, 2026
e62dea6
Fall back to item count when list_info.total_count is absent
May 20, 2026
ea680c6
Log tool name on tools/call so it's visible in Azure output
May 20, 2026
83997c7
Note plain text constraint on close_request resolution field
May 20, 2026
72aff44
Revert "Note plain text constraint on close_request resolution field"
May 20, 2026
7160305
Strip HTML from closure_comments; keep HTML in resolution.content
May 20, 2026
134a316
Correct advanced_search_requests description
May 20, 2026
f2c4627
Remove claude_code_command tool — not needed in production
May 20, 2026
99c6ed9
Improve advanced_search_requests schema to eliminate AI clarifying qu…
May 20, 2026
bc2d76d
Add logging to advanced_search_requests to diagnose silent failures
May 20, 2026
010f81f
Remove enum from criteria field/condition — prevents Copilot Studio p…
May 20, 2026
36fe389
Rewrite advanced_search_requests with flat parameters
May 20, 2026
330cf34
Fix searchRequestsFlat: plain object search_criteria only, filter_by …
May 20, 2026
fda5aa7
Fix searchRequestsFlat: drop filter_by when search_criteria present
May 20, 2026
8c300c7
Try flat reference fields in list_info for multi-filter search
May 20, 2026
92545e7
Fix search_criteria: use array format with lowercase logical_operator
May 20, 2026
77a9074
Fix duplicate criteriaList declaration causing startup crash
May 21, 2026
d858282
Fix searchRequestsFlat: use children-nested search_criteria format
May 21, 2026
5d14766
Fix searchRequestsFlat: lowercase status values in search_criteria
May 21, 2026
586f5b8
Add Content-Type header to all SDP API requests
May 21, 2026
fa1a4b8
Fix URL encoding and add full URL logging for API requests
May 21, 2026
83bd3d5
Lowercase email addresses in searchRequestsFlat criteria
May 21, 2026
340f431
Fix API call consistency across all endpoints
May 21, 2026
4e9dad4
Add proactive SSE reconnection and APPID logging for Azure Easy Auth
May 22, 2026
9462341
Revert "Add proactive SSE reconnection and APPID logging for Azure Ea…
May 22, 2026
8733637
Fix API call consistency across all endpoints
May 22, 2026
59e1b99
Fix request ID guidance, On Hold routing, and display_id resolution
May 22, 2026
ae64bf3
Revert get_request display_id search value to integer
May 22, 2026
01cbe1a
Fix technician/requester endpoints and metadata fallbacks
May 26, 2026
4131887
Add token preview and full error logging for auth diagnostics
May 26, 2026
e15a12d
Remove diagnostic token/error logging
May 26, 2026
c109c3c
Add subcategories to get_metadata response
May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Service Desk Plus OAuth Configuration
SDP_CLIENT_ID=1000.U38EZ7R0KMO9DQZHYGKE83FG4OVUEU
SDP_CLIENT_SECRET=5752f7060c587171f81b21d58c5b8d0019587ca999
SDP_CLIENT_ID=YOUR_CLIENT_ID
SDP_CLIENT_SECRET=YOUR_CLIENT_SECRET
SDP_PORTAL_NAME=your-portal-name
SDP_DATA_CENTER=US

Expand Down
4 changes: 2 additions & 2 deletions .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"type": "sse",
"url": "http://10.212.0.7:3456/sse",
"env": {
"SDP_CLIENT_ID": "1000.U38EZ7R0KMO9DQZHYGKE83FG4OVUEU",
"SDP_CLIENT_SECRET": "5752f7060c587171f81b21d58c5b8d0019587ca999"
"SDP_CLIENT_ID": "YOUR_CLIENT_ID",
"SDP_CLIENT_SECRET": "YOUR_CLIENT_SECRET"
}
}
}
Expand Down
179 changes: 179 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Changelog

All notable changes to the SDP MCP Server are documented here.

## [Unreleased] — 2026-05-19 (session 5)

### New Features
- **`list_requests` accepts `technician_email` and `requester_email` filters** (`src/tools/requests.cjs`,
`src/sdp-api-client-v2.cjs`) — Copilot Studio's AI cannot construct the structured `criteria` array
required by `advanced_search_requests` from natural language ("pull up all my assigned requests that are
open"), so the two most common person-based filters are now exposed as simple string parameters on
`list_requests`. Both are forwarded to `listRequests` as `technicianEmail` / `requesterEmail`, which
already builds the correct `search_criteria` objects internally.

## [Unreleased] — 2026-05-19 (session 4)

### Bug Fixes
- **Fixed `advancedSearchRequests` criteria normalisation** (`sdp-api-client-v2.cjs`) — four
issues corrected against the SDP v3 API documentation:
1. Single-element criteria array now unwrapped to a plain object before sending — the API
expects an object for single criteria, not a one-element array.
2. `logical_operator` is now stripped from the first criterion in multi-element arrays —
the API rejects queries where the first item carries `logical_operator`.
3. `page` parameter replaced with `start_index: (page-1) * rowCount` — aligns with
`listRequests` and the primary documented pagination parameter.
4. `display_id` lookup in `get_request` now passes the value as an integer (`parseInt`) —
`display_id` is type `long` in the API; sending a string caused a type mismatch.

## [Unreleased] — 2026-05-19 (session 3)

### New Features
- **`get_request` accepts display_id** (`src/tools/requests.cjs`) — the tool now resolves
short human-readable ticket numbers (e.g. `31230`) to the internal 17-digit ID
automatically via `advanced_search_requests` before fetching full details. IDs of 10
digits or fewer are treated as display_ids; longer IDs are passed directly as before.
The schema description is updated to document both formats.

## [Unreleased] — 2026-05-19 (session 2)

### Refactoring
- **Modularised `working-sse-server.cjs`** — slimmed from 1769 to 246 lines using a factory
function pattern (`makeImplementations(sdpClient)`). Tool implementations extracted into
three focused modules:
- `src/tools/requests.cjs` — 16 request tools (list, get, create, update, close, notes,
replies, search, delete, attachment, conversation)
- `src/tools/technicians.cjs` — 3 technician tools (list, get, find)
- `src/tools/metadata.cjs` — get_metadata, get_usage_guide, claude_code_command
- **Extracted MCP Resources** into `src/mcp-resources.cjs` — server now uses
`listResources()` / `readResource()` from this module; `metadata.cjs` uses the same
module for `get_usage_guide`, eliminating the inline `SDP_RESOURCES` constant.

### New Features
- **MCP Prompts** (`src/mcp-prompts.cjs`) — four prompt templates wired to `prompts/list`
and `prompts/get` protocol handlers; `prompts: {}` declared in `initialize` capabilities:
- `resolve_request` — get → update → reply → close workflow
- `triage_request` — get → get_metadata → update → reply workflow
- `escalate_request` — get → find_technician → update → add_private_note → reply workflow
- `follow_up_request` — get_request_conversation → reply workflow
All templates use "request" terminology throughout (not "ticket").
- **Input validation at tool layer** (`src/tools/requests.cjs`) — three validators applied
before any API call:
- `subject` truncation prevented: error thrown if > 250 characters
- `impact_details` truncation prevented: error thrown if > 250 characters
- `closure_code` validated against known enum (Resolved, Cancelled, Duplicate, Closed,
On Hold, Open) with a clear error message listing valid values
- **Richer `/health` endpoint** — now async; performs a live OAuth token probe via
`sdpClient.testConnection()` and returns `auth_status` (ok / failed / error /
not_configured), `instance`, `base_url`, and `data_center` alongside existing fields.

### Dead Code Removal
- Deleted `src/sdp-api-client.cjs` — superseded by `sdp-api-client-v2.cjs`
- Deleted `src/sdp-api-client-enhanced.cjs` — no importers
- Deleted `src/simple-sse-server.cjs` — superseded by `working-sse-server.cjs`
- Deleted `src/index-sse-simple.ts` — unused TypeScript entry point
- Deleted `src/index.ts` — unused TypeScript entry point

## [Unreleased] — 2026-05-19

### Bug Fixes
- **Fixed API base URL** (`sdp-api-client-v2.cjs`, `sdp-api-metadata.cjs`) — the path
`/app/{portalName}` was incorrectly prepended to all API calls. The SDP v3 API base path
is `{SDP_BASE_URL}/api/v3` with no portal name segment. Corrected in both files.
- **Fixed infinite 401 retry loop** (`sdp-api-client-v2.cjs`) — the axios response
interceptor re-entered itself on a token-refresh retry, producing an unbound loop on
authentication failures. Fixed with an `_retry` flag on the request config that caps
the refresh at one attempt per request.
- **Fixed `testConnection()` calling an unreachable endpoint** (`sdp-api-client-v2.cjs`)
— startup connection test was calling `GET /priorities`, which returns a Tomcat HTML 401
on some SDP instances. Changed to OAuth token acquisition only; if a valid token is
returned the connection is considered healthy.

### Environment / Configuration
- **Unified `SDP_INSTANCE_NAME` into `SDP_PORTAL_NAME`** — both variables referred to the
same value. `SDP_INSTANCE_NAME` has been removed; `SDP_PORTAL_NAME` now serves both the
portal name and instance name roles across all three source files.
- **Corrected OAuth env var names in `.env.example` and `README.md`** — references to
`SDP_OAUTH_CLIENT_ID`, `SDP_OAUTH_CLIENT_SECRET`, and `SDP_OAUTH_REFRESH_TOKEN` replaced
with the correct names: `SDP_CLIENT_ID`, `SDP_CLIENT_SECRET`, `SDP_REFRESH_TOKEN`.
- **Azure / cloud deployment support** (`working-sse-server.cjs`) — server now binds to
`process.env.PORT` so it works on Azure App Service (which injects `PORT=8080`)
without manual configuration.
- **Added startup environment diagnostics** (`working-sse-server.cjs`) — logs
`SDP_BASE_URL`, `SDP_PORTAL_NAME`, `SDP_CLIENT_ID` (set/not set), `SDP_REFRESH_TOKEN`
(set/not set), and `SDP_DATA_CENTER` at boot for faster misconfiguration diagnosis.

### Documentation
- **Added `SDP-MCP_Usage.md`** — comprehensive AI context reference covering all tools,
field formats, error codes, action sequences, and the tool decision matrix. Intended as
a system prompt supplement for agents consuming this MCP server.
- **Updated `README.md`** — corrected environment variable names, documented the API URL
fix, added Azure deployment troubleshooting section, updated status date.

### New Features
- **Implemented MCP Resources** (`working-sse-server.cjs`) — server now advertises six
named resources sourced from `SDP-MCP_Usage.md`:
- `sdp://usage/api-rules` — Critical API Rules
- `sdp://usage/field-formats` — Field Format Reference
- `sdp://usage/tool-reference` — Full Tool Reference
- `sdp://usage/error-codes` — Error Code Reference
- `sdp://usage/action-sequences` — Common Action Sequences
- `sdp://usage/decision-matrix` — Tool Decision Matrix
Handlers added for `resources/list` and `resources/read`. The `resources: {}`
capability is declared in the `initialize` response.
- **Added `get_usage_guide` tool** (`working-sse-server.cjs`) — MCP tool that returns any
of the six resource sections as both a `text` and a typed `resource` content item,
satisfying the Microsoft Copilot Studio requirement that resources be surfaced as tool
outputs.

## [Unreleased] — 2026-05-15

### Security
- **Removed hardcoded credentials from `CLAUDE.md`** — plaintext PostgreSQL passwords replaced with `(set via SDP_DB_PASSWORD environment variable)` and `(set via SDP_DB_ROOT_PASSWORD environment variable)`.
- **Removed hardcoded OAuth credentials from `sdp-oauth-client.cjs`** — constructor no longer contains fallback client ID/secret strings; all credential values must come from environment variables (`SDP_CLIENT_ID`, `SDP_CLIENT_SECRET`, `SDP_OAUTH_REFRESH_TOKEN`).
- **Removed hardcoded customer-specific fallback values** from `sdp-api-client-v2.cjs`, `sdp-api-metadata.cjs`, and `working-sse-server.cjs` — portal name (`kaltentech`), custom domain (`https://helpdesk.pttg.com`), and instance name (`itdesk`) are no longer hardcoded; all must be supplied via environment variables (`SDP_PORTAL_NAME`, `SDP_BASE_URL`, `SDP_INSTANCE_NAME`).
- **Removed hardcoded server IP** (`10.212.0.7`) from startup log in `working-sse-server.cjs` — replaced with `process.env.SERVER_HOST || 'localhost'`.
- **Removed hardcoded local paths** (`/Users/kalten/projects/SDP-MCP`) from `working-sse-server.cjs` claude_code_command handler — replaced with `process.cwd()`.

### Runtime
- **Updated Node.js engine requirement** in `package.json` from `>=20.0.0` to `>=22.0.0` to match the current LTS target and remove the deprecated runtime warning.
- **Updated `@types/node`** dev dependency from `^20.12.0` to `^22.0.0` to match.

### Bug Fixes
- **Fixed `closure_code` 400 error on close request** (`sdp-api-client-v2.cjs`) — the SDP v3 API requires all reference fields to be objects. `closure_code` was being sent as a plain string (`"Resolved"`); corrected to `{ name: "Resolved" }`.
- **Fixed `resolution` field format** — `resolution` was being sent as a plain string in both `updateRequest` and `closeRequest`. Corrected to always send as `{ content: "..." }` per the API spec. Normalisation applied: string input is automatically wrapped; object input is passed through unchanged.
- **Fixed `start_index` pagination** in `listRequests` and `searchRequests` — was incorrectly set to `1` when offset was `0`. The SDP v3 API uses 0-based `start_index`; corrected to pass `offset` directly.
- **Fixed priority name regression** in `listRequests` and `updateRequest` — `'z - Medium'` was incorrectly changed to `'2 - Medium'` during a prior edit. Reverted to `'z - Medium'`, which is the actual priority name configured in this SDP instance (confirmed via `sdp-api-metadata.cjs` which maps `p.name === 'z - Medium'`).

### New Features

#### API Client (`sdp-api-client-v2.cjs`)
- **Added `deleteRequest(requestId)`** — sends `DELETE /api/v3/requests/{id}`.
- **Added `addAttachment(requestId, filePath, fileName)`** — sends `POST /api/v3/requests/{id}/attachments` using a raw multipart/form-data body constructed from a file `Buffer`; no new runtime dependencies.
- **Added shared `PRIORITY_NAMES` constant** at module level — single source of truth for the priority name map (`low` → `1 - Low`, `medium` → `z - Medium`, `high` → `3 - High`, `urgent` → `4 - Critical`). All three previously duplicated inline `priorityMap` objects in `listRequests` (×2) and `updateRequest` have been replaced with references to this constant.
- **Re-enabled `requester` field on `createRequest`** — previously skipped with a comment about validation errors. Now sends `requester: { email_id }` when `requester_email` is provided, `{ name }` when `requester_name` is provided, or passes through an object directly.
- **Re-enabled `priority` on `createRequest`** — previously commented out. Now included using `PRIORITY_NAMES` lookup.
- **Removed hardcoded subcategory default** — `createRequest` previously always injected `subcategory: { name: 'Not in list' }` (with customer-specific category ID checks) when no subcategory was supplied. The field is now omitted entirely when not provided by the caller, letting SDP apply its own defaults.

#### SSE Server (`working-sse-server.cjs`)
- **Added `delete_request` tool** — exposes `deleteRequest` to MCP clients. Required field: `request_id`.
- **Added `add_attachment` tool** — exposes `addAttachment` to MCP clients. Required fields: `request_id`, `file_path`. Optional: `file_name`.
- **Added `resolution` field** to `close_request` tool schema and handler.
- **Expanded `closure_code` enum** on `close_request` — added `Closed`, `On Hold`, and `Open` to the existing `Resolved`, `Cancelled`, `Duplicate` options.
- **Wired technician tools to real `SDPUsersAPI`** — `list_technicians`, `get_technician`, and `find_technician` were returning hardcoded stub responses claiming the `/users` endpoint does not exist. They now call `sdpClient.users.listTechnicians()`, `sdpClient.users.getTechnician()`, and `sdpClient.users.findTechnician()` respectively (the `SDPUsersAPI` class in `sdp-api-users.cjs` was already fully implemented).
- **Expanded `create_request` schema** — added: `requester_name`, `urgency`, `impact`, `level`, `mode`, `request_type`, `group`, `site`, `template`, `due_by_time`, `impact_details`, `email_ids_to_notify`.
- **Expanded `update_request` schema** — added: `update_reason`, `due_by_time`, `urgency`, `impact`, `level`, `group`, `site`, `scheduled_start_time`, `scheduled_end_time`; status enum extended with `'in progress'` and `'on hold'`.
- **Added `advanced_search_requests` tool** — exposes `advancedSearchRequests` to MCP clients. Accepts a structured `criteria` array (field, condition, value, logical_operator), with `limit`, `page`, `sort_by`, and `sort_order` options. Enables complex multi-field queries (e.g., filter by requester + date range + priority in a single call).

#### Metadata Client (`sdp-api-metadata.cjs`)
- **`getStatuses()` now tries the API first** — previously always returned a hardcoded list because the endpoint was assumed to return 404. It now attempts `GET /statuses`; uses the API response when successful, falls back to the hardcoded list on error or empty response.

### API Conformance
Audited all SDP v3 API calls against the official documentation. Key findings applied:
- All reference fields (priority, status, category, subcategory, closure_code, requester, technician, mode, request_type, urgency, impact, level) are sent as objects (`{ name: "..." }` or `{ id: "..." }`), not plain strings.
- `input_data` is sent as a URL query parameter on all request methods (GET, POST, PUT, DELETE), with a `null` body on POST/PUT.
- `Authorization` header uses `Zoho-oauthtoken <token>` format (not `Bearer`).
- `start_index` is 0-based.
- `row_count` maximum is 100 per API limits.
- Close request uses `POST /api/v3/requests/{id}/close`, not PUT.
- Subject and `impact_details` fields are validated to 250-character maximum before sending.
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -655,8 +655,8 @@ The current implementation stores OAuth tokens in environment variables. Databas
- Port: 5433 (non-standard to avoid conflicts)
- Database: sdp_mcp
- User: sdpmcpservice
- Password: *jDE1Bj%IPXKMe%Z
- Root user: root / 16vOp$BeC!&9SCqv
- Password: (set via SDP_DB_PASSWORD environment variable)
- Root user: (set via SDP_DB_ROOT_PASSWORD environment variable)

### Environment Variables
```bash
Expand Down
Loading